From 6dc00a08bc71e8eb9b030e749b7f6277bdf144d2 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Fri, 18 Mar 2011 09:37:08 +0300 Subject: [PATCH 01/57] BUG#11867985 - export symbols needed for auditing --- include/m_ctype.h | 2 +- sql/mysqld.h | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/m_ctype.h b/include/m_ctype.h index bb7f5ddfb3d..a35aea31a71 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -346,7 +346,7 @@ extern CHARSET_INFO my_charset_utf32_bin; extern CHARSET_INFO my_charset_utf32_general_ci; extern CHARSET_INFO my_charset_utf32_unicode_ci; -extern CHARSET_INFO my_charset_utf8_general_ci; +extern MYSQL_PLUGIN_IMPORT CHARSET_INFO my_charset_utf8_general_ci; extern CHARSET_INFO my_charset_utf8_unicode_ci; extern CHARSET_INFO my_charset_utf8_bin; extern CHARSET_INFO my_charset_utf8mb4_bin; diff --git a/sql/mysqld.h b/sql/mysqld.h index 2099e57e96d..fab28767d45 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -73,7 +73,7 @@ void flush_thread_cache(); void refresh_status(THD *thd); bool is_secure_file_path(char *path); -extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; +extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *national_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *table_alias_charset; @@ -181,7 +181,8 @@ extern ulong opt_binlog_rows_event_max_size; extern ulong rpl_recovery_rank, thread_cache_size; extern ulong back_log; extern char language[FN_REFLEN]; -extern ulong server_id, concurrency; +extern "C" MYSQL_PLUGIN_IMPORT ulong server_id; +extern ulong concurrency; extern time_t server_start_time, flush_status_time; extern char *opt_mysql_tmpdir, mysql_charsets_dir[]; extern int mysql_unpacked_real_data_home_len; @@ -202,8 +203,8 @@ extern handlerton *heap_hton; extern const char *load_default_groups[]; extern struct my_option my_long_options[]; extern int mysqld_server_started; -extern int orig_argc; -extern char **orig_argv; +extern "C" MYSQL_PLUGIN_IMPORT int orig_argc; +extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv; extern pthread_attr_t connection_attrib; extern MYSQL_FILE *bootstrap_file; extern my_bool old_mode; @@ -306,7 +307,7 @@ extern uint mysql_real_data_home_len; extern const char *mysql_real_data_home_ptr; extern ulong thread_handling; extern MYSQL_PLUGIN_IMPORT char *mysql_data_home; -extern char server_version[SERVER_VERSION_LENGTH]; +extern "C" MYSQL_PLUGIN_IMPORT char server_version[SERVER_VERSION_LENGTH]; extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[]; extern char mysql_unpacked_real_data_home[]; extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables; From a6b70da9a34bafd55b14daa4c81f3aecc10ae815 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 25 Mar 2011 12:36:02 +0100 Subject: [PATCH 02/57] Bug#11766249 bug#59316: PARTITIONING AND INDEX_MERGE MEMORY LEAK When executing row-ordered-retrieval index merge, the handler was cloned, but it used the wrong memory root, so instead of allocating memory on the thread/query's mem_root, it used the table's mem_root, resulting in non released memory in the table object, and was not freed until the table was closed. Solution was to ensure that memory used during cloning of a handler was allocated from the correct memory root. This was implemented by fixing handler::clone() to also take a name argument, so it can be used with partitioning. And in ha_partition only allocate the ha_partition's ref, and call the original ha_partition partitions clone() and set at cloned partitions. Fix of .bzrignore on Windows with VS 2010 --- .bzrignore | 12 ++ sql/ha_partition.cc | 230 ++++++++++++++++++++++-------- sql/ha_partition.h | 20 ++- sql/handler.cc | 6 +- sql/handler.h | 2 +- sql/opt_range.cc | 2 +- storage/heap/ha_heap.cc | 4 +- storage/heap/ha_heap.h | 2 +- storage/myisam/ha_myisam.cc | 5 +- storage/myisam/ha_myisam.h | 2 +- storage/myisammrg/ha_myisammrg.cc | 9 +- storage/myisammrg/ha_myisammrg.h | 2 +- 12 files changed, 214 insertions(+), 82 deletions(-) diff --git a/.bzrignore b/.bzrignore index 3d27c001e2b..9287e9499e3 100644 --- a/.bzrignore +++ b/.bzrignore @@ -37,7 +37,13 @@ *.user *.vcproj *.vcproj.cmake +*.vcxproj +*.vcxproj.filters */*.dir/* +*.dir +Debug +MySql.sdf +Win32 */*_pure_*warnings */.deps */.libs/* @@ -46,6 +52,7 @@ */minsizerel/* */release/* */relwithdebinfo/* +RelWithDebInfo *~ .*.swp ./CMakeCache.txt @@ -607,6 +614,7 @@ include/mysql_h.ic include/mysql_version.h include/mysqld_ername.h include/mysqld_error.h +include/mysqld_error.h.rule include/openssl include/readline include/readline/*.h @@ -1879,7 +1887,9 @@ scripts/mysql_find_rows scripts/mysql_fix_extensions scripts/mysql_fix_privilege_tables scripts/mysql_fix_privilege_tables.sql +scripts/mysql_fix_privilege_tables.sql.rule scripts/mysql_fix_privilege_tables_sql.c +scripts/mysql_fix_privilege_tables_sql.c.rule scripts/mysql_install_db scripts/mysql_secure_installation scripts/mysql_setpermission @@ -2116,6 +2126,7 @@ sql/handlerton.cc sql/html sql/latex sql/lex_hash.h +sql/lex_hash.h.rule sql/link_sources sql/max/* sql/message.h @@ -2147,6 +2158,7 @@ sql/sql_builtin.cc sql/sql_select.cc.orig sql/sql_yacc.cc sql/sql_yacc.h +sql/sql_yacc.h.rule sql/sql_yacc.output sql/sql_yacc.yy.orig sql/test_time diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 7bcbd241541..946ecc652ef 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -163,10 +163,14 @@ const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF; */ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) - :handler(hton, share), m_part_info(NULL), m_create_handler(FALSE), - m_is_sub_partitioned(0) + :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(table)"); + m_part_info= NULL; + m_create_handler= FALSE; + m_is_sub_partitioned= 0; + m_is_clone_of= NULL; + m_clone_mem_root= NULL; init_handler_variables(); DBUG_VOID_RETURN; } @@ -184,15 +188,46 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) */ ha_partition::ha_partition(handlerton *hton, partition_info *part_info) - :handler(hton, NULL), m_part_info(part_info), m_create_handler(TRUE), - m_is_sub_partitioned(m_part_info->is_sub_partitioned()) + :handler(hton, NULL) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); + DBUG_ASSERT(part_info); + m_part_info= part_info; + m_create_handler= TRUE; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); init_handler_variables(); - DBUG_ASSERT(m_part_info); DBUG_VOID_RETURN; } +/** + ha_partition constructor method used by ha_partition::clone() + + @param hton Handlerton (partition_hton) + @param share Table share object + @param part_info_arg partition_info to use + @param clone_arg ha_partition to clone + @param clme_mem_root_arg MEM_ROOT to use + + @return New partition handler +*/ + +ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, + partition_info *part_info_arg, + ha_partition *clone_arg, + MEM_ROOT *clone_mem_root_arg) + :handler(hton, share) +{ + DBUG_ENTER("ha_partition::ha_partition(clone)"); + m_part_info= part_info_arg; + m_create_handler= TRUE; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); + m_is_clone_of= clone_arg; + m_clone_mem_root= clone_mem_root_arg; + init_handler_variables(); + m_tot_parts= clone_arg->m_tot_parts; + DBUG_ASSERT(m_tot_parts); + DBUG_VOID_RETURN; +} /* Initialize handler object @@ -244,7 +279,6 @@ void ha_partition::init_handler_variables() m_rec0= 0; m_curr_key_info[0]= NULL; m_curr_key_info[1]= NULL; - is_clone= FALSE, m_part_func_monotonicity_info= NON_MONOTONIC; auto_increment_lock= FALSE; auto_increment_safe_stmt_log_lock= FALSE; @@ -359,7 +393,8 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root) */ DBUG_RETURN(0); } - else if (get_from_handler_file(table_share->normalized_path.str, mem_root)) + else if (get_from_handler_file(table_share->normalized_path.str, + mem_root, false)) { my_message(ER_UNKNOWN_ERROR, "Failed to read from the .par file", MYF(0)); DBUG_RETURN(1); @@ -1848,7 +1883,7 @@ uint ha_partition::del_ren_cre_table(const char *from, DBUG_RETURN(TRUE); } - if (get_from_handler_file(from, ha_thd()->mem_root)) + if (get_from_handler_file(from, ha_thd()->mem_root, false)) DBUG_RETURN(TRUE); DBUG_ASSERT(m_file_buffer); DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to)); @@ -2368,7 +2403,8 @@ error_end: partitions. */ -bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) +bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root, + bool clone) { char buff[FN_REFLEN], *address_tot_name_len; File file; @@ -2403,15 +2439,18 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) m_tot_parts= uint4korr((file_buffer) + 8); DBUG_PRINT("info", ("No of parts = %u", m_tot_parts)); tot_partition_words= (m_tot_parts + 3) / 4; - engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); - for (i= 0; i < m_tot_parts; i++) + if (!clone) { - engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), - (enum legacy_db_type) - *(uchar *) ((file_buffer) + - 12 + i)); - if (!engine_array[i]) - goto err3; + engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); + for (i= 0; i < m_tot_parts; i++) + { + engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), + (enum legacy_db_type) + *(uchar *) ((file_buffer) + + 12 + i)); + if (!engine_array[i]) + goto err3; + } } address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words; tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4; @@ -2422,16 +2461,19 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) m_file_buffer= file_buffer; // Will be freed in clear_handler_file() m_name_buffer_ptr= name_buffer_ptr; - if (!(m_engine_array= (plugin_ref*) - my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME)))) - goto err3; + if (!clone) + { + if (!(m_engine_array= (plugin_ref*) + my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME)))) + goto err3; - for (i= 0; i < m_tot_parts; i++) - m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]); + for (i= 0; i < m_tot_parts; i++) + m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]); - my_afree((gptr) engine_array); + my_afree((gptr) engine_array); + } - if (!m_file && create_handlers(mem_root)) + if (!clone && !m_file && create_handlers(mem_root)) { clear_handler_file(); DBUG_RETURN(TRUE); @@ -2439,7 +2481,8 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) DBUG_RETURN(FALSE); err3: - my_afree((gptr) engine_array); + if (!clone) + my_afree((gptr) engine_array); err2: my_free(file_buffer, MYF(0)); err1: @@ -2491,13 +2534,13 @@ void ha_data_partition_destroy(void *ha_data) int ha_partition::open(const char *name, int mode, uint test_if_locked) { - char *name_buffer_ptr= m_name_buffer_ptr; + char *name_buffer_ptr; int error; uint alloc_len; handler **file; char name_buff[FN_REFLEN]; bool is_not_tmp_table= (table_share->tmp_table == NO_TMP_TABLE); - ulonglong check_table_flags= 0; + ulonglong check_table_flags; DBUG_ENTER("ha_partition::open"); DBUG_ASSERT(table->s == table_share); @@ -2505,8 +2548,9 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_mode= mode; m_open_test_lock= test_if_locked; m_part_field_array= m_part_info->full_part_field_array; - if (get_from_handler_file(name, &table->mem_root)) + if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of))) DBUG_RETURN(1); + name_buffer_ptr= m_name_buffer_ptr; m_start_key.length= 0; m_rec0= table->record[0]; m_rec_length= table_share->reclength; @@ -2542,8 +2586,9 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(1); bitmap_clear_all(&m_bulk_insert_started); /* Initialize the bitmap we use to determine what partitions are used */ - if (!is_clone) + if (!m_is_clone_of) { + DBUG_ASSERT(!m_clone_mem_root); if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) { bitmap_free(&m_bulk_insert_started); @@ -2552,32 +2597,70 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) bitmap_set_all(&(m_part_info->used_partitions)); } + if (m_is_clone_of) + { + uint i; + DBUG_ASSERT(m_clone_mem_root); + /* Allocate an array of handler pointers for the partitions handlers. */ + alloc_len= (m_tot_parts + 1) * sizeof(handler*); + if (!(m_file= (handler **) alloc_root(m_clone_mem_root, alloc_len))) + goto err_alloc; + memset(m_file, 0, alloc_len); + /* + Populate them by cloning the original partitions. This also opens them. + Note that file->ref is allocated too. + */ + file= m_is_clone_of->m_file; + for (i= 0; i < m_tot_parts; i++) + { + create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, + FALSE); + if (!(m_file[i]= file[i]->clone((const char*) name_buff, + m_clone_mem_root))) + { + error= HA_ERR_INITIALIZATION; + file= &m_file[i]; + goto err_handler; + } + name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + } + } + else + { + file= m_file; + do + { + create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, + FALSE); + if ((error= (*file)->ha_open(table, (const char*) name_buff, mode, + test_if_locked))) + goto err_handler; + m_no_locks+= (*file)->lock_count(); + name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + } while (*(++file)); + } + file= m_file; + ref_length= (*file)->ref_length; + check_table_flags= (((*file)->ha_table_flags() & + ~(PARTITION_DISABLED_TABLE_FLAGS)) | + (PARTITION_ENABLED_TABLE_FLAGS)); + file++; do { - create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, - FALSE); - if ((error= (*file)->ha_open(table, (const char*) name_buff, mode, - test_if_locked))) - goto err_handler; - m_no_locks+= (*file)->lock_count(); - name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + DBUG_ASSERT(ref_length >= (*file)->ref_length); set_if_bigger(ref_length, ((*file)->ref_length)); /* Verify that all partitions have the same set of table flags. Mask all flags that partitioning enables/disables. */ - if (!check_table_flags) - { - check_table_flags= (((*file)->ha_table_flags() & - ~(PARTITION_DISABLED_TABLE_FLAGS)) | - (PARTITION_ENABLED_TABLE_FLAGS)); - } - else if (check_table_flags != (((*file)->ha_table_flags() & - ~(PARTITION_DISABLED_TABLE_FLAGS)) | - (PARTITION_ENABLED_TABLE_FLAGS))) + if (check_table_flags != (((*file)->ha_table_flags() & + ~(PARTITION_DISABLED_TABLE_FLAGS)) | + (PARTITION_ENABLED_TABLE_FLAGS))) { error= HA_ERR_INITIALIZATION; + /* set file to last handler, so all of them is closed */ + file = &m_file[m_tot_parts - 1]; goto err_handler; } } while (*(++file)); @@ -2589,6 +2672,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) */ ref_length+= PARTITION_BYTES_IN_POS; m_ref_length= ref_length; + /* Release buffer read from .par file. It will not be reused again after being opened once. @@ -2646,25 +2730,55 @@ err_handler: DEBUG_SYNC(ha_thd(), "partition_open_error"); while (file-- != m_file) (*file)->close(); +err_alloc: bitmap_free(&m_bulk_insert_started); - if (!is_clone) + if (!m_is_clone_of) bitmap_free(&(m_part_info->used_partitions)); DBUG_RETURN(error); } -handler *ha_partition::clone(MEM_ROOT *mem_root) + +/** + Clone the open and locked partitioning handler. + + @param mem_root MEM_ROOT to use. + + @return Pointer to the successfully created clone or NULL + + @details + This function creates a new ha_partition handler as a clone/copy. The + original (this) must already be opened and locked. The clone will use + the originals m_part_info. + It also allocates memory to ref + ref_dup. + In ha_partition::open() it will clone its original handlers partitions + which will allocate then om the correct MEM_ROOT and also open them. +*/ + +handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) { - handler *new_handler= get_new_handler(table->s, mem_root, - table->s->db_type()); - ((ha_partition*)new_handler)->m_part_info= m_part_info; - ((ha_partition*)new_handler)->is_clone= TRUE; - if (new_handler && !new_handler->ha_open(table, - table->s->normalized_path.str, - table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) - return new_handler; - return NULL; + ha_partition *new_handler; + + DBUG_ENTER("ha_partition::clone"); + new_handler= new (mem_root) ha_partition(ht, table_share, m_part_info, + this, mem_root); + if (!new_handler) + DBUG_RETURN(NULL); + + /* + Allocate new_handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(m_ref_length)*2); + if (!new_handler->ref) + DBUG_RETURN(NULL); + + if (new_handler->ha_open(table, name, + table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) + DBUG_RETURN(NULL); + + DBUG_RETURN((handler*) new_handler); } @@ -2695,7 +2809,7 @@ int ha_partition::close(void) DBUG_ASSERT(table->s == table_share); delete_queue(&m_queue); bitmap_free(&m_bulk_insert_started); - if (!is_clone) + if (!m_is_clone_of) bitmap_free(&(m_part_info->used_partitions)); file= m_file; diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 76b91e160ca..a38d56af8ff 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -133,6 +133,13 @@ private: bool m_is_sub_partitioned; // Is subpartitioned bool m_ordered_scan_ongoing; + /* + If set, this object was created with ha_partition::clone and doesn't + "own" the m_part_info structure. + */ + ha_partition *m_is_clone_of; + MEM_ROOT *m_clone_mem_root; + /* We keep track if all underlying handlers are MyISAM since MyISAM has a great number of extra flags not needed by other handlers. @@ -169,11 +176,6 @@ private: PARTITION_SHARE *share; /* Shared lock info */ #endif - /* - TRUE <=> this object was created with ha_partition::clone and doesn't - "own" the m_part_info structure. - */ - bool is_clone; bool auto_increment_lock; /**< lock reading/updating auto_inc */ /** Flag to keep the auto_increment lock through out the statement. @@ -186,7 +188,7 @@ private: /** used for prediction of start_bulk_insert rows */ enum_monotonicity_info m_part_func_monotonicity_info; public: - handler *clone(MEM_ROOT *mem_root); + handler *clone(const char *name, MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) { m_part_info= part_info; @@ -205,6 +207,10 @@ public: */ ha_partition(handlerton *hton, TABLE_SHARE * table); ha_partition(handlerton *hton, partition_info * part_info); + ha_partition(handlerton *hton, TABLE_SHARE *share, + partition_info *part_info_arg, + ha_partition *clone_arg, + MEM_ROOT *clone_mem_root_arg); ~ha_partition(); /* A partition handler has no characteristics in itself. It only inherits @@ -275,7 +281,7 @@ private: And one method to read it in. */ bool create_handler_file(const char *name); - bool get_from_handler_file(const char *name, MEM_ROOT *mem_root); + bool get_from_handler_file(const char *name, MEM_ROOT *mem_root, bool clone); bool new_handlers_from_part_info(MEM_ROOT *mem_root); bool create_handlers(MEM_ROOT *mem_root); void clear_handler_file(); diff --git a/sql/handler.cc b/sql/handler.cc index 5968a78b587..8adb8e061a3 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2037,9 +2037,9 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, /**************************************************************************** ** General handler functions ****************************************************************************/ -handler *handler::clone(MEM_ROOT *mem_root) +handler *handler::clone(const char *name, MEM_ROOT *mem_root) { - handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type()); + handler *new_handler= get_new_handler(table->s, mem_root, ht); /* Allocate handler->ref here because otherwise ha_open will allocate it on this->table->mem_root and we will not be able to reclaim that memory @@ -2048,7 +2048,7 @@ handler *handler::clone(MEM_ROOT *mem_root) if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) return NULL; if (new_handler && !new_handler->ha_open(table, - table->s->normalized_path.str, + name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) return new_handler; diff --git a/sql/handler.h b/sql/handler.h index dabc179079a..3de901dec62 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1166,7 +1166,7 @@ public: DBUG_ASSERT(locked == FALSE); /* TODO: DBUG_ASSERT(inited == NONE); */ } - virtual handler *clone(MEM_ROOT *mem_root); + virtual handler *clone(const char *name, MEM_ROOT *mem_root); /** This is called after create to allow us to set up cached variables */ void init() { diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 9edd4f58f04..fd71166dc23 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1335,7 +1335,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } thd= head->in_use; - if (!(file= head->file->clone(thd->mem_root))) + if (!(file= head->file->clone(head->s->normalized_path.str, thd->mem_root))) { /* Manually set the error flag. Note: there seems to be quite a few diff --git a/storage/heap/ha_heap.cc b/storage/heap/ha_heap.cc index fb7c13e4e41..9f29dee2030 100644 --- a/storage/heap/ha_heap.cc +++ b/storage/heap/ha_heap.cc @@ -142,11 +142,11 @@ int ha_heap::close(void) DESCRIPTION Do same as default implementation but use file->s->name instead of table->s->path. This is needed by Windows where the clone() call sees - '/'-delimited path in table->s->path, while ha_peap::open() was called + '/'-delimited path in table->s->path, while ha_heap::open() was called with '\'-delimited path. */ -handler *ha_heap::clone(MEM_ROOT *mem_root) +handler *ha_heap::clone(const char *name, MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type()); if (new_handler && !new_handler->ha_open(table, file->s->name, table->db_stat, diff --git a/storage/heap/ha_heap.h b/storage/heap/ha_heap.h index 22722129f4c..69751101645 100644 --- a/storage/heap/ha_heap.h +++ b/storage/heap/ha_heap.h @@ -34,7 +34,7 @@ class ha_heap: public handler public: ha_heap(handlerton *hton, TABLE_SHARE *table); ~ha_heap() {} - handler *clone(MEM_ROOT *mem_root); + handler *clone(const char *name, MEM_ROOT *mem_root); const char *table_type() const { return (table->in_use->variables.sql_mode & MODE_MYSQL323) ? diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 2650cc850a8..e5b657a4630 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -552,9 +552,10 @@ ha_myisam::ha_myisam(handlerton *hton, TABLE_SHARE *table_arg) can_enable_indexes(1) {} -handler *ha_myisam::clone(MEM_ROOT *mem_root) +handler *ha_myisam::clone(const char *name, MEM_ROOT *mem_root) { - ha_myisam *new_handler= static_cast (handler::clone(mem_root)); + ha_myisam *new_handler= static_cast (handler::clone(name, + mem_root)); if (new_handler) new_handler->file->state= file->state; return new_handler; diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h index 55a5eac92de..54801bfd0b8 100644 --- a/storage/myisam/ha_myisam.h +++ b/storage/myisam/ha_myisam.h @@ -44,7 +44,7 @@ class ha_myisam: public handler public: ha_myisam(handlerton *hton, TABLE_SHARE *table_arg); ~ha_myisam() {} - handler *clone(MEM_ROOT *mem_root); + handler *clone(const char *name, MEM_ROOT *mem_root); const char *table_type() const { return "MyISAM"; } const char *index_type(uint key_number); const char **bas_ext() const; diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 4c8d45d1fe1..3beabd83512 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -459,8 +459,7 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), problem because all locking is handled by the original MERGE table from which this is cloned of. */ - if (!(file= myrg_open(table->s->normalized_path.str, table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED))) + if (!(file= myrg_open(name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))) { DBUG_PRINT("error", ("my_errno %d", my_errno)); DBUG_RETURN(my_errno ? my_errno : -1); @@ -484,7 +483,7 @@ int ha_myisammrg::open(const char *name, int mode __attribute__((unused)), @return A cloned handler instance. */ -handler *ha_myisammrg::clone(MEM_ROOT *mem_root) +handler *ha_myisammrg::clone(const char *name, MEM_ROOT *mem_root) { MYRG_TABLE *u_table,*newu_table; ha_myisammrg *new_handler= @@ -505,8 +504,8 @@ handler *ha_myisammrg::clone(MEM_ROOT *mem_root) return NULL; } - if (new_handler->ha_open(table, table->s->normalized_path.str, table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) + if (new_handler->ha_open(table, name, table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) { delete new_handler; return NULL; diff --git a/storage/myisammrg/ha_myisammrg.h b/storage/myisammrg/ha_myisammrg.h index 790aa15e90a..a1272c633a1 100644 --- a/storage/myisammrg/ha_myisammrg.h +++ b/storage/myisammrg/ha_myisammrg.h @@ -62,7 +62,7 @@ class ha_myisammrg: public handler int open(const char *name, int mode, uint test_if_locked); int attach_children(void); int detach_children(void); - virtual handler *clone(MEM_ROOT *mem_root); + virtual handler *clone(const char *name, MEM_ROOT *mem_root); int close(void); int write_row(uchar * buf); int update_row(const uchar * old_data, uchar * new_data); From 272fa443f8b18507392b42621a7b564579528a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 5 Apr 2011 10:18:43 +0300 Subject: [PATCH 03/57] Bug 12323643 - CLEAN UP THE INNODB THREAD SHUTDOWN AND ASSERTIONS (WL#5136) Remove most references to thread id in InnoDB. Three references remain: the current holder of a mutex, and the current x-lock holder of a rw-lock, and some references in UNIV_SYNC_DEBUG checks. This allows MySQL to change the thread associated to a client connection. Tighten the UNIV_SYNC_DEBUG checks, trying to ensure that no InnoDB mutex or x-lock is being held when returning control to MySQL. The only semaphore that may be held is the btr_search_latch in shared mode. sync_thread_levels_empty_except_dict(): A wrapper for sync_thread_levels_empty_gen(TRUE). sync_thread_levels_nonempty_trx(): Check that the current thread is not holding any InnoDB semaphores, except btr_search_latch if trx->has_search_latch. sync_thread_levels_empty(): Unused function; remove. trx_t: Remove mysql_thread_id and mysql_process_no. srv_slot_t: Remove id and handle. row_search_for_mysql(), srv_conc_enter_innodb(), srv_conc_force_enter_innodb(), srv_conc_force_exit_innodb(), srv_conc_exit_innodb(), srv_suspend_mysql_thread: Assert !sync_thread_levels_nonempty_trx(). rb:634 approved by Sunny Bains --- storage/innobase/buf/buf0flu.c | 2 +- storage/innobase/include/log0log.ic | 2 +- storage/innobase/include/sync0sync.h | 35 +++++++++------ storage/innobase/include/trx0trx.h | 5 --- storage/innobase/row/row0merge.c | 2 - storage/innobase/row/row0mysql.c | 14 ------ storage/innobase/row/row0sel.c | 17 +++++-- storage/innobase/srv/srv0srv.c | 39 +++++++++++----- storage/innobase/sync/sync0sync.c | 67 +++++++++++++++++++++++----- storage/innobase/trx/trx0roll.c | 4 -- storage/innobase/trx/trx0trx.c | 10 ----- 11 files changed, 120 insertions(+), 77 deletions(-) diff --git a/storage/innobase/buf/buf0flu.c b/storage/innobase/buf/buf0flu.c index 07a32e55f97..ebe96a82a10 100644 --- a/storage/innobase/buf/buf0flu.c +++ b/storage/innobase/buf/buf0flu.c @@ -1716,7 +1716,7 @@ buf_flush_batch( ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); #ifdef UNIV_SYNC_DEBUG ut_ad((flush_type != BUF_FLUSH_LIST) - || sync_thread_levels_empty_gen(TRUE)); + || sync_thread_levels_empty_except_dict()); #endif /* UNIV_SYNC_DEBUG */ buf_pool_mutex_enter(buf_pool); diff --git a/storage/innobase/include/log0log.ic b/storage/innobase/include/log0log.ic index 1ce00fd7313..67db6695cab 100644 --- a/storage/innobase/include/log0log.ic +++ b/storage/innobase/include/log0log.ic @@ -435,7 +435,7 @@ log_free_check(void) { #ifdef UNIV_SYNC_DEBUG - ut_ad(sync_thread_levels_empty_gen(TRUE)); + ut_ad(sync_thread_levels_empty_except_dict()); #endif /* UNIV_SYNC_DEBUG */ if (log_sys->check_flush_or_checkpoint) { diff --git a/storage/innobase/include/sync0sync.h b/storage/innobase/include/sync0sync.h index a24c2106033..b823c9d5259 100644 --- a/storage/innobase/include/sync0sync.h +++ b/storage/innobase/include/sync0sync.h @@ -413,13 +413,6 @@ sync_thread_reset_level( /*====================*/ void* latch); /*!< in: pointer to a mutex or an rw-lock */ /******************************************************************//** -Checks that the level array for the current thread is empty. -@return TRUE if empty */ -UNIV_INTERN -ibool -sync_thread_levels_empty(void); -/*==========================*/ -/******************************************************************//** Checks if the level array for the current thread contains a mutex or rw-latch at the specified level. @return a matching latch, or NULL if not found */ @@ -430,17 +423,33 @@ sync_thread_levels_contains( ulint level); /*!< in: latching order level (SYNC_DICT, ...)*/ /******************************************************************//** -Checks if the level array for the current thread is empty. +Checks that the level array for the current thread is empty. @return a latch, or NULL if empty except the exceptions specified below */ UNIV_INTERN void* sync_thread_levels_nonempty_gen( /*============================*/ - ibool dict_mutex_allowed); /*!< in: TRUE if dictionary mutex is - allowed to be owned by the thread, - also purge_is_running mutex is - allowed */ -#define sync_thread_levels_empty_gen(d) (!sync_thread_levels_nonempty_gen(d)) + ibool dict_mutex_allowed) /*!< in: TRUE if dictionary mutex is + allowed to be owned by the thread */ + __attribute__((warn_unused_result)); +/******************************************************************//** +Checks if the level array for the current thread is empty, +except for data dictionary latches. */ +#define sync_thread_levels_empty_except_dict() \ + (!sync_thread_levels_nonempty_gen(TRUE)) +/******************************************************************//** +Checks if the level array for the current thread is empty, +except for the btr_search_latch. +@return a latch, or NULL if empty except the exceptions specified below */ +UNIV_INTERN +void* +sync_thread_levels_nonempty_trx( +/*============================*/ + ibool has_search_latch) + /*!< in: TRUE if and only if the thread + is supposed to hold btr_search_latch */ + __attribute__((warn_unused_result)); + /******************************************************************//** Gets the debug information for a reserved mutex. */ UNIV_INTERN diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 83f6182c347..ba840978b18 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -569,11 +569,6 @@ struct trx_struct{ ib_int64_t mysql_log_offset;/* if MySQL binlog is used, this field contains the end offset of the binlog entry */ - os_thread_id_t mysql_thread_id;/* id of the MySQL thread associated - with this transaction object */ - ulint mysql_process_no;/* since in Linux, 'top' reports - process id's and not thread id's, we - store the process number too */ /*------------------------------*/ ulint n_mysql_tables_in_use; /* number of Innobase tables used in the processing of the current diff --git a/storage/innobase/row/row0merge.c b/storage/innobase/row/row0merge.c index 03e37a8c4a4..5be437add5a 100644 --- a/storage/innobase/row/row0merge.c +++ b/storage/innobase/row/row0merge.c @@ -1929,7 +1929,6 @@ row_merge_lock_table( sel_node_t* node; ut_ad(trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(mode == LOCK_X || mode == LOCK_S); heap = mem_heap_create(512); @@ -2390,7 +2389,6 @@ row_merge_rename_tables( pars_info_t* info; char old_name[MAX_FULL_NAME_LEN + 1]; - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(old_table != new_table); ut_ad(mutex_own(&dict_sys->mutex)); diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 5fb4b4ac8c3..f7e5c5fdceb 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -976,7 +976,6 @@ row_lock_table_autoinc_for_mysql( ibool was_lock_wait; ut_ad(trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); /* If we already hold an AUTOINC lock on the table then do nothing. Note: We peek at the value of the current owner without acquiring @@ -1056,7 +1055,6 @@ row_lock_table_for_mysql( ibool was_lock_wait; ut_ad(trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); trx->op_info = "setting table lock"; @@ -1130,7 +1128,6 @@ row_insert_for_mysql( ins_node_t* node = prebuilt->ins_node; ut_ad(trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); if (prebuilt->table->ibd_file_missing) { ut_print_timestamp(stderr); @@ -1364,7 +1361,6 @@ row_update_for_mysql( trx_t* trx = prebuilt->trx; ut_ad(prebuilt && trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); UT_NOT_USED(mysql_rec); if (prebuilt->table->ibd_file_missing) { @@ -1532,7 +1528,6 @@ row_unlock_for_mysql( trx_t* trx = prebuilt->trx; ut_ad(prebuilt && trx); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); if (UNIV_UNLIKELY (!srv_locks_unsafe_for_binlog @@ -1834,7 +1829,6 @@ row_create_table_for_mysql( ulint table_name_len; ulint err; - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ @@ -2008,7 +2002,6 @@ row_create_index_for_mysql( ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(mutex_own(&(dict_sys->mutex))); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); trx->op_info = "creating index"; @@ -2411,8 +2404,6 @@ row_discard_tablespace_for_mysql( table->n_foreign_key_checks_running > 0, we do not allow the discard. We also reserve the data dictionary latch. */ - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - trx->op_info = "discarding tablespace"; trx_start_if_not_started(trx); @@ -2571,8 +2562,6 @@ row_import_tablespace_for_mysql( ib_uint64_t current_lsn; ulint err = DB_SUCCESS; - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); - trx_start_if_not_started(trx); trx->op_info = "importing tablespace"; @@ -2756,7 +2745,6 @@ row_truncate_table_for_mysql( redo log records on the truncated tablespace, we will assign a new tablespace identifier to the truncated tablespace. */ - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(table); if (srv_created_new_raw) { @@ -3607,7 +3595,6 @@ row_drop_database_for_mysql( int err = DB_SUCCESS; ulint namelen = strlen(name); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(name != NULL); ut_a(name[namelen - 1] == '/'); @@ -3777,7 +3764,6 @@ row_rename_table_for_mysql( ibool old_is_tmp, new_is_tmp; pars_info_t* info = NULL; - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_a(old_name != NULL); ut_a(new_name != NULL); diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 14deff2d465..66aff528f38 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -1951,7 +1951,7 @@ stop_for_a_while: mtr_commit(&mtr); #ifdef UNIV_SYNC_DEBUG - ut_ad(sync_thread_levels_empty_gen(TRUE)); + ut_ad(sync_thread_levels_empty_except_dict()); #endif /* UNIV_SYNC_DEBUG */ err = DB_SUCCESS; goto func_exit; @@ -1971,7 +1971,7 @@ commit_mtr_for_a_while: mtr_has_extra_clust_latch = FALSE; #ifdef UNIV_SYNC_DEBUG - ut_ad(sync_thread_levels_empty_gen(TRUE)); + ut_ad(sync_thread_levels_empty_except_dict()); #endif /* UNIV_SYNC_DEBUG */ goto table_loop; @@ -1988,7 +1988,7 @@ lock_wait_or_error: mtr_commit(&mtr); #ifdef UNIV_SYNC_DEBUG - ut_ad(sync_thread_levels_empty_gen(TRUE)); + ut_ad(sync_thread_levels_empty_except_dict()); #endif /* UNIV_SYNC_DEBUG */ func_exit: @@ -3370,7 +3370,6 @@ row_search_for_mysql( rec_offs_init(offsets_); ut_ad(index && pcur && search_tuple); - ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) { ut_print_timestamp(stderr); @@ -3387,11 +3386,17 @@ row_search_for_mysql( "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ return(DB_ERROR); } if (UNIV_UNLIKELY(!prebuilt->index_usable)) { +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ return(DB_MISSING_HISTORY); } @@ -4680,6 +4685,10 @@ func_exit: prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT; } } + +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ return(err); } diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 3af7a86a164..f0012b949ed 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -687,8 +687,6 @@ Unix.*/ /* Thread slot in the thread table */ struct srv_slot_struct{ - os_thread_id_t id; /*!< thread id */ - os_thread_t handle; /*!< thread handle */ unsigned type:1; /*!< thread type: user, utility etc. */ unsigned in_use:1; /*!< TRUE if this slot is in use */ unsigned suspended:1; /*!< TRUE if the thread is waiting @@ -887,8 +885,6 @@ srv_table_reserve_slot( slot->suspended = FALSE; slot->type = type; ut_ad(srv_slot_get_type(slot) == type); - slot->id = os_thread_get_curr_id(); - slot->handle = os_thread_get_curr(); return(slot); } @@ -907,7 +903,6 @@ srv_suspend_thread( ut_ad(mutex_own(&kernel_mutex)); ut_ad(slot->in_use); ut_ad(!slot->suspended); - ut_ad(slot->id == os_thread_get_curr_id()); if (srv_print_thread_releases) { fprintf(stderr, @@ -962,10 +957,9 @@ srv_release_threads( if (srv_print_thread_releases) { fprintf(stderr, - "Releasing thread %lu type %lu" + "Releasing thread type %lu" " from slot %lu\n", - (ulong) slot->id, (ulong) type, - (ulong) i); + (ulong) type, (ulong) i); } count++; @@ -1149,6 +1143,10 @@ srv_conc_enter_innodb( srv_conc_slot_t* slot = NULL; ulint i; +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ + if (trx->mysql_thd != NULL && thd_is_replication_slave_thread(trx->mysql_thd)) { @@ -1272,6 +1270,10 @@ retry: /* Go to wait for the event; when a thread leaves InnoDB it will release this thread */ + ut_ad(!trx->has_search_latch); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ trx->op_info = "waiting in InnoDB queue"; thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_TABLE_LOCK); @@ -1307,6 +1309,10 @@ srv_conc_force_enter_innodb( trx_t* trx) /*!< in: transaction object associated with the thread */ { +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ + if (UNIV_LIKELY(!srv_thread_concurrency)) { return; @@ -1378,6 +1384,10 @@ srv_conc_force_exit_innodb( if (slot != NULL) { os_event_set(slot->event); } + +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ } /*********************************************************************//** @@ -1389,6 +1399,10 @@ srv_conc_exit_innodb( trx_t* trx) /*!< in: transaction object associated with the thread */ { +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ + if (trx->n_tickets_to_enter_innodb > 0) { /* We will pretend the thread is still inside InnoDB though it now leaves the InnoDB engine. In this way we save @@ -1505,10 +1519,9 @@ srv_table_reserve_slot_for_mysql(void) slot = srv_mysql_table + i; fprintf(stderr, - "Slot %lu: thread id %lu, type %lu," + "Slot %lu: thread type %lu," " in use %lu, susp %lu, time %lu\n", (ulong) i, - (ulong) os_thread_pf(slot->id), (ulong) slot->type, (ulong) slot->in_use, (ulong) slot->suspended, @@ -1525,8 +1538,6 @@ srv_table_reserve_slot_for_mysql(void) ut_a(slot->in_use == FALSE); slot->in_use = TRUE; - slot->id = os_thread_get_curr_id(); - slot->handle = os_thread_get_curr(); return(slot); } @@ -1733,6 +1744,10 @@ srv_suspend_mysql_thread( trx->error_state = DB_INTERRUPTED; } + +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ } /********************************************************************//** diff --git a/storage/innobase/sync/sync0sync.c b/storage/innobase/sync/sync0sync.c index 23fd64cc5ed..0b56e736209 100644 --- a/storage/innobase/sync/sync0sync.c +++ b/storage/innobase/sync/sync0sync.c @@ -189,12 +189,12 @@ UNIV_INTERN sync_array_t* sync_primary_wait_array; /** This variable is set to TRUE when sync_init is called */ UNIV_INTERN ibool sync_initialized = FALSE; +#ifdef UNIV_SYNC_DEBUG /** An acquired mutex or rw-lock and its level in the latching order */ typedef struct sync_level_struct sync_level_t; /** Mutexes or rw-locks held by a thread */ typedef struct sync_thread_struct sync_thread_t; -#ifdef UNIV_SYNC_DEBUG /** The latch levels currently owned by threads are stored in this data structure; the size of this array is OS_THREAD_MAX_N */ @@ -221,7 +221,6 @@ UNIV_INTERN mysql_pfs_key_t mutex_list_mutex_key; #ifdef UNIV_SYNC_DEBUG /** Latching order checks start when this is set TRUE */ UNIV_INTERN ibool sync_order_checks_on = FALSE; -#endif /* UNIV_SYNC_DEBUG */ /** Number of slots reserved for each OS thread in the sync level array */ static const ulint SYNC_THREAD_N_LEVELS = 10000; @@ -258,6 +257,7 @@ struct sync_level_struct{ the ordinal value of the next free element */ }; +#endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Creates, or rather, initializes a mutex object in a specified memory @@ -1020,9 +1020,7 @@ void* sync_thread_levels_nonempty_gen( /*============================*/ ibool dict_mutex_allowed) /*!< in: TRUE if dictionary mutex is - allowed to be owned by the thread, - also purge_is_running mutex is - allowed */ + allowed to be owned by the thread */ { ulint i; sync_arr_t* arr; @@ -1069,14 +1067,61 @@ sync_thread_levels_nonempty_gen( } /******************************************************************//** -Checks that the level array for the current thread is empty. -@return TRUE if empty */ +Checks if the level array for the current thread is empty, +except for the btr_search_latch. +@return a latch, or NULL if empty except the exceptions specified below */ UNIV_INTERN -ibool -sync_thread_levels_empty(void) -/*==========================*/ +void* +sync_thread_levels_nonempty_trx( +/*============================*/ + ibool has_search_latch) + /*!< in: TRUE if and only if the thread + is supposed to hold btr_search_latch */ { - return(sync_thread_levels_empty_gen(FALSE)); + ulint i; + sync_arr_t* arr; + sync_thread_t* thread_slot; + + if (!sync_order_checks_on) { + + return(NULL); + } + + ut_a(!has_search_latch + || sync_thread_levels_contains(SYNC_SEARCH_SYS)); + + mutex_enter(&sync_thread_mutex); + + thread_slot = sync_thread_level_arrays_find_slot(); + + if (thread_slot == NULL) { + + mutex_exit(&sync_thread_mutex); + + return(NULL); + } + + arr = thread_slot->levels; + + for (i = 0; i < arr->n_elems; ++i) { + const sync_level_t* slot; + + slot = &arr->elems[i]; + + if (slot->latch != NULL + && (!has_search_latch + || slot->level != SYNC_SEARCH_SYS)) { + + mutex_exit(&sync_thread_mutex); + ut_error; + + return(slot->latch); + } + } + + mutex_exit(&sync_thread_mutex); + + return(NULL); } /******************************************************************//** diff --git a/storage/innobase/trx/trx0roll.c b/storage/innobase/trx/trx0roll.c index f9d421e3705..b55471959ce 100644 --- a/storage/innobase/trx/trx0roll.c +++ b/storage/innobase/trx/trx0roll.c @@ -460,10 +460,6 @@ trx_rollback_active( (ulong) rows_to_undo, unit); mutex_exit(&kernel_mutex); - trx->mysql_thread_id = os_thread_get_curr_id(); - - trx->mysql_process_no = os_proc_get_number(); - if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { row_mysql_lock_data_dictionary(trx); dictionary_locked = TRUE; diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 0d01bedc4fb..475e2a51c8c 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -214,10 +214,6 @@ trx_allocate_for_mysql(void) mutex_exit(&kernel_mutex); - trx->mysql_thread_id = os_thread_get_curr_id(); - - trx->mysql_process_no = os_proc_get_number(); - return(trx); } @@ -1729,12 +1725,6 @@ trx_print( fprintf(f, " state %lu", (ulong) trx->conc_state); } -#ifdef UNIV_LINUX - fprintf(f, ", process no %lu", trx->mysql_process_no); -#endif - fprintf(f, ", OS thread id %lu", - (ulong) os_thread_pf(trx->mysql_thread_id)); - if (*trx->op_info) { putc(' ', f); fputs(trx->op_info, f); From 5530926e720dd0068e0e408edb7375a512bc4cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 5 Apr 2011 10:37:58 +0300 Subject: [PATCH 04/57] Bug 12323643 - CLEAN UP THE INNODB THREAD SHUTDOWN AND ASSERTIONS (WL#5136) On shutdown, do not exit threads in os_event_wait(). This method of exiting was only used by the I/O handler threads. Exit them on a higher level. os_event_wait_low(), os_event_wait_time_low(): Do not exit on shutdown. os_thread_exit(), ut_dbg_assertion_failed(), ut_print_timestamp(): Add attribute cold, so that GCC knows that these functions are rarely invoked and can be optimized for size. os_aio_linux_collect(): Return on shutdown. os_aio_linux_handle(), os_aio_simulated_handle(), os_aio_windows_handle(): Set *message1 = *message2 = NULL and return TRUE on shutdown. fil_aio_wait(): Return on shutdown. logs_empty_and_mark_files_at_shutdown(): Even in very fast shutdown (innodb_fast_shutdown=2), allow the background threads to exit, but skip the flushing and log checkpointing. innobase_shutdown_for_mysql(): Always wait for all the threads to exit. rb:633 approved by Sunny Bains --- storage/innobase/fil/fil0fil.c | 6 +- storage/innobase/include/os0sync.h | 5 +- storage/innobase/include/os0thread.h | 3 +- storage/innobase/include/ut0dbg.h | 7 +- storage/innobase/include/ut0ut.h | 3 +- storage/innobase/log/log0log.c | 93 ++++++------ storage/innobase/os/os0file.c | 215 +++++++++++++++------------ storage/innobase/os/os0sync.c | 52 ++----- storage/innobase/srv/srv0srv.c | 4 - storage/innobase/srv/srv0start.c | 12 +- 10 files changed, 190 insertions(+), 210 deletions(-) diff --git a/storage/innobase/fil/fil0fil.c b/storage/innobase/fil/fil0fil.c index 6c50b853187..0d9846fdbf8 100644 --- a/storage/innobase/fil/fil0fil.c +++ b/storage/innobase/fil/fil0fil.c @@ -4527,8 +4527,8 @@ fil_aio_wait( ret = os_aio_linux_handle(segment, &fil_node, &message, &type); #else - ret = 0; /* Eliminate compiler warning */ ut_error; + ret = 0; /* Eliminate compiler warning */ #endif } else { srv_set_io_thread_op_info(segment, "simulated aio handle"); @@ -4538,6 +4538,10 @@ fil_aio_wait( } ut_a(ret); + if (UNIV_UNLIKELY(fil_node == NULL)) { + ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); + return; + } srv_set_io_thread_op_info(segment, "complete io for fil node"); diff --git a/storage/innobase/include/os0sync.h b/storage/innobase/include/os0sync.h index b294d7421c8..1b98f94f641 100644 --- a/storage/innobase/include/os0sync.h +++ b/storage/innobase/include/os0sync.h @@ -150,10 +150,7 @@ os_event_free( os_event_t event); /*!< in: event to free */ /**********************************************************//** -Waits for an event object until it is in the signaled state. If -srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS this also exits the -waiting thread when the event becomes signaled (or immediately if the -event is already in the signaled state). +Waits for an event object until it is in the signaled state. Typically, if the event has been signalled after the os_event_reset() we'll return immediately because event->is_set == TRUE. diff --git a/storage/innobase/include/os0thread.h b/storage/innobase/include/os0thread.h index cc56e2158ee..3a4448b463e 100644 --- a/storage/innobase/include/os0thread.h +++ b/storage/innobase/include/os0thread.h @@ -107,8 +107,9 @@ UNIV_INTERN void os_thread_exit( /*===========*/ - void* exit_value); /*!< in: exit value; in Windows this void* + void* exit_value) /*!< in: exit value; in Windows this void* is cast as a DWORD */ + __attribute__((cold, noreturn)); /*****************************************************************//** Returns the thread identifier of current thread. @return current thread identifier */ diff --git a/storage/innobase/include/ut0dbg.h b/storage/innobase/include/ut0dbg.h index d7ec90db0fb..2cb208a4e9e 100644 --- a/storage/innobase/include/ut0dbg.h +++ b/storage/innobase/include/ut0dbg.h @@ -50,9 +50,10 @@ UNIV_INTERN void ut_dbg_assertion_failed( /*====================*/ - const char* expr, /*!< in: the failed assertion */ - const char* file, /*!< in: source file containing the assertion */ - ulint line); /*!< in: line number of the assertion */ + const char* expr, /*!< in: the failed assertion */ + const char* file, /*!< in: source file containing the assertion */ + ulint line) /*!< in: line number of the assertion */ + __attribute__((nonnull(2), cold)); #if defined(__WIN__) || defined(__INTEL_COMPILER) # undef UT_DBG_USE_ABORT diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h index cd5c7ca99f1..fe28a573631 100644 --- a/storage/innobase/include/ut0ut.h +++ b/storage/innobase/include/ut0ut.h @@ -275,7 +275,8 @@ UNIV_INTERN void ut_print_timestamp( /*===============*/ - FILE* file); /*!< in: file where to print */ + FILE* file) /*!< in: file where to print */ + __attribute__((nonnull, cold)); /**********************************************************//** Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */ UNIV_INTERN diff --git a/storage/innobase/log/log0log.c b/storage/innobase/log/log0log.c index 3fef4ee4fc5..d638fe5cb6d 100644 --- a/storage/innobase/log/log0log.c +++ b/storage/innobase/log/log0log.c @@ -3078,6 +3078,7 @@ logs_empty_and_mark_files_at_shutdown(void) { ib_uint64_t lsn; ulint arch_log_no; + ibool server_busy; if (srv_print_verbose_log) { ut_print_timestamp(stderr); @@ -3092,14 +3093,12 @@ loop: mutex_enter(&kernel_mutex); - /* We need the monitor threads to stop before we proceed with a - normal shutdown. In case of very fast shutdown, however, we can - proceed without waiting for monitor threads. */ + /* We need the monitor threads to stop before we proceed with + a shutdown. */ - if (srv_fast_shutdown < 2 - && (srv_error_monitor_active - || srv_lock_timeout_active - || srv_monitor_active)) { + if (srv_error_monitor_active + || srv_lock_timeout_active + || srv_monitor_active) { mutex_exit(&kernel_mutex); @@ -3114,65 +3113,57 @@ loop: for the 'very fast' shutdown, because the InnoDB layer may have committed or prepared transactions and we don't want to lose them. */ - if (trx_n_mysql_transactions > 0 - || UT_LIST_GET_LEN(trx_sys->trx_list) > 0) { - - mutex_exit(&kernel_mutex); - - goto loop; - } - - if (srv_fast_shutdown == 2) { - /* In this fastest shutdown we do not flush the buffer pool: - it is essentially a 'crash' of the InnoDB server. Make sure - that the log is all flushed to disk, so that we can recover - all committed transactions in a crash recovery. We must not - write the lsn stamps to the data files, since at a startup - InnoDB deduces from the stamps if the previous shutdown was - clean. */ - - log_buffer_flush_to_disk(); - - mutex_exit(&kernel_mutex); - - return; /* We SKIP ALL THE REST !! */ - } - + server_busy = trx_n_mysql_transactions > 0 + || UT_LIST_GET_LEN(trx_sys->trx_list) > 0; mutex_exit(&kernel_mutex); - /* Check that the background threads are suspended */ - - if (srv_is_any_background_thread_active()) { + if (server_busy || srv_is_any_background_thread_active()) { goto loop; } - mutex_enter(&(log_sys->mutex)); - - if (log_sys->n_pending_checkpoint_writes + mutex_enter(&log_sys->mutex); + server_busy = log_sys->n_pending_checkpoint_writes #ifdef UNIV_LOG_ARCHIVE - || log_sys->n_pending_archive_ios + || log_sys->n_pending_archive_ios #endif /* UNIV_LOG_ARCHIVE */ - || log_sys->n_pending_writes) { - - mutex_exit(&(log_sys->mutex)); - - goto loop; - } - - mutex_exit(&(log_sys->mutex)); - - if (!buf_pool_check_no_pending_io()) { + || log_sys->n_pending_writes; + mutex_exit(&log_sys->mutex); + if (server_busy || !buf_pool_check_no_pending_io()) { goto loop; } #ifdef UNIV_LOG_ARCHIVE log_archive_all(); #endif /* UNIV_LOG_ARCHIVE */ + if (srv_fast_shutdown == 2) { + /* In this fastest shutdown we do not flush the buffer + pool: it is essentially a 'crash' of the InnoDB + server. Make sure that the log is all flushed to disk, + so that we can recover all committed transactions in a + crash recovery. We must not write the lsn stamps to + the data files, since at a startup InnoDB deduces from + the stamps if the previous shutdown was clean. */ + + log_buffer_flush_to_disk(); + + /* Check that the background threads stay suspended */ + if (srv_is_any_background_thread_active()) { + fprintf(stderr, + "InnoDB: Warning: some background thread" + " woke up during shutdown\n"); + goto loop; + } + + srv_shutdown_state = SRV_SHUTDOWN_LAST_PHASE; + fil_close_all_files(); + ut_a(!srv_is_any_background_thread_active()); + return; + } log_make_checkpoint_at(IB_ULONGLONG_MAX, TRUE); - mutex_enter(&(log_sys->mutex)); + mutex_enter(&log_sys->mutex); lsn = log_sys->lsn; @@ -3183,7 +3174,7 @@ loop: #endif /* UNIV_LOG_ARCHIVE */ ) { - mutex_exit(&(log_sys->mutex)); + mutex_exit(&log_sys->mutex); goto loop; } @@ -3201,7 +3192,7 @@ loop: log_archive_close_groups(TRUE); #endif /* UNIV_LOG_ARCHIVE */ - mutex_exit(&(log_sys->mutex)); + mutex_exit(&log_sys->mutex); /* Check that the background threads stay suspended */ if (srv_is_any_background_thread_active()) { diff --git a/storage/innobase/os/os0file.c b/storage/innobase/os/os0file.c index 74dbac3bc96..50607e07076 100644 --- a/storage/innobase/os/os0file.c +++ b/storage/innobase/os/os0file.c @@ -4064,13 +4064,13 @@ os_aio_func( } try_again: - if (mode == OS_AIO_NORMAL) { - if (type == OS_FILE_READ) { - array = os_aio_read_array; - } else { - array = os_aio_write_array; - } - } else if (mode == OS_AIO_IBUF) { + switch (mode) { + case OS_AIO_NORMAL: + array = (type == OS_FILE_READ) + ? os_aio_read_array + : os_aio_write_array; + break; + case OS_AIO_IBUF: ut_ad(type == OS_FILE_READ); /* Reduce probability of deadlock bugs in connection with ibuf: do not let the ibuf i/o handler sleep */ @@ -4078,19 +4078,21 @@ try_again: wake_later = FALSE; array = os_aio_ibuf_array; - } else if (mode == OS_AIO_LOG) { - + break; + case OS_AIO_LOG: array = os_aio_log_array; - } else if (mode == OS_AIO_SYNC) { + break; + case OS_AIO_SYNC: array = os_aio_sync_array; #if defined(LINUX_NATIVE_AIO) /* In Linux native AIO we don't use sync IO array. */ ut_a(!srv_use_native_aio); #endif /* LINUX_NATIVE_AIO */ - } else { - array = NULL; /* Eliminate compiler warning */ + break; + default: ut_error; + array = NULL; /* Eliminate compiler warning */ } slot = os_aio_array_reserve_slot(type, array, message1, message2, file, @@ -4253,11 +4255,17 @@ os_aio_windows_handle( INFINITE); } - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - os_thread_exit(NULL); + os_mutex_enter(array->mutex); + + if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS + && array->n_reserved == 0) { + *message1 = NULL; + *message2 = NULL; + os_mutex_exit(array->mutex); + return(TRUE); } - os_mutex_enter(array->mutex); + ut_a(i >= WAIT_OBJECT_0 && i <= WAIT_OBJECT_0 + n); slot = os_aio_array_get_nth_slot(array, i + segment * n); @@ -4403,14 +4411,6 @@ os_aio_linux_collect( retry: - /* Go down if we are in shutdown mode. - In case of srv_fast_shutdown == 2, there may be pending - IO requests but that should be OK as we essentially treat - that as a crash of InnoDB. */ - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - os_thread_exit(NULL); - } - /* Initialize the events. The timeout value is arbitrary. We probably need to experiment with it a little. */ memset(events, 0, sizeof(*events) * seg_size); @@ -4419,76 +4419,72 @@ retry: ret = io_getevents(io_ctx, 1, seg_size, events, &timeout); + if (ret > 0) { + for (i = 0; i < ret; i++) { + os_aio_slot_t* slot; + struct iocb* control; + + control = (struct iocb *)events[i].obj; + ut_a(control != NULL); + + slot = (os_aio_slot_t *) control->data; + + /* Some sanity checks. */ + ut_a(slot != NULL); + ut_a(slot->reserved); + +#if defined(UNIV_AIO_DEBUG) + fprintf(stderr, + "io_getevents[%c]: slot[%p] ctx[%p]" + " seg[%lu]\n", + (slot->type == OS_FILE_WRITE) ? 'w' : 'r', + slot, io_ctx, segment); +#endif + + /* We are not scribbling previous segment. */ + ut_a(slot->pos >= start_pos); + + /* We have not overstepped to next segment. */ + ut_a(slot->pos < end_pos); + + /* Mark this request as completed. The error handling + will be done in the calling function. */ + os_mutex_enter(array->mutex); + slot->n_bytes = events[i].res; + slot->ret = events[i].res2; + slot->io_already_done = TRUE; + os_mutex_exit(array->mutex); + } + return; + } + + if (UNIV_UNLIKELY(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS)) { + return; + } + /* This error handling is for any error in collecting the IO requests. The errors, if any, for any particular IO request are simply passed on to the calling routine. */ - /* Not enough resources! Try again. */ - if (ret == -EAGAIN) { + switch (ret) { + case -EAGAIN: + /* Not enough resources! Try again. */ + case -EINTR: + /* Interrupted! I have tested the behaviour in case of an + interrupt. If we have some completed IOs available then + the return code will be the number of IOs. We get EINTR only + if there are no completed IOs and we have been interrupted. */ + case 0: + /* No pending request! Go back and check again. */ goto retry; } - /* Interrupted! I have tested the behaviour in case of an - interrupt. If we have some completed IOs available then - the return code will be the number of IOs. We get EINTR only - if there are no completed IOs and we have been interrupted. */ - if (ret == -EINTR) { - goto retry; - } - - /* No pending request! Go back and check again. */ - if (ret == 0) { - goto retry; - } - - /* All other errors! should cause a trap for now. */ - if (UNIV_UNLIKELY(ret < 0)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: unexpected ret_code[%d] from" - " io_getevents()!\n", ret); - ut_error; - } - - ut_a(ret > 0); - - for (i = 0; i < ret; i++) { - os_aio_slot_t* slot; - struct iocb* control; - - control = (struct iocb *)events[i].obj; - ut_a(control != NULL); - - slot = (os_aio_slot_t *) control->data; - - /* Some sanity checks. */ - ut_a(slot != NULL); - ut_a(slot->reserved); - -#if defined(UNIV_AIO_DEBUG) - fprintf(stderr, - "io_getevents[%c]: slot[%p] ctx[%p]" - " seg[%lu]\n", - (slot->type == OS_FILE_WRITE) ? 'w' : 'r', - slot, io_ctx, segment); -#endif - - /* We are not scribbling previous segment. */ - ut_a(slot->pos >= start_pos); - - /* We have not overstepped to next segment. */ - ut_a(slot->pos < end_pos); - - /* Mark this request as completed. The error handling - will be done in the calling function. */ - os_mutex_enter(array->mutex); - slot->n_bytes = events[i].res; - slot->ret = events[i].res2; - slot->io_already_done = TRUE; - os_mutex_exit(array->mutex); - } - - return; + /* All other errors should cause a trap for now. */ + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: unexpected ret_code[%d] from io_getevents()!\n", + ret); + ut_error; } /**********************************************************************//** @@ -4532,20 +4528,35 @@ os_aio_linux_handle( /* Loop until we have found a completed request. */ for (;;) { + ibool any_reserved = FALSE; os_mutex_enter(array->mutex); for (i = 0; i < n; ++i) { slot = os_aio_array_get_nth_slot( - array, i + segment * n); - if (slot->reserved && slot->io_already_done) { + array, i + segment * n); + if (!slot->reserved) { + continue; + } else if (slot->io_already_done) { /* Something for us to work on. */ goto found; + } else { + any_reserved = TRUE; } } os_mutex_exit(array->mutex); - /* We don't have any completed request. - Wait for some request. Note that we return + /* There is no completed request. + If there is no pending request at all, + and the system is being shut down, exit. */ + if (UNIV_UNLIKELY + (!any_reserved + && srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS)) { + *message1 = NULL; + *message2 = NULL; + return(TRUE); + } + + /* Wait for some request. Note that we return from wait iff we have found a request. */ srv_set_io_thread_op_info(global_seg, @@ -4641,6 +4652,7 @@ os_aio_simulated_handle( byte* combined_buf; byte* combined_buf2; ibool ret; + ibool any_reserved; ulint n; ulint i; @@ -4671,18 +4683,21 @@ restart: goto recommended_sleep; } - os_mutex_enter(array->mutex); - srv_set_io_thread_op_info(global_segment, "looking for i/o requests (b)"); /* Check if there is a slot for which the i/o has already been done */ + any_reserved = FALSE; + + os_mutex_enter(array->mutex); for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i + segment * n); - if (slot->reserved && slot->io_already_done) { + if (!slot->reserved) { + continue; + } else if (slot->io_already_done) { if (os_aio_print_debug) { fprintf(stderr, @@ -4694,9 +4709,23 @@ restart: ret = TRUE; goto slot_io_done; + } else { + any_reserved = TRUE; } } + /* There is no completed request. + If there is no pending request at all, + and the system is being shut down, exit. */ + if (UNIV_UNLIKELY + (!any_reserved + && srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS)) { + os_mutex_exit(array->mutex); + *message1 = NULL; + *message2 = NULL; + return(TRUE); + } + n_consecutive = 0; /* If there are at least 2 seconds old requests, then pick the oldest diff --git a/storage/innobase/os/os0sync.c b/storage/innobase/os/os0sync.c index b461f9b7c78..41a19843812 100644 --- a/storage/innobase/os/os0sync.c +++ b/storage/innobase/os/os0sync.c @@ -558,10 +558,7 @@ os_event_free( } /**********************************************************//** -Waits for an event object until it is in the signaled state. If -srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS this also exits the -waiting thread when the event becomes signaled (or immediately if the -event is already in the signaled state). +Waits for an event object until it is in the signaled state. Typically, if the event has been signalled after the os_event_reset() we'll return immediately because event->is_set == TRUE. @@ -586,8 +583,6 @@ os_event_wait_low( returned by previous call of os_event_reset(). */ { - ib_int64_t old_signal_count; - #ifdef __WIN__ if(!srv_use_native_conditions) { DWORD err; @@ -600,43 +595,25 @@ os_event_wait_low( err = WaitForSingleObject(event->handle, INFINITE); ut_a(err == WAIT_OBJECT_0); - - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - os_thread_exit(NULL); - } return; } #endif - os_fast_mutex_lock(&(event->os_mutex)); + os_fast_mutex_lock(&event->os_mutex); - if (reset_sig_count) { - old_signal_count = reset_sig_count; - } else { - old_signal_count = event->signal_count; + if (!reset_sig_count) { + reset_sig_count = event->signal_count; } - for (;;) { - if (event->is_set == TRUE - || event->signal_count != old_signal_count) { - - os_fast_mutex_unlock(&(event->os_mutex)); - - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - - os_thread_exit(NULL); - } - /* Ok, we may return */ - - return; - } - + while (!event->is_set && event->signal_count == reset_sig_count) { os_cond_wait(&(event->cond_var), &(event->os_mutex)); /* Solaris manual said that spurious wakeups may occur: we have to check if the event really has been signaled after we came here to wait */ } + + os_fast_mutex_unlock(&event->os_mutex); } /**********************************************************//** @@ -657,7 +634,6 @@ os_event_wait_time_low( { ibool timed_out = FALSE; - ib_int64_t old_signal_count; #ifdef __WIN__ DWORD time_in_ms; @@ -727,15 +703,12 @@ os_event_wait_time_low( os_fast_mutex_lock(&event->os_mutex); - if (reset_sig_count) { - old_signal_count = reset_sig_count; - } else { - old_signal_count = event->signal_count; + if (!reset_sig_count) { + reset_sig_count = event->signal_count; } do { - if (event->is_set == TRUE - || event->signal_count != old_signal_count) { + if (event->is_set || event->signal_count != reset_sig_count) { break; } @@ -753,11 +726,6 @@ os_event_wait_time_low( os_fast_mutex_unlock(&event->os_mutex); - if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - - os_thread_exit(NULL); - } - return(timed_out ? OS_SYNC_TIME_EXCEEDED : 0); } diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index f0012b949ed..ecf1432016d 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -3082,11 +3082,7 @@ suspend_thread: os_event_wait(slot->event); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { - /* This is only extra safety, the thread should exit - already when the event wait ends */ - os_thread_exit(NULL); - } /* When there is user activity, InnoDB will set the event and the diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index 79eae610a05..864c71209c0 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -2122,17 +2122,9 @@ innobase_shutdown_for_mysql(void) srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; - /* In a 'very fast' shutdown, we do not need to wait for these threads - to die; all which counts is that we flushed the log; a 'very fast' - shutdown is essentially a crash. */ - - if (srv_fast_shutdown == 2) { - return(DB_SUCCESS); - } - /* All threads end up waiting for certain events. Put those events - to the signaled state. Then the threads will exit themselves in - os_thread_event_wait(). */ + to the signaled state. Then the threads will exit themselves after + os_event_wait(). */ for (i = 0; i < 1000; i++) { /* NOTE: IF YOU CREATE THREADS IN INNODB, YOU MUST EXIT THEM From 619f684f54db1e154c2c94267281554db546fff2 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 5 Apr 2011 11:08:36 +0300 Subject: [PATCH 05/57] Add the testcase for Bug#59410 to 5.1/builtin Bug#59410 read uncommitted: unlock row could not find a 3 mode lock on the record This bug is present only in 5.6 but I am adding the test case to earlier versions to ensure it never appears in earlier versions too. --- .../suite/innodb/r/innodb_bug59410.result | 17 +++++++++++++ .../suite/innodb/t/innodb_bug59410.test | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 mysql-test/suite/innodb/r/innodb_bug59410.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug59410.test diff --git a/mysql-test/suite/innodb/r/innodb_bug59410.result b/mysql-test/suite/innodb/r/innodb_bug59410.result new file mode 100644 index 00000000000..494d601ba4f --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug59410.result @@ -0,0 +1,17 @@ +create table `bug59410_1`(`a` int)engine=innodb; +insert into `bug59410_1` values (1),(2),(3); +select 1 from `bug59410_1` where `a` <> any ( +select 1 from `bug59410_1` where `a` <> 1 for update) +for update; +1 +1 +1 +drop table bug59410_1; +create table bug59410_2(`a` char(1),`b` int)engine=innodb; +insert into bug59410_2 values('0',0); +set transaction isolation level read uncommitted; +start transaction; +set @a=(select b from bug59410_2 where +(select 1 from bug59410_2 where a group by @a=b) +group by @a:=b); +drop table bug59410_2; diff --git a/mysql-test/suite/innodb/t/innodb_bug59410.test b/mysql-test/suite/innodb/t/innodb_bug59410.test new file mode 100644 index 00000000000..30bb0642679 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug59410.test @@ -0,0 +1,24 @@ +# +# Bug#59410 read uncommitted: unlock row could not find a 3 mode lock on the record +# +-- source include/have_innodb.inc + +# only interested that the following do not produce something like +# InnoDB: Error: unlock row could not find a 2 mode lock on the record +# in the error log + +create table `bug59410_1`(`a` int)engine=innodb; +insert into `bug59410_1` values (1),(2),(3); +select 1 from `bug59410_1` where `a` <> any ( +select 1 from `bug59410_1` where `a` <> 1 for update) +for update; +drop table bug59410_1; + +create table bug59410_2(`a` char(1),`b` int)engine=innodb; +insert into bug59410_2 values('0',0); +set transaction isolation level read uncommitted; +start transaction; +set @a=(select b from bug59410_2 where +(select 1 from bug59410_2 where a group by @a=b) +group by @a:=b); +drop table bug59410_2; From 56c34b5c1e403a5c9d4f2cf09902adb23ea77b17 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 5 Apr 2011 11:20:20 +0300 Subject: [PATCH 06/57] Add the testcase for Bug#59410 to 5.1/InnoDB Plugin Bug#59410 read uncommitted: unlock row could not find a 3 mode lock on the record This bug is present only in 5.6 but I am adding the test case to earlier versions to ensure it never appears in earlier versions too. --- .../innodb_plugin/r/innodb_bug59410.result | 17 +++++++++++++ .../innodb_plugin/t/innodb_bug59410.test | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug59410.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug59410.test diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug59410.result b/mysql-test/suite/innodb_plugin/r/innodb_bug59410.result new file mode 100644 index 00000000000..494d601ba4f --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug59410.result @@ -0,0 +1,17 @@ +create table `bug59410_1`(`a` int)engine=innodb; +insert into `bug59410_1` values (1),(2),(3); +select 1 from `bug59410_1` where `a` <> any ( +select 1 from `bug59410_1` where `a` <> 1 for update) +for update; +1 +1 +1 +drop table bug59410_1; +create table bug59410_2(`a` char(1),`b` int)engine=innodb; +insert into bug59410_2 values('0',0); +set transaction isolation level read uncommitted; +start transaction; +set @a=(select b from bug59410_2 where +(select 1 from bug59410_2 where a group by @a=b) +group by @a:=b); +drop table bug59410_2; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test b/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test new file mode 100644 index 00000000000..30bb0642679 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test @@ -0,0 +1,24 @@ +# +# Bug#59410 read uncommitted: unlock row could not find a 3 mode lock on the record +# +-- source include/have_innodb.inc + +# only interested that the following do not produce something like +# InnoDB: Error: unlock row could not find a 2 mode lock on the record +# in the error log + +create table `bug59410_1`(`a` int)engine=innodb; +insert into `bug59410_1` values (1),(2),(3); +select 1 from `bug59410_1` where `a` <> any ( +select 1 from `bug59410_1` where `a` <> 1 for update) +for update; +drop table bug59410_1; + +create table bug59410_2(`a` char(1),`b` int)engine=innodb; +insert into bug59410_2 values('0',0); +set transaction isolation level read uncommitted; +start transaction; +set @a=(select b from bug59410_2 where +(select 1 from bug59410_2 where a group by @a=b) +group by @a:=b); +drop table bug59410_2; From 30f785bc11ba50f11ed1cc32ae4a4ec62bf53d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 5 Apr 2011 13:58:37 +0300 Subject: [PATCH 07/57] Remove unused functions. os_thread_get_curr(), os_thread_get_priority(), os_thread_set_priority(), os_thread_get_last_error(): Remove. --- storage/innobase/include/os0thread.h | 30 --------- storage/innobase/os/os0thread.c | 93 ---------------------------- 2 files changed, 123 deletions(-) diff --git a/storage/innobase/include/os0thread.h b/storage/innobase/include/os0thread.h index 3a4448b463e..0c4acafa7d7 100644 --- a/storage/innobase/include/os0thread.h +++ b/storage/innobase/include/os0thread.h @@ -118,13 +118,6 @@ os_thread_id_t os_thread_get_curr_id(void); /*========================*/ /*****************************************************************//** -Returns handle to the current thread. -@return current thread handle */ -UNIV_INTERN -os_thread_t -os_thread_get_curr(void); -/*====================*/ -/*****************************************************************//** Advises the os to give up remainder of the thread's time slice. */ UNIV_INTERN void @@ -137,29 +130,6 @@ void os_thread_sleep( /*============*/ ulint tm); /*!< in: time in microseconds */ -/******************************************************************//** -Gets a thread priority. -@return priority */ -UNIV_INTERN -ulint -os_thread_get_priority( -/*===================*/ - os_thread_t handle);/*!< in: OS handle to the thread */ -/******************************************************************//** -Sets a thread priority. */ -UNIV_INTERN -void -os_thread_set_priority( -/*===================*/ - os_thread_t handle, /*!< in: OS handle to the thread */ - ulint pri); /*!< in: priority: one of OS_PRIORITY_... */ -/******************************************************************//** -Gets the last operating system error code for the calling thread. -@return last error on Windows, 0 otherwise */ -UNIV_INTERN -ulint -os_thread_get_last_error(void); -/*==========================*/ #ifndef UNIV_NONINL #include "os0thread.ic" diff --git a/storage/innobase/os/os0thread.c b/storage/innobase/os/os0thread.c index adc876be5d5..12b6805d98e 100644 --- a/storage/innobase/os/os0thread.c +++ b/storage/innobase/os/os0thread.c @@ -219,21 +219,6 @@ os_thread_exit( #endif } -/*****************************************************************//** -Returns handle to the current thread. -@return current thread handle */ -UNIV_INTERN -os_thread_t -os_thread_get_curr(void) -/*====================*/ -{ -#ifdef __WIN__ - return(GetCurrentThread()); -#else - return(pthread_self()); -#endif -} - /*****************************************************************//** Advises the os to give up remainder of the thread's time slice. */ UNIV_INTERN @@ -274,81 +259,3 @@ os_thread_sleep( select(0, NULL, NULL, NULL, &t); #endif } - -#ifndef UNIV_HOTBACKUP -/******************************************************************//** -Sets a thread priority. */ -UNIV_INTERN -void -os_thread_set_priority( -/*===================*/ - os_thread_t handle, /*!< in: OS handle to the thread */ - ulint pri) /*!< in: priority */ -{ -#ifdef __WIN__ - int os_pri; - - if (pri == OS_THREAD_PRIORITY_BACKGROUND) { - os_pri = THREAD_PRIORITY_BELOW_NORMAL; - } else if (pri == OS_THREAD_PRIORITY_NORMAL) { - os_pri = THREAD_PRIORITY_NORMAL; - } else if (pri == OS_THREAD_PRIORITY_ABOVE_NORMAL) { - os_pri = THREAD_PRIORITY_HIGHEST; - } else { - ut_error; - } - - ut_a(SetThreadPriority(handle, os_pri)); -#else - UT_NOT_USED(handle); - UT_NOT_USED(pri); -#endif -} - -/******************************************************************//** -Gets a thread priority. -@return priority */ -UNIV_INTERN -ulint -os_thread_get_priority( -/*===================*/ - os_thread_t handle __attribute__((unused))) - /*!< in: OS handle to the thread */ -{ -#ifdef __WIN__ - int os_pri; - ulint pri; - - os_pri = GetThreadPriority(handle); - - if (os_pri == THREAD_PRIORITY_BELOW_NORMAL) { - pri = OS_THREAD_PRIORITY_BACKGROUND; - } else if (os_pri == THREAD_PRIORITY_NORMAL) { - pri = OS_THREAD_PRIORITY_NORMAL; - } else if (os_pri == THREAD_PRIORITY_HIGHEST) { - pri = OS_THREAD_PRIORITY_ABOVE_NORMAL; - } else { - ut_error; - } - - return(pri); -#else - return(0); -#endif -} - -/******************************************************************//** -Gets the last operating system error code for the calling thread. -@return last error on Windows, 0 otherwise */ -UNIV_INTERN -ulint -os_thread_get_last_error(void) -/*==========================*/ -{ -#ifdef __WIN__ - return(GetLastError()); -#else - return(0); -#endif -} -#endif /* !UNIV_HOTBACKUP */ From af7d9929fab25a2f7029001b194fbe0d2a01207d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 6 Apr 2011 09:22:36 +0300 Subject: [PATCH 08/57] Enable __attribute__((cold)) only for GCC 4.3 and later. This attribute was introduced in bzr revision-id marko.makela@oracle.com-20110405073758-b8y733yvkqum940i and caused older GCC versions to emit warnings. --- storage/innobase/include/os0thread.h | 2 +- storage/innobase/include/univ.i | 13 +++++++++++++ storage/innobase/include/ut0dbg.h | 2 +- storage/innobase/include/ut0ut.h | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/storage/innobase/include/os0thread.h b/storage/innobase/include/os0thread.h index 0c4acafa7d7..df3cdb7728e 100644 --- a/storage/innobase/include/os0thread.h +++ b/storage/innobase/include/os0thread.h @@ -109,7 +109,7 @@ os_thread_exit( /*===========*/ void* exit_value) /*!< in: exit value; in Windows this void* is cast as a DWORD */ - __attribute__((cold, noreturn)); + UNIV_COLD __attribute__((noreturn)); /*****************************************************************//** Returns the thread identifier of current thread. @return current thread identifier */ diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index f561226a2de..2c7d32bd1a3 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -255,6 +255,19 @@ easy way to get it to work. See http://bugs.mysql.com/bug.php?id=52263. */ #else # define UNIV_INTERN #endif +#if defined __GNUC__ && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3) +/** Starting with GCC 4.3, the "cold" attribute is used to inform the +compiler that a function is unlikely executed. The function is +optimized for size rather than speed and on many targets it is placed +into special subsection of the text section so all cold functions +appears close together improving code locality of non-cold parts of +program. The paths leading to call of cold functions within code are +marked as unlikely by the branch prediction mechanism. optimize a +rarely invoked function for size instead for speed. */ +# define UNIV_COLD __attribute__((cold)) +#else +# define UNIV_COLD /* empty */ +#endif #ifndef UNIV_MUST_NOT_INLINE /* Definition for inline version */ diff --git a/storage/innobase/include/ut0dbg.h b/storage/innobase/include/ut0dbg.h index 2cb208a4e9e..07730176d81 100644 --- a/storage/innobase/include/ut0dbg.h +++ b/storage/innobase/include/ut0dbg.h @@ -53,7 +53,7 @@ ut_dbg_assertion_failed( const char* expr, /*!< in: the failed assertion */ const char* file, /*!< in: source file containing the assertion */ ulint line) /*!< in: line number of the assertion */ - __attribute__((nonnull(2), cold)); + UNIV_COLD __attribute__((nonnull(2))); #if defined(__WIN__) || defined(__INTEL_COMPILER) # undef UT_DBG_USE_ABORT diff --git a/storage/innobase/include/ut0ut.h b/storage/innobase/include/ut0ut.h index fe28a573631..cad39e9a34f 100644 --- a/storage/innobase/include/ut0ut.h +++ b/storage/innobase/include/ut0ut.h @@ -276,7 +276,7 @@ void ut_print_timestamp( /*===============*/ FILE* file) /*!< in: file where to print */ - __attribute__((nonnull, cold)); + UNIV_COLD __attribute__((nonnull)); /**********************************************************//** Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */ UNIV_INTERN From 3c5f4c30d738137382cfa0da1bf9c02429268d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 6 Apr 2011 10:34:49 +0300 Subject: [PATCH 09/57] Non-functional change: move a printout from innobase_shutdown_for_mysql() to logs_empty_and_mark_files_at_shutdown() where the rest of the logic is located. --- storage/innobase/log/log0log.c | 8 ++++++++ storage/innobase/srv/srv0start.c | 11 ----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/storage/innobase/log/log0log.c b/storage/innobase/log/log0log.c index d638fe5cb6d..b07135465c3 100644 --- a/storage/innobase/log/log0log.c +++ b/storage/innobase/log/log0log.c @@ -3137,6 +3137,14 @@ loop: log_archive_all(); #endif /* UNIV_LOG_ARCHIVE */ if (srv_fast_shutdown == 2) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: MySQL has requested a very fast shutdown" + " without flushing " + "the InnoDB buffer pool to data files." + " At the next mysqld startup " + "InnoDB will do a crash recovery!\n"); + /* In this fastest shutdown we do not flush the buffer pool: it is essentially a 'crash' of the InnoDB server. Make sure that the log is all flushed to disk, diff --git a/storage/innobase/srv/srv0start.c b/storage/innobase/srv/srv0start.c index 864c71209c0..cf11e75b8e0 100644 --- a/storage/innobase/srv/srv0start.c +++ b/storage/innobase/srv/srv0start.c @@ -2097,17 +2097,6 @@ innobase_shutdown_for_mysql(void) The step 1 is the real InnoDB shutdown. The remaining steps 2 - ... just free data structures after the shutdown. */ - - if (srv_fast_shutdown == 2) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: MySQL has requested a very fast shutdown" - " without flushing " - "the InnoDB buffer pool to data files." - " At the next mysqld startup " - "InnoDB will do a crash recovery!\n"); - } - logs_empty_and_mark_files_at_shutdown(); if (srv_conc_n_threads != 0) { From 325715c43f849a6ed783082c7cce1519d79b8154 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Wed, 6 Apr 2011 14:38:24 +0300 Subject: [PATCH 10/57] Load the innodb plugin instead of builtin in innodb_plugin.innodb_bug59410 Spotted by: Marko --- mysql-test/suite/innodb_plugin/t/innodb_bug59410.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test b/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test index 30bb0642679..6eabe0a8403 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug59410.test @@ -1,7 +1,7 @@ # # Bug#59410 read uncommitted: unlock row could not find a 3 mode lock on the record # --- source include/have_innodb.inc +-- source include/have_innodb_plugin.inc # only interested that the following do not produce something like # InnoDB: Error: unlock row could not find a 2 mode lock on the record From 82a68a5f5012b6422c5d7ec47966dd9bed2fe0b9 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 6 Apr 2011 17:31:26 +0300 Subject: [PATCH 11/57] Bug #12325444 : 60746: CLIENT_PLUGIN.H IS BROKEN Removed the STDCALL macro and the function from the .def file, since it's not used by the connectors atm. --- include/mysql/client_plugin.h | 5 ++--- libmysql/libmysql.def | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index cc3f468040f..6b37170aeab 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -156,8 +156,7 @@ mysql_client_register_plugin(struct st_mysql *mysql, @retval 0 on success, 1 in case of failure **/ -int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin, - const char *option, - const void *value); +int mysql_plugin_options(struct st_mysql_client_plugin *plugin, + const char *option, const void *value); #endif diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index fc15fcf0884..ce85d2a4086 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -104,4 +104,3 @@ EXPORTS mysql_server_end mysql_set_character_set mysql_get_character_set_info - mysql_plugin_options From 08bf3ddde99f6a6014efe9ecf10a976bdd741a8d Mon Sep 17 00:00:00 2001 From: Guilhem Bichot Date: Thu, 7 Apr 2011 15:09:19 +0200 Subject: [PATCH 12/57] Fix for Bug#11765141 - "58072: LOAD DATA INFILE: LEAKS IO CACHE MEMORY WHEN ERROR OCCURS" --- mysql-test/r/loaddata.result | 9 +++++++++ mysql-test/t/loaddata.test | 15 +++++++++++++++ sql/sql_load.cc | 9 ++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 3a421b3ea3f..59a1b904744 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -539,4 +539,13 @@ CREATE TABLE t1(f1 INT); SELECT 0xE1BB30 INTO OUTFILE 't1.dat'; LOAD DATA INFILE 't1.dat' IGNORE INTO TABLE t1 CHARACTER SET utf8; DROP TABLE t1; +# +# Bug#11765141 - 58072: LOAD DATA INFILE: LEAKS IO CACHE MEMORY +# WHEN ERROR OCCURS +# +SELECT '1\n' INTO DUMPFILE 'MYSQLTEST_VARDIR/tmp/bug11735141.txt'; +create table t1(a point); +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/bug11735141.txt' INTO TABLE t1; +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +drop table t1; End of 5.1 tests diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index e0764b67ec0..3d0fdea05ed 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -625,4 +625,19 @@ DROP TABLE t1; let $MYSQLD_DATADIR= `select @@datadir`; remove_file $MYSQLD_DATADIR/test/t1.dat; +--echo # +--echo # Bug#11765141 - 58072: LOAD DATA INFILE: LEAKS IO CACHE MEMORY +--echo # WHEN ERROR OCCURS +--echo # + +--let $file=$MYSQLTEST_VARDIR/tmp/bug11735141.txt +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--eval SELECT '1\n' INTO DUMPFILE '$file' + +create table t1(a point); +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_CANT_CREATE_GEOMETRY_OBJECT +--eval LOAD DATA INFILE '$file' INTO TABLE t1 +drop table t1; + --echo End of 5.1 tests diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 513cd62b510..b9b7bd74f6c 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1075,9 +1075,10 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, String &field_term, String &line_start, String &line_term, String &enclosed_par, int escape, bool get_it_from_net, bool is_fifo) - :file(file_par),escape_char(escape) + :file(file_par), buff_length(tot_length), escape_char(escape), + found_end_of_line(false), eof(false), need_end_io_cache(false), + error(false), line_cuted(false), found_null(false), read_charset(cs) { - read_charset= cs; field_term_ptr=(char*) field_term.ptr(); field_term_length= field_term.length(); line_term_ptr=(char*) line_term.ptr(); @@ -1104,8 +1105,6 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, (uchar) enclosed_par[0] : INT_MAX; field_term_char= field_term_length ? (uchar) field_term_ptr[0] : INT_MAX; line_term_char= line_term_length ? (uchar) line_term_ptr[0] : INT_MAX; - error=eof=found_end_of_line=found_null=line_cuted=0; - buff_length=tot_length; /* Set of a stack for unget if long terminators */ @@ -1151,7 +1150,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, READ_INFO::~READ_INFO() { - if (!error && need_end_io_cache) + if (need_end_io_cache) ::end_io_cache(&cache); my_free(buffer, MYF(MY_ALLOW_ZERO_PTR)); From 1a0dde92066236436de80d3a20978bf3baffc9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Apr 2011 21:12:54 +0300 Subject: [PATCH 13/57] Bug #11766513 - 59641: Prepared XA transaction in system after hard crash causes future shutdown hang InnoDB would hang on shutdown if any XA transactions exist in the system in the PREPARED state. This has been masked by the fact that MySQL would roll back any PREPARED transaction on shutdown, in the spirit of Bug #12161 Xa recovery and client disconnection. [mysql-test-run] do_shutdown_server: Interpret --shutdown_server 0 as a request to kill the server immediately without initiating a shutdown procedure. xid_cache_insert(): Initialize XID_STATE::rm_error in order to avoid a bogus error message on XA ROLLBACK of a recovered PREPARED transaction. innobase_commit_by_xid(), innobase_rollback_by_xid(): Free the InnoDB transaction object after rolling back a PREPARED transaction. trx_get_trx_by_xid(): Only consider transactions whose trx->is_prepared flag is set. The MySQL layer seems to prevent attempts to roll back connected transactions that are in the PREPARED state from another connection, but it is better to play it safe. The is_prepared flag was introduced in the InnoDB Plugin. trx_n_prepared: A new counter, counting the number of InnoDB transactions in the PREPARED state. logs_empty_and_mark_files_at_shutdown(): On shutdown, allow trx_n_prepared transactions to exist in the system. trx_undo_free_prepared(), trx_free_prepared(): New functions, to free the memory objects of PREPARED transactions on shutdown. This is not needed in the built-in InnoDB, because it would collect all allocated memory on shutdown. The InnoDB Plugin needs this because of innodb_use_sys_malloc. trx_sys_close(): Invoke trx_free_prepared() on all remaining transactions. --- client/mysqltest.cc | 12 ++-- .../suite/innodb/r/innodb_bug59641.result | 57 +++++++++++++++ .../suite/innodb/t/innodb_bug59641.test | 66 +++++++++++++++++ .../innodb_plugin/r/innodb_bug59641.result | 57 +++++++++++++++ .../innodb_plugin/t/innodb_bug59641.test | 70 +++++++++++++++++++ sql/sql_class.cc | 1 + storage/innobase/handler/ha_innodb.cc | 6 +- storage/innobase/include/trx0trx.h | 5 ++ storage/innobase/log/log0log.c | 9 +-- storage/innobase/trx/trx0trx.c | 11 +++ storage/innodb_plugin/ChangeLog | 7 ++ storage/innodb_plugin/handler/ha_innodb.cc | 6 +- storage/innodb_plugin/include/trx0trx.h | 11 +++ storage/innodb_plugin/include/trx0undo.h | 9 +++ storage/innodb_plugin/log/log0log.c | 9 +-- storage/innodb_plugin/trx/trx0sys.c | 10 +++ storage/innodb_plugin/trx/trx0trx.c | 70 ++++++++++++++++++- storage/innodb_plugin/trx/trx0undo.c | 28 ++++++++ 18 files changed, 425 insertions(+), 19 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug59641.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug59641.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug59641.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug59641.test diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 52c76b8c68e..a1813838a24 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -4461,13 +4461,14 @@ static int my_kill(int pid, int sig) command called command DESCRIPTION - shutdown [] + shutdown_server [] */ void do_shutdown_server(struct st_command *command) { - int timeout=60, pid; + long timeout=60; + int pid; DYNAMIC_STRING ds_pidfile_name; MYSQL* mysql = &cur_con->mysql; static DYNAMIC_STRING ds_timeout; @@ -4482,8 +4483,9 @@ void do_shutdown_server(struct st_command *command) if (ds_timeout.length) { - timeout= atoi(ds_timeout.str); - if (timeout == 0) + char* endptr; + timeout= strtol(ds_timeout.str, &endptr, 10); + if (*endptr != '\0') die("Illegal argument for timeout: '%s'", ds_timeout.str); } dynstr_free(&ds_timeout); @@ -4525,7 +4527,7 @@ void do_shutdown_server(struct st_command *command) DBUG_PRINT("info", ("Process %d does not exist anymore", pid)); DBUG_VOID_RETURN; } - DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout)); + DBUG_PRINT("info", ("Sleeping, timeout: %ld", timeout)); my_sleep(1000000L); } diff --git a/mysql-test/suite/innodb/r/innodb_bug59641.result b/mysql-test/suite/innodb/r/innodb_bug59641.result new file mode 100644 index 00000000000..361172aa82b --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug59641.result @@ -0,0 +1,57 @@ +CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; +INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); +COMMIT; +XA START '123'; +INSERT INTO t VALUES(1,1); +XA END '123'; +XA PREPARE '123'; +XA START '456'; +INSERT INTO t VALUES(3,47),(5,67); +UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8; +XA END '456'; +XA PREPARE '456'; +XA START '789'; +UPDATE t SET b=4*a WHERE a=32; +XA END '789'; +XA PREPARE '789'; +call mtr.add_suppression("Found 3 prepared XA transactions"); +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a b +1 1 +2 2 +3 47 +4 4 +5 134 +8 16 +16 16 +32 128 +COMMIT; +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a b +1 1 +2 2 +3 47 +4 4 +5 134 +8 16 +16 16 +32 128 +COMMIT; +XA RECOVER; +formatID gtrid_length bqual_length data +1 3 0 789 +1 3 0 456 +1 3 0 123 +XA ROLLBACK '123'; +XA ROLLBACK '456'; +XA COMMIT '789'; +SELECT * FROM t; +a b +2 2 +4 4 +8 8 +16 16 +32 128 +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/innodb_bug59641.test b/mysql-test/suite/innodb/t/innodb_bug59641.test new file mode 100644 index 00000000000..0237673061c --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug59641.test @@ -0,0 +1,66 @@ +# Bug #59641 Prepared XA transaction causes shutdown hang after a crash + +-- source include/not_embedded.inc +-- source include/have_innodb.inc + +CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; +INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); +COMMIT; +XA START '123'; +INSERT INTO t VALUES(1,1); +XA END '123'; +XA PREPARE '123'; + +CONNECT (con1,localhost,root,,); +CONNECTION con1; + +XA START '456'; +INSERT INTO t VALUES(3,47),(5,67); +UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8; +XA END '456'; +XA PREPARE '456'; + +CONNECT (con2,localhost,root,,); +CONNECTION con2; + +XA START '789'; +UPDATE t SET b=4*a WHERE a=32; +XA END '789'; +XA PREPARE '789'; + +# The server would issue this warning on restart. +call mtr.add_suppression("Found 3 prepared XA transactions"); + +# Kill the server without sending a shutdown command +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 0 +-- source include/wait_until_disconnected.inc + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +COMMIT; + +# Shut down the server. This would hang because of the bug. +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server +-- source include/wait_until_disconnected.inc + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +COMMIT; +XA RECOVER; +XA ROLLBACK '123'; +XA ROLLBACK '456'; +XA COMMIT '789'; +SELECT * FROM t; + +DROP TABLE t; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug59641.result b/mysql-test/suite/innodb_plugin/r/innodb_bug59641.result new file mode 100644 index 00000000000..361172aa82b --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug59641.result @@ -0,0 +1,57 @@ +CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; +INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); +COMMIT; +XA START '123'; +INSERT INTO t VALUES(1,1); +XA END '123'; +XA PREPARE '123'; +XA START '456'; +INSERT INTO t VALUES(3,47),(5,67); +UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8; +XA END '456'; +XA PREPARE '456'; +XA START '789'; +UPDATE t SET b=4*a WHERE a=32; +XA END '789'; +XA PREPARE '789'; +call mtr.add_suppression("Found 3 prepared XA transactions"); +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a b +1 1 +2 2 +3 47 +4 4 +5 134 +8 16 +16 16 +32 128 +COMMIT; +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a b +1 1 +2 2 +3 47 +4 4 +5 134 +8 16 +16 16 +32 128 +COMMIT; +XA RECOVER; +formatID gtrid_length bqual_length data +1 3 0 789 +1 3 0 456 +1 3 0 123 +XA ROLLBACK '123'; +XA ROLLBACK '456'; +XA COMMIT '789'; +SELECT * FROM t; +a b +2 2 +4 4 +8 8 +16 16 +32 128 +DROP TABLE t; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug59641.test b/mysql-test/suite/innodb_plugin/t/innodb_bug59641.test new file mode 100644 index 00000000000..0fb24e47a54 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug59641.test @@ -0,0 +1,70 @@ +# Bug #59641 Prepared XA transaction causes shutdown hang after a crash + +-- source include/not_embedded.inc +-- source include/have_innodb_plugin.inc + +let $innodb_file_format_check_orig=`select @@innodb_file_format_check`; + +CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; +INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); +COMMIT; +XA START '123'; +INSERT INTO t VALUES(1,1); +XA END '123'; +XA PREPARE '123'; + +CONNECT (con1,localhost,root,,); +CONNECTION con1; + +XA START '456'; +INSERT INTO t VALUES(3,47),(5,67); +UPDATE t SET b=2*b WHERE a BETWEEN 5 AND 8; +XA END '456'; +XA PREPARE '456'; + +CONNECT (con2,localhost,root,,); +CONNECTION con2; + +XA START '789'; +UPDATE t SET b=4*a WHERE a=32; +XA END '789'; +XA PREPARE '789'; + +# The server would issue this warning on restart. +call mtr.add_suppression("Found 3 prepared XA transactions"); + +# Kill the server without sending a shutdown command +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 0 +-- source include/wait_until_disconnected.inc + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +COMMIT; + +# Shut down the server. This would hang because of the bug. +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server +-- source include/wait_until_disconnected.inc + +# Restart the server. +-- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc + +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +COMMIT; +XA RECOVER; +XA ROLLBACK '123'; +XA ROLLBACK '456'; +XA COMMIT '789'; +SELECT * FROM t; + +DROP TABLE t; +--disable_query_log +eval set global innodb_file_format_check=$innodb_file_format_check_orig; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a61ce7bfd14..ae21a5335fd 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3383,6 +3383,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) xs->xa_state=xa_state; xs->xid.set(xid); xs->in_thd=0; + xs->rm_error=0; res=my_hash_insert(&xid_cache, (uchar*)xs); } pthread_mutex_unlock(&LOCK_xid_cache); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 6f58fd70fbd..75c732c44d4 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -8565,7 +8565,7 @@ innobase_commit_by_xid( if (trx) { innobase_commit_low(trx); - + trx_free_for_background(trx); return(XA_OK); } else { return(XAER_NOTA); @@ -8588,7 +8588,9 @@ innobase_rollback_by_xid( trx = trx_get_trx_by_xid(xid); if (trx) { - return(innobase_rollback_trx(trx)); + int ret = innobase_rollback_trx(trx); + trx_free_for_background(trx); + return(ret); } else { return(XAER_NOTA); } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 4652f45892e..7cb16107746 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -19,7 +19,12 @@ Created 3/26/1996 Heikki Tuuri #include "dict0types.h" #include "trx0xa.h" +/* Number of transactions currently allocated for MySQL: protected by +the kernel mutex */ extern ulint trx_n_mysql_transactions; +/* Number of transactions currently in the XA PREPARED state: protected by +the kernel mutex */ +extern ulint trx_n_prepared; /************************************************************************ Releases the search latch if trx has reserved it. */ diff --git a/storage/innobase/log/log0log.c b/storage/innobase/log/log0log.c index 3300997112b..092e3bfe37f 100644 --- a/storage/innobase/log/log0log.c +++ b/storage/innobase/log/log0log.c @@ -3052,12 +3052,13 @@ loop: goto loop; } - /* Check that there are no longer transactions. We need this wait even - for the 'very fast' shutdown, because the InnoDB layer may have - committed or prepared transactions and we don't want to lose them. */ + /* Check that there are no longer transactions, except for + PREPARED ones. We need this wait even for the 'very fast' + shutdown, because the InnoDB layer may have committed or + prepared transactions and we don't want to lose them. */ if (trx_n_mysql_transactions > 0 - || UT_LIST_GET_LEN(trx_sys->trx_list) > 0) { + || UT_LIST_GET_LEN(trx_sys->trx_list) > trx_n_prepared) { mutex_exit(&kernel_mutex); diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index a82d7f452fc..d174f1e1b37 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -41,6 +41,9 @@ sess_t* trx_dummy_sess = NULL; /* Number of transactions currently allocated for MySQL: protected by the kernel mutex */ ulint trx_n_mysql_transactions = 0; +/* Number of transactions currently in the XA PREPARED state: protected by +the kernel mutex */ +ulint trx_n_prepared = 0; /***************************************************************** Starts the transaction if it is not yet started. */ @@ -480,6 +483,7 @@ trx_lists_init_at_db_start(void) if (srv_force_recovery == 0) { trx->conc_state = TRX_PREPARED; + trx_n_prepared++; } else { fprintf(stderr, "InnoDB: Since" @@ -558,6 +562,7 @@ trx_lists_init_at_db_start(void) trx->conc_state = TRX_PREPARED; + trx_n_prepared++; } else { fprintf(stderr, "InnoDB: Since" @@ -832,6 +837,11 @@ trx_commit_off_kernel( || trx->conc_state == TRX_PREPARED); ut_ad(mutex_own(&kernel_mutex)); + if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) { + ut_a(trx_n_prepared > 0); + trx_n_prepared--; + } + /* The following assignment makes the transaction committed in memory and makes its changes to data visible to other transactions. NOTE that there is a small discrepancy from the strict formal @@ -1882,6 +1892,7 @@ trx_prepare_off_kernel( /*--------------------------------------*/ trx->conc_state = TRX_PREPARED; + trx_n_prepared++; /*--------------------------------------*/ if (must_flush_log) { diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 100cf3690ce..d062fc7e648 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,10 @@ +2011-04-07 The InnoDB Team + + * handler/ha_innodb.cc, include/trx0trx.h, include/trx0undo.h, + log/log0log.c, trx/trx0sys.c, trx/trx0trx.c, trx/trx0undo.c: + Fix Bug #59641 Prepared XA transaction in system after hard crash + causes future shutdown hang + 2011-03-30 The InnoDB Team * srv/srv0srv.c, sync/sync0arr.h, sync/sync0arr.c: diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index dda2fbaa4d2..7f92d797d30 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -9998,7 +9998,7 @@ innobase_commit_by_xid( if (trx) { innobase_commit_low(trx); - + trx_free_for_background(trx); return(XA_OK); } else { return(XAER_NOTA); @@ -10024,7 +10024,9 @@ innobase_rollback_by_xid( trx = trx_get_trx_by_xid(xid); if (trx) { - return(innobase_rollback_trx(trx)); + int ret = innobase_rollback_trx(trx); + trx_free_for_background(trx); + return(ret); } else { return(XAER_NOTA); } diff --git a/storage/innodb_plugin/include/trx0trx.h b/storage/innodb_plugin/include/trx0trx.h index 833bae4a4ff..4bf3e75a5ee 100644 --- a/storage/innodb_plugin/include/trx0trx.h +++ b/storage/innodb_plugin/include/trx0trx.h @@ -44,6 +44,9 @@ extern sess_t* trx_dummy_sess; /** Number of transactions currently allocated for MySQL: protected by the kernel mutex */ extern ulint trx_n_mysql_transactions; +/** Number of transactions currently in the XA PREPARED state: protected by +the kernel mutex */ +extern ulint trx_n_prepared; /********************************************************************//** Releases the search latch if trx has reserved it. */ @@ -108,6 +111,14 @@ trx_free( /*=====*/ trx_t* trx); /*!< in, own: trx object */ /********************************************************************//** +At shutdown, frees a transaction object that is in the PREPARED state. */ +UNIV_INTERN +void +trx_free_prepared( +/*==============*/ + trx_t* trx) /*!< in, own: trx object */ + __attribute__((nonnull)); +/********************************************************************//** Frees a transaction object for MySQL. */ UNIV_INTERN void diff --git a/storage/innodb_plugin/include/trx0undo.h b/storage/innodb_plugin/include/trx0undo.h index a084f2394b5..4f15cd85833 100644 --- a/storage/innodb_plugin/include/trx0undo.h +++ b/storage/innodb_plugin/include/trx0undo.h @@ -298,6 +298,15 @@ void trx_undo_insert_cleanup( /*====================*/ trx_t* trx); /*!< in: transaction handle */ + +/********************************************************************//** +At shutdown, frees the undo logs of a PREPARED transaction. */ +UNIV_INTERN +void +trx_undo_free_prepared( +/*===================*/ + trx_t* trx) /*!< in/out: PREPARED transaction */ + __attribute__((nonnull)); #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses the redo log entry of an undo log page initialization. diff --git a/storage/innodb_plugin/log/log0log.c b/storage/innodb_plugin/log/log0log.c index 183c24d2147..4bb9abdc1a4 100644 --- a/storage/innodb_plugin/log/log0log.c +++ b/storage/innodb_plugin/log/log0log.c @@ -3085,12 +3085,13 @@ loop: goto loop; } - /* Check that there are no longer transactions. We need this wait even - for the 'very fast' shutdown, because the InnoDB layer may have - committed or prepared transactions and we don't want to lose them. */ + /* Check that there are no longer transactions, except for + PREPARED ones. We need this wait even for the 'very fast' + shutdown, because the InnoDB layer may have committed or + prepared transactions and we don't want to lose them. */ if (trx_n_mysql_transactions > 0 - || UT_LIST_GET_LEN(trx_sys->trx_list) > 0) { + || UT_LIST_GET_LEN(trx_sys->trx_list) > trx_n_prepared) { mutex_exit(&kernel_mutex); diff --git a/storage/innodb_plugin/trx/trx0sys.c b/storage/innodb_plugin/trx/trx0sys.c index 6eb356947cc..352fa6af219 100644 --- a/storage/innodb_plugin/trx/trx0sys.c +++ b/storage/innodb_plugin/trx/trx0sys.c @@ -37,6 +37,7 @@ Created 3/26/1996 Heikki Tuuri #include "trx0rseg.h" #include "trx0undo.h" #include "srv0srv.h" +#include "srv0start.h" #include "trx0purge.h" #include "log0log.h" #include "os0file.h" @@ -1548,10 +1549,12 @@ void trx_sys_close(void) /*===============*/ { + trx_t* trx; trx_rseg_t* rseg; read_view_t* view; ut_ad(trx_sys != NULL); + ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); /* Check that all read views are closed except read view owned by a purge. */ @@ -1583,6 +1586,13 @@ trx_sys_close(void) mem_free(trx_doublewrite); trx_doublewrite = NULL; + /* Only prepared transactions may be left in the system. Free them. */ + ut_a(UT_LIST_GET_LEN(trx_sys->trx_list) == trx_n_prepared); + + while ((trx = UT_LIST_GET_FIRST(trx_sys->trx_list)) != NULL) { + trx_free_prepared(trx); + } + /* There can't be any active transactions. */ rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); diff --git a/storage/innodb_plugin/trx/trx0trx.c b/storage/innodb_plugin/trx/trx0trx.c index f0bbf220815..7f3a3fcb4bf 100644 --- a/storage/innodb_plugin/trx/trx0trx.c +++ b/storage/innodb_plugin/trx/trx0trx.c @@ -50,6 +50,9 @@ UNIV_INTERN sess_t* trx_dummy_sess = NULL; /** Number of transactions currently allocated for MySQL: protected by the kernel mutex */ UNIV_INTERN ulint trx_n_mysql_transactions = 0; +/* Number of transactions currently in the XA PREPARED state: protected by +the kernel mutex */ +UNIV_INTERN ulint trx_n_prepared = 0; /*************************************************************//** Set detailed error message for the transaction. */ @@ -333,6 +336,60 @@ trx_free( mem_free(trx); } +/********************************************************************//** +At shutdown, frees a transaction object that is in the PREPARED state. */ +UNIV_INTERN +void +trx_free_prepared( +/*==============*/ + trx_t* trx) /*!< in, own: trx object */ +{ + ut_ad(mutex_own(&kernel_mutex)); + ut_a(trx->conc_state == TRX_PREPARED); + ut_a(trx->magic_n == TRX_MAGIC_N); + + /* Prepared transactions are sort of active; they allow + ROLLBACK and COMMIT operations. Because the system does not + contain any other transactions than prepared transactions at + the shutdown stage and because a transaction cannot become + PREPARED while holding locks, it is safe to release the locks + held by PREPARED transactions here at shutdown.*/ + lock_release_off_kernel(trx); + + trx_undo_free_prepared(trx); + + mutex_free(&trx->undo_mutex); + + if (trx->undo_no_arr) { + trx_undo_arr_free(trx->undo_no_arr); + } + + ut_a(UT_LIST_GET_LEN(trx->signals) == 0); + ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0); + + ut_a(trx->wait_lock == NULL); + ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); + + ut_a(!trx->has_search_latch); + + ut_a(trx->dict_operation_lock_mode == 0); + + if (trx->lock_heap) { + mem_heap_free(trx->lock_heap); + } + + if (trx->global_read_view_heap) { + mem_heap_free(trx->global_read_view_heap); + } + + ut_a(ib_vector_is_empty(trx->autoinc_locks)); + ib_vector_free(trx->autoinc_locks); + + UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); + + mem_free(trx); +} + /********************************************************************//** Frees a transaction object for MySQL. */ UNIV_INTERN @@ -463,6 +520,7 @@ trx_lists_init_at_db_start(void) if (srv_force_recovery == 0) { trx->conc_state = TRX_PREPARED; + trx_n_prepared++; } else { fprintf(stderr, "InnoDB: Since" @@ -541,6 +599,7 @@ trx_lists_init_at_db_start(void) trx->conc_state = TRX_PREPARED; + trx_n_prepared++; } else { fprintf(stderr, "InnoDB: Since" @@ -820,6 +879,11 @@ trx_commit_off_kernel( || trx->conc_state == TRX_PREPARED); ut_ad(mutex_own(&kernel_mutex)); + if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) { + ut_a(trx_n_prepared > 0); + trx_n_prepared--; + } + /* The following assignment makes the transaction committed in memory and makes its changes to data visible to other transactions. NOTE that there is a small discrepancy from the strict formal @@ -1857,6 +1921,7 @@ trx_prepare_off_kernel( /*--------------------------------------*/ trx->conc_state = TRX_PREPARED; + trx_n_prepared++; /*--------------------------------------*/ if (lsn) { @@ -2031,10 +2096,11 @@ trx_get_trx_by_xid( while (trx) { /* Compare two X/Open XA transaction id's: their length should be the same and binary comparison - of gtrid_lenght+bqual_length bytes should be + of gtrid_length+bqual_length bytes should be the same */ - if (trx->conc_state == TRX_PREPARED + if (trx->is_recovered + && trx->conc_state == TRX_PREPARED && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, diff --git a/storage/innodb_plugin/trx/trx0undo.c b/storage/innodb_plugin/trx/trx0undo.c index 76e88948e41..68ff82f618c 100644 --- a/storage/innodb_plugin/trx/trx0undo.c +++ b/storage/innodb_plugin/trx/trx0undo.c @@ -36,6 +36,7 @@ Created 3/26/1996 Heikki Tuuri #include "trx0rseg.h" #include "trx0trx.h" #include "srv0srv.h" +#include "srv0start.h" #include "trx0rec.h" #include "trx0purge.h" @@ -1976,4 +1977,31 @@ trx_undo_insert_cleanup( mutex_exit(&(rseg->mutex)); } + +/********************************************************************//** +At shutdown, frees the undo logs of a PREPARED transaction. */ +UNIV_INTERN +void +trx_undo_free_prepared( +/*===================*/ + trx_t* trx) /*!< in/out: PREPARED transaction */ +{ + mutex_enter(&trx->rseg->mutex); + + ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); + + if (trx->update_undo) { + ut_a(trx->update_undo->state == TRX_UNDO_PREPARED); + UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list, + trx->update_undo); + trx_undo_mem_free(trx->update_undo); + } + if (trx->insert_undo) { + ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED); + UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list, + trx->insert_undo); + trx_undo_mem_free(trx->insert_undo); + } + mutex_exit(&trx->rseg->mutex); +} #endif /* !UNIV_HOTBACKUP */ From 1c3087bbc944c5e25475fb42affaa6a2554e0c29 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 8 Apr 2011 10:23:46 +0300 Subject: [PATCH 14/57] Increment InnoDB version from 1.1.6 to 1.1.7 InnoDB 1.1.6 was released with MySQL 5.5.11 --- storage/innobase/include/univ.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 2c7d32bd1a3..9816edc2eaa 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -51,7 +51,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 1 #define INNODB_VERSION_MINOR 1 -#define INNODB_VERSION_BUGFIX 6 +#define INNODB_VERSION_BUGFIX 7 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From d0b1a6466c431046d1915c2c065d3d5d4ad011e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Apr 2011 16:40:28 +0300 Subject: [PATCH 15/57] Bug #11760042 - 52409: Assertion failure: long semaphore wait In ha_innobase::create(), we check some things while holding an exclusive lock on the data dictionary. Defer the locking and the creation of transactions until after the checks have passed. The THDVAR could hang due to a mutex wait (see Bug #11750569 - 41163: deadlock in mysqld: LOCK_global_system_variables and LOCK_open), and we want to avoid waiting while holding InnoDB mutexes. innobase_index_name_is_reserved(): Replace the parameter trx_t with THD, so that the test can be performed before starting an InnoDB transaction. We only needed trx->mysql_thd. ha_innobase::create(): Create transaction and lock the data dictionary only after passing the basic tests. create_table_def(): Move the IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS check to ha_innobase::create(). Assign to srv_lower_case_table_names while holding dict_sys->mutex. ha_innobase::delete_table(), ha_innobase::rename_table(), innobase_rename_table(): Assign srv_lower_case_table_names as late as possible. Here, the variable is not necessarily protected by dict_sys->mutex. ha_innobase::add_index(): Invoke innobase_index_name_is_reserved() and innobase_check_index_keys() before allocating anything. rb:618 approved by Jimmy Yang --- storage/innobase/handler/ha_innodb.cc | 93 ++++++++---------- storage/innodb_plugin/ChangeLog | 5 + storage/innodb_plugin/handler/ha_innodb.cc | 94 ++++++++----------- storage/innodb_plugin/handler/ha_innodb.h | 11 +-- .../innodb_plugin/handler/handler0alter.cc | 55 +++++------ 5 files changed, 114 insertions(+), 144 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 75c732c44d4..2afacf6d2a8 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -189,7 +189,7 @@ innobase_index_name_is_reserved( /*============================*/ /* out: true if index name matches a reserved name */ - const trx_t* trx, /* in: InnoDB transaction handle */ + THD* thd, /* in/out: MySQL connection */ const TABLE* form, /* in: information on table columns and indexes */ const char* norm_name); /* in: table name */ @@ -5285,10 +5285,6 @@ create_table_def( DBUG_PRINT("enter", ("table_name: %s", table_name)); ut_a(trx->mysql_thd != NULL); - if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(table_name, - (THD*) trx->mysql_thd)) { - DBUG_RETURN(HA_ERR_GENERIC); - } n_cols = form->s->fields; @@ -5397,6 +5393,8 @@ err_col: col_len); } + srv_lower_case_table_names = lower_case_table_names; + error = row_create_table_for_mysql(table, trx); innodb_check_for_record_too_big_error(flags & DICT_TF_COMPACT, error); @@ -5642,6 +5640,35 @@ ha_innobase::create( DBUG_RETURN(HA_ERR_TO_BIG_ROW); } + strcpy(name2, name); + + normalize_table_name(norm_name, name2); + + /* Create the table definition in InnoDB */ + + flags = form->s->row_type != ROW_TYPE_REDUNDANT ? DICT_TF_COMPACT : 0; + + /* Look for a primary key */ + + primary_key_no= (form->s->primary_key != MAX_KEY ? + (int) form->s->primary_key : + -1); + + /* Our function row_get_mysql_key_number_for_index assumes + the primary key is always number 0, if it exists */ + + DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0); + + /* Check for name conflicts (with reserved name) for + any user indices to be created. */ + if (innobase_index_name_is_reserved(thd, form, norm_name)) { + DBUG_RETURN(-1); + } + + if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(norm_name, thd)) { + DBUG_RETURN(HA_ERR_GENERIC); + } + /* Get the transaction associated with the current thd, or create one if not yet created */ @@ -5665,48 +5692,12 @@ ha_innobase::create( trx->check_unique_secondary = FALSE; } - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - - strcpy(name2, name); - - normalize_table_name(norm_name, name2); - /* Latch the InnoDB data dictionary exclusively so that no deadlocks or lock waits can happen in it during a table create operation. Drop table etc. do this latching in row0mysql.c. */ row_mysql_lock_data_dictionary(trx); - /* Create the table definition in InnoDB */ - - flags = 0; - - if (form->s->row_type != ROW_TYPE_REDUNDANT) { - flags |= DICT_TF_COMPACT; - } - - /* Look for a primary key */ - - primary_key_no= (form->s->primary_key != MAX_KEY ? - (int) form->s->primary_key : - -1); - - /* Our function row_get_mysql_key_number_for_index assumes - the primary key is always number 0, if it exists */ - - DBUG_ASSERT(primary_key_no == -1 || primary_key_no == 0); - - /* Check for name conflicts (with reserved name) for - any user indices to be created. */ - if (innobase_index_name_is_reserved(trx, form, norm_name)) { - error = -1; - goto cleanup; - } - error = create_table_def(trx, form, norm_name, create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL, flags); @@ -5936,12 +5927,6 @@ ha_innobase::delete_table( trx_search_latch_release_if_reserved(parent_trx); - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - trx = trx_allocate_for_mysql(); trx->mysql_thd = thd; @@ -5961,6 +5946,8 @@ ha_innobase::delete_table( /* Drop the table in InnoDB */ + srv_lower_case_table_names = lower_case_table_names; + error = row_drop_table_for_mysql(norm_name, trx, thd_sql_command(thd) == SQLCOM_DROP_DB); @@ -6089,12 +6076,6 @@ ha_innobase::rename_table( trx_search_latch_release_if_reserved(parent_trx); - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - trx = trx_allocate_for_mysql(); trx->mysql_thd = thd; INNOBASE_COPY_STMT(thd, trx); @@ -6114,6 +6095,8 @@ ha_innobase::rename_table( /* Rename the table in InnoDB */ + srv_lower_case_table_names = lower_case_table_names; + error = row_rename_table_for_mysql(norm_from, norm_to, trx); /* Flush the log to reduce probability that the .frm files and @@ -8826,7 +8809,7 @@ innobase_index_name_is_reserved( /*============================*/ /* out: true if an index name matches the reserved name */ - const trx_t* trx, /* in: InnoDB transaction handle */ + THD* thd, /* in/out: MySQL connection */ const TABLE* form, /* in: information on table columns and indexes */ const char* norm_name) /* in: table name */ @@ -8840,7 +8823,7 @@ innobase_index_name_is_reserved( if (innobase_strcasecmp(key->name, innobase_index_reserve_name) == 0) { /* Push warning to mysql */ - push_warning_printf((THD*) trx->mysql_thd, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_CANT_CREATE_TABLE, "Cannot Create Index with name " diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index d062fc7e648..0b201816819 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-04-07 The InnoDB Team + + * handler/ha_innodb.cc, handler/ha_innodb.h, handler/handler0alter.cc: + Fix Bug #52409 Assertion failure: long semaphore wait + 2011-04-07 The InnoDB Team * handler/ha_innodb.cc, include/trx0trx.h, include/trx0undo.h, diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 7f92d797d30..2b0dbf82b34 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -6023,10 +6023,6 @@ create_table_def( DBUG_PRINT("enter", ("table_name: %s", table_name)); ut_a(trx->mysql_thd != NULL); - if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(table_name, - (THD*) trx->mysql_thd)) { - DBUG_RETURN(HA_ERR_GENERIC); - } /* MySQL does the name length check. But we do additional check on the name length here */ @@ -6146,6 +6142,8 @@ err_col: col_len); } + srv_lower_case_table_names = lower_case_table_names; + error = row_create_table_for_mysql(table, trx); if (error == DB_DUPLICATE_KEY) { @@ -6562,42 +6560,17 @@ ha_innobase::create( DBUG_RETURN(HA_ERR_TO_BIG_ROW); } - /* Get the transaction associated with the current thd, or create one - if not yet created */ - - parent_trx = check_trx_exists(thd); - - /* In case MySQL calls this in the middle of a SELECT query, release - possible adaptive hash latch to avoid deadlocks of threads */ - - trx_search_latch_release_if_reserved(parent_trx); - - trx = innobase_trx_allocate(thd); - - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - strcpy(name2, name); normalize_table_name(norm_name, name2); - /* Latch the InnoDB data dictionary exclusively so that no deadlocks - or lock waits can happen in it during a table create operation. - Drop table etc. do this latching in row0mysql.c. */ - - row_mysql_lock_data_dictionary(trx); - /* Create the table definition in InnoDB */ flags = 0; /* Validate create options if innodb_strict_mode is set. */ if (!create_options_are_valid(thd, form, create_info)) { - error = ER_ILLEGAL_HA_CREATE_OPTION; - goto cleanup; + DBUG_RETURN(ER_ILLEGAL_HA_CREATE_OPTION); } if (create_info->key_block_size) { @@ -6739,16 +6712,37 @@ ha_innobase::create( /* Check for name conflicts (with reserved name) for any user indices to be created. */ - if (innobase_index_name_is_reserved(trx, form->key_info, + if (innobase_index_name_is_reserved(thd, form->key_info, form->s->keys)) { - error = -1; - goto cleanup; + DBUG_RETURN(-1); + } + + if (IS_MAGIC_TABLE_AND_USER_DENIED_ACCESS(norm_name, thd)) { + DBUG_RETURN(HA_ERR_GENERIC); } if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { flags |= DICT_TF2_TEMPORARY << DICT_TF2_SHIFT; } + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + + trx = innobase_trx_allocate(thd); + + /* Latch the InnoDB data dictionary exclusively so that no deadlocks + or lock waits can happen in it during a table create operation. + Drop table etc. do this latching in row0mysql.c. */ + + row_mysql_lock_data_dictionary(trx); + error = create_table_def(trx, form, norm_name, create_info->options & HA_LEX_CREATE_TMP_TABLE ? name2 : NULL, flags); @@ -6992,18 +6986,14 @@ ha_innobase::delete_table( trx = innobase_trx_allocate(thd); - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - name_len = strlen(name); ut_a(name_len < 1000); /* Drop the table in InnoDB */ + srv_lower_case_table_names = lower_case_table_names; + error = row_drop_table_for_mysql(norm_name, trx, thd_sql_command(thd) == SQLCOM_DROP_DB); @@ -7119,12 +7109,6 @@ innobase_rename_table( char* norm_to; char* norm_from; - if (lower_case_table_names) { - srv_lower_case_table_names = TRUE; - } else { - srv_lower_case_table_names = FALSE; - } - // Magic number 64 arbitrary norm_to = (char*) my_malloc(strlen(to) + 64, MYF(0)); norm_from = (char*) my_malloc(strlen(from) + 64, MYF(0)); @@ -7139,6 +7123,8 @@ innobase_rename_table( row_mysql_lock_data_dictionary(trx); } + srv_lower_case_table_names = lower_case_table_names; + error = row_rename_table_for_mysql( norm_from, norm_to, trx, lock_and_commit); @@ -10700,19 +10686,19 @@ static int show_innodb_vars(THD *thd, SHOW_VAR *var, char *buff) return 0; } -/*********************************************************************** +/*********************************************************************//** This function checks each index name for a table against reserved -system default primary index name 'GEN_CLUST_INDEX'. If a name matches, -this function pushes an warning message to the client, and returns true. */ +system default primary index name 'GEN_CLUST_INDEX'. If a name +matches, this function pushes an warning message to the client, +and returns true. +@return true if the index name matches the reserved name */ extern "C" UNIV_INTERN bool innobase_index_name_is_reserved( /*============================*/ - /* out: true if an index name - matches the reserved name */ - const trx_t* trx, /* in: InnoDB transaction handle */ - const KEY* key_info, /* in: Indexes to be created */ - ulint num_of_keys) /* in: Number of indexes to + THD* thd, /*!< in/out: MySQL connection */ + const KEY* key_info, /*!< in: Indexes to be created */ + ulint num_of_keys) /*!< in: Number of indexes to be created. */ { const KEY* key; @@ -10724,7 +10710,7 @@ innobase_index_name_is_reserved( if (innobase_strcasecmp(key->name, innobase_index_reserve_name) == 0) { /* Push warning to mysql */ - push_warning_printf((THD*) trx->mysql_thd, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WRONG_NAME_FOR_INDEX, "Cannot Create Index with name " diff --git a/storage/innodb_plugin/handler/ha_innodb.h b/storage/innodb_plugin/handler/ha_innodb.h index 7a8f29853de..f7a5456b1a7 100644 --- a/storage/innodb_plugin/handler/ha_innodb.h +++ b/storage/innodb_plugin/handler/ha_innodb.h @@ -317,15 +317,14 @@ innobase_trx_allocate( This function checks each index name for a table against reserved system default primary index name 'GEN_CLUST_INDEX'. If a name matches, this function pushes an warning message to the client, -and returns true. */ +and returns true. +@return true if the index name matches the reserved name */ extern "C" bool innobase_index_name_is_reserved( /*============================*/ - /* out: true if the index name - matches the reserved name */ - const trx_t* trx, /* in: InnoDB transaction handle */ - const KEY* key_info, /* in: Indexes to be created */ - ulint num_of_keys); /* in: Number of indexes to + THD* thd, /*!< in/out: MySQL connection */ + const KEY* key_info, /*!< in: Indexes to be created */ + ulint num_of_keys); /*!< in: Number of indexes to be created. */ diff --git a/storage/innodb_plugin/handler/handler0alter.cc b/storage/innodb_plugin/handler/handler0alter.cc index dc1317d5c5a..485e03737e3 100644 --- a/storage/innodb_plugin/handler/handler0alter.cc +++ b/storage/innodb_plugin/handler/handler0alter.cc @@ -649,11 +649,30 @@ ha_innobase::add_index( update_thd(); - heap = mem_heap_create(1024); - /* In case MySQL calls this in the middle of a SELECT query, release possible adaptive hash latch to avoid deadlocks of threads. */ trx_search_latch_release_if_reserved(prebuilt->trx); + + /* Check if the index name is reserved. */ + if (innobase_index_name_is_reserved(user_thd, key_info, num_of_keys)) { + DBUG_RETURN(-1); + } + + innodb_table = indexed_table + = dict_table_get(prebuilt->table->name, FALSE); + + if (UNIV_UNLIKELY(!innodb_table)) { + DBUG_RETURN(HA_ERR_NO_SUCH_TABLE); + } + + /* Check that index keys are sensible */ + error = innobase_check_index_keys(key_info, num_of_keys, innodb_table); + + if (UNIV_UNLIKELY(error)) { + DBUG_RETURN(error); + } + + heap = mem_heap_create(1024); trx_start_if_not_started(prebuilt->trx); /* Create a background transaction for the operations on @@ -661,32 +680,6 @@ ha_innobase::add_index( trx = innobase_trx_allocate(user_thd); trx_start_if_not_started(trx); - innodb_table = indexed_table - = dict_table_get(prebuilt->table->name, FALSE); - - if (UNIV_UNLIKELY(!innodb_table)) { - error = HA_ERR_NO_SUCH_TABLE; - goto err_exit; - } - - /* Check if the index name is reserved. */ - if (innobase_index_name_is_reserved(trx, key_info, num_of_keys)) { - error = -1; - } else { - /* Check that index keys are sensible */ - error = innobase_check_index_keys(key_info, num_of_keys, - innodb_table); - } - - if (UNIV_UNLIKELY(error)) { -err_exit: - mem_heap_free(heap); - trx_general_rollback_for_mysql(trx, NULL); - trx_free_for_mysql(trx); - trx_commit_for_mysql(prebuilt->trx); - DBUG_RETURN(error); - } - /* Create table containing all indexes to be built in this alter table add index so that they are in the correct order in the table. */ @@ -758,8 +751,12 @@ err_exit: ut_d(dict_table_check_for_dup_indexes(innodb_table, FALSE)); + mem_heap_free(heap); + trx_general_rollback_for_mysql(trx, NULL); row_mysql_unlock_data_dictionary(trx); - goto err_exit; + trx_free_for_mysql(trx); + trx_commit_for_mysql(prebuilt->trx); + DBUG_RETURN(error); } trx->table_id = indexed_table->id; From 1ef8d5fc34fb3e86f7ea1838e6d9867a10dd2910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 12 Apr 2011 09:22:43 +0300 Subject: [PATCH 16/57] Bug #12329920 ASSERT UT_ERROR IN SYNC_THREAD_LEVELS_NONEMPTY_TRX SRV_CONC_FORCE_EXIT_INNODB This is a bogus UNIV_SYNC_DEBUG assertion failure that I introduced when introducing assertions for checking that InnoDB is not holding any mutexes or rw-locks when returning control to MySQL. srv_suspend_mysql_thread(): Release dict_operation_lock before invoking srv_conc_force_exit_innodb(), which would now check that the thread is not holding any mutexes or rw-locks. After resuming, check sync_thread_levels_nonempty_trx() and do srv_conc_force_enter_innodb() before reacquiring the dict_operation_lock. rb:646 approved by Sunny Bains --- storage/innobase/srv/srv0srv.c | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index ecf1432016d..789fe3bf36a 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -1624,17 +1624,6 @@ srv_suspend_mysql_thread( mutex_exit(&kernel_mutex); - if (trx->declared_to_be_inside_innodb) { - - was_declared_inside_innodb = TRUE; - - /* We must declare this OS thread to exit InnoDB, since a - possible other thread holding a lock which this thread waits - for must be allowed to enter, sooner or later */ - - srv_conc_force_exit_innodb(trx); - } - had_dict_lock = trx->dict_operation_lock_mode; switch (had_dict_lock) { @@ -1662,12 +1651,34 @@ srv_suspend_mysql_thread( ut_a(trx->dict_operation_lock_mode == 0); + if (trx->declared_to_be_inside_innodb) { + + was_declared_inside_innodb = TRUE; + + /* We must declare this OS thread to exit InnoDB, since a + possible other thread holding a lock which this thread waits + for must be allowed to enter, sooner or later */ + + srv_conc_force_exit_innodb(trx); + } + /* Suspend this thread and wait for the event. */ thd_wait_begin(trx->mysql_thd, THD_WAIT_ROW_TABLE_LOCK); os_event_wait(event); thd_wait_end(trx->mysql_thd); +#ifdef UNIV_SYNC_DEBUG + ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); +#endif /* UNIV_SYNC_DEBUG */ + + if (was_declared_inside_innodb) { + + /* Return back inside InnoDB */ + + srv_conc_force_enter_innodb(trx); + } + /* After resuming, reacquire the data dictionary latch if necessary. */ @@ -1683,13 +1694,6 @@ srv_suspend_mysql_thread( break; } - if (was_declared_inside_innodb) { - - /* Return back inside InnoDB */ - - srv_conc_force_enter_innodb(trx); - } - mutex_enter(&kernel_mutex); /* Release the slot for others to use */ @@ -1744,10 +1748,6 @@ srv_suspend_mysql_thread( trx->error_state = DB_INTERRUPTED; } - -#ifdef UNIV_SYNC_DEBUG - ut_ad(!sync_thread_levels_nonempty_trx(trx->has_search_latch)); -#endif /* UNIV_SYNC_DEBUG */ } /********************************************************************//** From 404504bbbd32bbc5bfbed646c334f94ead8d4b51 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 14 Apr 2011 14:25:15 +0200 Subject: [PATCH 17/57] Bug #12360501 MTR --GCOV DOES NOT WORK WITH CMAKE BUILDS Changed to use $bindir instead of $basedir Simplified search for files: find all *.gcno Also, .msg and .err files had been mixed up --- mysql-test/lib/mtr_gcov.pl | 24 ++++++++++-------------- mysql-test/mysql-test-run.pl | 6 +++--- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/mysql-test/lib/mtr_gcov.pl b/mysql-test/lib/mtr_gcov.pl index d8fb1c0a07d..6f9e744a548 100644 --- a/mysql-test/lib/mtr_gcov.pl +++ b/mysql-test/lib/mtr_gcov.pl @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (C) 2004, 2006 MySQL AB, 2009 Sun Microsystems, Inc. +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ sub gcov_prepare ($) { # # Collect gcov statistics. # Arguments: -# $dir basedir, normally source directory +# $dir basedir, normally build directory # $gcov gcov utility program [path] name # $gcov_msg message file name # $gcov_err error file name @@ -43,29 +43,25 @@ sub gcov_collect ($$$) { my $start_dir= cwd(); print "Collecting source coverage info using '$gcov'...\n"; - -f "$start_dir/$gcov_msg" and unlink("$start_dir/$gcov_msg"); - -f "$start_dir/$gcov_err" and unlink("$start_dir/$gcov_err"); + -f "$dir/$gcov_msg" and unlink("$dir/$gcov_msg"); + -f "$dir/$gcov_err" and unlink("$dir/$gcov_err"); my @dirs= `find "$dir" -type d -print | sort`; #print "List of directories:\n@dirs\n"; foreach my $d ( @dirs ) { - my $dir_reported= 0; chomp($d); chdir($d) or next; - foreach my $f ( (glob("*.h"), glob("*.cc"), glob("*.c")) ) { - $f =~ /(.*)\.[ch]c?/; - -f "$1.gcno" or next; - if (!$dir_reported) { - print "Collecting in '$d'...\n"; - $dir_reported= 1; - } - system("$gcov $f 2>>$start_dir/$gcov_err >>$start_dir/$gcov_msg"); + my @flist= glob("*.*.gcno"); + print ("Collecting in '$d'...\n") if @flist; + + foreach my $f (@flist) { + system("$gcov $f 2>>$dir/$gcov_err >>$dir/$gcov_msg"); } chdir($start_dir); } - print "gcov info in $gcov_msg, errors in $gcov_err\n"; + print "gcov info in $dir/$gcov_msg, errors in $dir/$gcov_err\n"; } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 0d227170476..2097bc8f63a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -210,8 +210,8 @@ our $opt_clean_vardir= $ENV{'MTR_CLEAN_VARDIR'}; our $opt_gcov; our $opt_gcov_exe= "gcov"; -our $opt_gcov_err= "mysql-test-gcov.msg"; -our $opt_gcov_msg= "mysql-test-gcov.err"; +our $opt_gcov_err= "mysql-test-gcov.err"; +our $opt_gcov_msg= "mysql-test-gcov.msg"; our $opt_gprof; our %gprof_dirs; @@ -507,7 +507,7 @@ sub main { mtr_print_line(); if ( $opt_gcov ) { - gcov_collect($basedir, $opt_gcov_exe, + gcov_collect($bindir, $opt_gcov_exe, $opt_gcov_msg, $opt_gcov_err); } From a23276c5c11dc1f7500c3e71de0e196c211b432e Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 14 Apr 2011 16:17:58 +0200 Subject: [PATCH 18/57] Bug #12351213 MTR --VS-CONFIG DOES NOT WORK LIKE MTR_VS_CONFIG Fix for --vs-config applied Find.pm incorrectly tested an unitialized local variable instead of the global, corrected. Find.pm is also wrong in 5.5: uses a non-existent global variable. Fix when merging up. --- mysql-test/lib/My/Find.pm | 6 ++---- mysql-test/mysql-test-run.pl | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mysql-test/lib/My/Find.pm b/mysql-test/lib/My/Find.pm index 9c89a7e4e2a..8cbd6db3201 100644 --- a/mysql-test/lib/My/Find.pm +++ b/mysql-test/lib/My/Find.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (C) 2008 MySQL AB +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -28,8 +28,6 @@ use My::Platform; use base qw(Exporter); our @EXPORT= qw(my_find_bin my_find_dir my_find_file NOT_REQUIRED); -our $vs_config_dir; - my $bin_extension= ".exe" if IS_WINDOWS; # Helper function to be used for fourth parameter to find functions @@ -158,7 +156,7 @@ sub my_find_paths { # User can select to look in a special build dir # which is a subdirectory of any of the paths my @extra_dirs; - my $build_dir= $vs_config_dir || $ENV{MTR_VS_CONFIG} || $ENV{MTR_BUILD_DIR}; + my $build_dir= $::opt_vs_config || $ENV{MTR_VS_CONFIG} || $ENV{MTR_BUILD_DIR}; push(@extra_dirs, $build_dir) if defined $build_dir; if (defined $extension){ diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2301b2444d3..9b8b1dc67cf 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl # -*- cperl -*- -# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -893,7 +893,7 @@ sub command_line_setup { 'ssl|with-openssl' => \$opt_ssl, 'skip-ssl' => \$opt_skip_ssl, 'compress' => \$opt_compress, - 'vs-config' => \$opt_vs_config, + 'vs-config=s' => \$opt_vs_config, # Max number of parallel threads to use 'parallel=s' => \$opt_parallel, From 331058bc914f9d59441c3b98003de9a6f7fd7811 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 15 Apr 2011 10:30:52 +0200 Subject: [PATCH 19/57] Bug #12360195 MTR DOES NOT IGNORE TABS IN EXPERIMENTAL FILE Instead of just filtering space, filter white space (\s) I left the default.experimental file as is, with tabs. --- mysql-test/mysql-test-run.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 9b8b1dc67cf..2897ae3142a 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1123,7 +1123,7 @@ sub command_line_setup { chomp; # remove comments (# foo) at the beginning of the line, or after a # blank at the end of the line - s/( +|^)#.*$//; + s/(\s+|^)#.*$//; # If @ platform specifier given, use this entry only if it contains # @ or @! where xxx != platform if (/\@.*/) @@ -1134,8 +1134,8 @@ sub command_line_setup { s/\@.*$//; } # remove whitespace - s/^ +//; - s/ +$//; + s/^\s+//; + s/\s+$//; # if nothing left, don't need to remember this line if ( $_ eq "" ) { next; From 7b4cafe9b9357cfc6f080c55f1bafab5c01d7cf1 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Mon, 18 Apr 2011 10:44:41 +0200 Subject: [PATCH 20/57] Bug 11758558 - 50774: WRONG RESULTSET WHEN TIMESTAMP VALUES ARE APPENDED WITH .0 The bug was fixed by the patch for bug number BUG 11763109 - 55779: SELECT DOES NOT WORK PROPERLY IN MYSQL SERVER VERSION "5.1.42 SUSE MYSQL (Exact same fix as was proposed for this bug.) Since the motivation for the two bug reports was completely different, however, it still makes sense to push the test case. This patch contains only the test case. --- mysql-test/r/type_timestamp.result | 63 ++++++++++++++++++++++++++++++ mysql-test/t/type_timestamp.test | 47 ++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result index e88d3462466..3176879343c 100644 --- a/mysql-test/r/type_timestamp.result +++ b/mysql-test/r/type_timestamp.result @@ -547,4 +547,67 @@ a 2000-01-01 00:00:01 2000-01-01 00:00:01 DROP TABLE t1; +# +# Bug#50774: failed to get the correct resultset when timestamp values +# are appended with .0 +# +CREATE TABLE t1 ( a TIMESTAMP, KEY ( a ) ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:01' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:02' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:03' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:04' ); +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; +a +2010-02-01 09:31:02 +2010-02-01 09:31:03 +2010-02-01 09:31:04 +SELECT * FROM t1 WHERE '2010-02-01 09:31:02.0' <= a; +a +2010-02-01 09:31:02 +2010-02-01 09:31:03 +2010-02-01 09:31:04 +SELECT * FROM t1 WHERE a <= '2010-02-01 09:31:02.0'; +a +2010-02-01 09:31:01 +2010-02-01 09:31:02 +SELECT * FROM t1 WHERE '2010-02-01 09:31:02.0' >= a; +a +2010-02-01 09:31:01 +2010-02-01 09:31:02 +EXPLAIN +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; +id select_type table type possible_keys key key_len ref rows Extra +x x x range x x x x x x +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; +a +2010-02-01 09:31:02 +2010-02-01 09:31:03 +2010-02-01 09:31:04 +CREATE TABLE t2 ( a TIMESTAMP, KEY ( a DESC ) ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:01' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:02' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:03' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:04' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:05' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:06' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:07' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:08' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:09' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:10' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:11' ); +# The bug would cause the range optimizer's comparison to use an open +# interval here. This reveals itself only in the number of reads +# performed. +FLUSH STATUS; +EXPLAIN +SELECT * FROM t2 WHERE a < '2010-02-01 09:31:02.0'; +id select_type table type possible_keys key key_len ref rows Extra +x x x range x x x x x x +SELECT * FROM t2 WHERE a < '2010-02-01 09:31:02.0'; +a +2010-02-01 09:31:01 +SHOW STATUS LIKE 'Handler_read_next'; +Variable_name Value +Handler_read_next 1 +DROP TABLE t1, t2; End of 5.1 tests diff --git a/mysql-test/t/type_timestamp.test b/mysql-test/t/type_timestamp.test index 602f6f089c2..53b45fc6732 100644 --- a/mysql-test/t/type_timestamp.test +++ b/mysql-test/t/type_timestamp.test @@ -373,4 +373,51 @@ SELECT a FROM t1 WHERE a >= '20000101000000'; DROP TABLE t1; +--echo # +--echo # Bug#50774: failed to get the correct resultset when timestamp values +--echo # are appended with .0 +--echo # +CREATE TABLE t1 ( a TIMESTAMP, KEY ( a ) ); + +INSERT INTO t1 VALUES( '2010-02-01 09:31:01' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:02' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:03' ); +INSERT INTO t1 VALUES( '2010-02-01 09:31:04' ); + +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; +SELECT * FROM t1 WHERE '2010-02-01 09:31:02.0' <= a; +SELECT * FROM t1 WHERE a <= '2010-02-01 09:31:02.0'; +SELECT * FROM t1 WHERE '2010-02-01 09:31:02.0' >= a; + +--replace_column 1 x 2 x 3 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; +SELECT * FROM t1 WHERE a >= '2010-02-01 09:31:02.0'; + +CREATE TABLE t2 ( a TIMESTAMP, KEY ( a DESC ) ); + +INSERT INTO t2 VALUES( '2010-02-01 09:31:01' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:02' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:03' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:04' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:05' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:06' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:07' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:08' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:09' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:10' ); +INSERT INTO t2 VALUES( '2010-02-01 09:31:11' ); + +--echo # The bug would cause the range optimizer's comparison to use an open +--echo # interval here. This reveals itself only in the number of reads +--echo # performed. +FLUSH STATUS; +--replace_column 1 x 2 x 3 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t2 WHERE a < '2010-02-01 09:31:02.0'; +SELECT * FROM t2 WHERE a < '2010-02-01 09:31:02.0'; +SHOW STATUS LIKE 'Handler_read_next'; + +DROP TABLE t1, t2; + --echo End of 5.1 tests From f3195e50ab40be7be9d3ee9d9d3ee9ffda3d03e2 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 18 Apr 2011 11:47:14 +0200 Subject: [PATCH 21/57] Bug #12365486 MTR FAILS TO FIND WARNINGS IN SERVER LOG WITH --VALGRIND COMBINED WITH --DEBUG With this combination, outoput was directed to .trace but not all parts of MTR was aware of this. Replace .err with .trace at the earliest possible place --- mysql-test/lib/My/ConfigFactory.pm | 8 ++++++-- mysql-test/mysql-test-run.pl | 9 +-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index bb990a9f8d2..7688283fdc1 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -141,7 +141,11 @@ sub fix_tmpdir { sub fix_log_error { my ($self, $config, $group_name, $group)= @_; my $dir= $self->{ARGS}->{vardir}; - return "$dir/log/$group_name.err"; + if ( $::opt_valgrind and $::opt_debug ) { + return "$dir/log/$group_name.trace"; + } else { + return "$dir/log/$group_name.err"; + } } sub fix_log { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2897ae3142a..1c7efccc69d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -256,7 +256,7 @@ my $opt_strace_client; our $opt_user = "root"; -my $opt_valgrind= 0; +our $opt_valgrind= 0; my $opt_valgrind_mysqld= 0; my $opt_valgrind_mysqltest= 0; my @default_valgrind_args= ("--show-reachable=yes"); @@ -4544,13 +4544,6 @@ sub mysqld_start ($$) { unlink($mysqld->value('pid-file')); my $output= $mysqld->value('#log-error'); - if ( $opt_valgrind and $opt_debug ) - { - # When both --valgrind and --debug is selected, send - # all output to the trace file, making it possible to - # see the exact location where valgrind complains - $output= "$opt_vardir/log/".$mysqld->name().".trace"; - } # Remember this log file for valgrind error report search $mysqld_logs{$output}= 1 if $opt_valgrind; # Remember data dir for gmon.out files if using gprof From 5038060b3bd21c08a1129092e2a62f3f7929a953 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Mon, 18 Apr 2011 14:42:14 +0200 Subject: [PATCH 22/57] test fails on more platforms, removed @freebsd from default.experimental. --- mysql-test/collections/default.experimental | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 72e14135ef0..9e74fa9bc30 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -26,7 +26,7 @@ ndb.* # joro : NDB tests marked as experiment rpl.rpl_innodb_bug28430 @solaris # Bug#46029 rpl.rpl_row_sp011 @solaris # Joro : Bug #45445 -rpl.rpl_stop_slave @freebsd # Sven : BUG#12345981 +rpl.rpl_stop_slave # Sven : BUG#12345981 rpl.rpl_bug37426 # WL#5867: skozlov: test case moved from unused bugs suite From cb0aa450b802b79abf76a880758e53084e7c8830 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 18 Apr 2011 15:29:26 +0200 Subject: [PATCH 23/57] Bug #12365486 MTR FAILS TO FIND WARNINGS IN SERVER LOG WITH --VALGRIND COMBINED WITH --DEBUG With this combination, outoput was directed to .trace but not all parts of MTR was aware of this. Replace .err with .trace at the earliest possible place --- mysql-test/lib/My/ConfigFactory.pm | 8 ++++++-- mysql-test/mysql-test-run.pl | 9 +-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index bb990a9f8d2..7688283fdc1 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -141,7 +141,11 @@ sub fix_tmpdir { sub fix_log_error { my ($self, $config, $group_name, $group)= @_; my $dir= $self->{ARGS}->{vardir}; - return "$dir/log/$group_name.err"; + if ( $::opt_valgrind and $::opt_debug ) { + return "$dir/log/$group_name.trace"; + } else { + return "$dir/log/$group_name.err"; + } } sub fix_log { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2897ae3142a..1c7efccc69d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -256,7 +256,7 @@ my $opt_strace_client; our $opt_user = "root"; -my $opt_valgrind= 0; +our $opt_valgrind= 0; my $opt_valgrind_mysqld= 0; my $opt_valgrind_mysqltest= 0; my @default_valgrind_args= ("--show-reachable=yes"); @@ -4544,13 +4544,6 @@ sub mysqld_start ($$) { unlink($mysqld->value('pid-file')); my $output= $mysqld->value('#log-error'); - if ( $opt_valgrind and $opt_debug ) - { - # When both --valgrind and --debug is selected, send - # all output to the trace file, making it possible to - # see the exact location where valgrind complains - $output= "$opt_vardir/log/".$mysqld->name().".trace"; - } # Remember this log file for valgrind error report search $mysqld_logs{$output}= 1 if $opt_valgrind; # Remember data dir for gmon.out files if using gprof From c346b0a483c3e7970dc62b26eddc1be3f2c62b66 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 18 Apr 2011 15:35:14 +0200 Subject: [PATCH 24/57] Bug #12365486 MTR FAILS TO FIND WARNINGS IN SERVER LOG WITH --VALGRIND COMBINED WITH --DEBUG With this combination, outoput was directed to .trace but not all parts of MTR was aware of this. Replace .err with .trace at the earliest possible place --- mysql-test/lib/My/ConfigFactory.pm | 8 ++++++-- mysql-test/mysql-test-run.pl | 9 +-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index bb990a9f8d2..7688283fdc1 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -1,5 +1,5 @@ # -*- cperl -*- -# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public @@ -141,7 +141,11 @@ sub fix_tmpdir { sub fix_log_error { my ($self, $config, $group_name, $group)= @_; my $dir= $self->{ARGS}->{vardir}; - return "$dir/log/$group_name.err"; + if ( $::opt_valgrind and $::opt_debug ) { + return "$dir/log/$group_name.trace"; + } else { + return "$dir/log/$group_name.err"; + } } sub fix_log { diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 2897ae3142a..1c7efccc69d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -256,7 +256,7 @@ my $opt_strace_client; our $opt_user = "root"; -my $opt_valgrind= 0; +our $opt_valgrind= 0; my $opt_valgrind_mysqld= 0; my $opt_valgrind_mysqltest= 0; my @default_valgrind_args= ("--show-reachable=yes"); @@ -4544,13 +4544,6 @@ sub mysqld_start ($$) { unlink($mysqld->value('pid-file')); my $output= $mysqld->value('#log-error'); - if ( $opt_valgrind and $opt_debug ) - { - # When both --valgrind and --debug is selected, send - # all output to the trace file, making it possible to - # see the exact location where valgrind complains - $output= "$opt_vardir/log/".$mysqld->name().".trace"; - } # Remember this log file for valgrind error report search $mysqld_logs{$output}= 1 if $opt_valgrind; # Remember data dir for gmon.out files if using gprof From 410ee93cdea97dbabb114c231fc430aee5f62059 Mon Sep 17 00:00:00 2001 From: Serge Kozlov Date: Mon, 18 Apr 2011 23:59:15 +0400 Subject: [PATCH 25/57] BUG#12371924 Update test case --- mysql-test/collections/default.experimental | 5 +---- mysql-test/suite/binlog/r/binlog_bug23533.result | 3 +++ mysql-test/suite/binlog/t/binlog_bug23533.test | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index 9e74fa9bc30..fb8c6845a5f 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -2,8 +2,7 @@ # in alphabetical order. This also helps with merge conflict resolution. binlog.binlog_multi_engine # joro : NDB tests marked as experimental as agreed with bochklin -binlog.binlog_bug23533 # WL#5867: skozlov: test case moved from unused bugs suite -binlog.binlog_bug36391 # WL#5867: skozlov: test case moved from unused bugs suite +binlog.binlog_bug23533 # skozlov: BUG#12371924 funcs_1.charset_collation_1 # depends on compile-time decisions @@ -27,8 +26,6 @@ ndb.* # joro : NDB tests marked as experiment rpl.rpl_innodb_bug28430 @solaris # Bug#46029 rpl.rpl_row_sp011 @solaris # Joro : Bug #45445 rpl.rpl_stop_slave # Sven : BUG#12345981 -rpl.rpl_bug37426 # WL#5867: skozlov: test case moved from unused bugs suite - rpl_ndb.* # joro : NDB tests marked as experimental as agreed with bochklin rpl_ndb.rpl_ndb_log # Bug#38998 diff --git a/mysql-test/suite/binlog/r/binlog_bug23533.result b/mysql-test/suite/binlog/r/binlog_bug23533.result index 07b124793d1..02605839ab0 100644 --- a/mysql-test/suite/binlog/r/binlog_bug23533.result +++ b/mysql-test/suite/binlog/r/binlog_bug23533.result @@ -3,7 +3,9 @@ CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b TEXT, PRIMARY KEY(a)) ENGINE=I SELECT COUNT(*) FROM t1; COUNT(*) 1000 +SET @saved_binlog_cache_size=@@binlog_cache_size; SET @saved_max_binlog_cache_size=@@max_binlog_cache_size; +SET GLOBAL binlog_cache_size=4096; SET GLOBAL max_binlog_cache_size=4096; START TRANSACTION; CREATE TABLE t2 SELECT * FROM t1; @@ -13,4 +15,5 @@ SHOW TABLES LIKE 't%'; Tables_in_test (t%) t1 SET GLOBAL max_binlog_cache_size=@saved_max_binlog_cache_size; +SET GLOBAL binlog_cache_size=@saved_binlog_cache_size; DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/binlog_bug23533.test b/mysql-test/suite/binlog/t/binlog_bug23533.test index fb2fc808b7b..05fe9fd9523 100644 --- a/mysql-test/suite/binlog/t/binlog_bug23533.test +++ b/mysql-test/suite/binlog/t/binlog_bug23533.test @@ -15,14 +15,18 @@ CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b TEXT, PRIMARY KEY(a)) ENGINE=I let $i= 1000; while ($i) { + BEGIN; eval INSERT INTO t1 VALUES($i, REPEAT('x', 4096)); + COMMIT; dec $i; } --enable_query_log SELECT COUNT(*) FROM t1; # Set small value for max_binlog_cache_size +SET @saved_binlog_cache_size=@@binlog_cache_size; SET @saved_max_binlog_cache_size=@@max_binlog_cache_size; +SET GLOBAL binlog_cache_size=4096; SET GLOBAL max_binlog_cache_size=4096; # Copied data from t1 into t2 large than max_binlog_cache_size @@ -34,4 +38,5 @@ SHOW TABLES LIKE 't%'; # 5.1 End of Test SET GLOBAL max_binlog_cache_size=@saved_max_binlog_cache_size; +SET GLOBAL binlog_cache_size=@saved_binlog_cache_size; DROP TABLE t1; From 02dc75a2e17940377570f91b6a47ab5e818d03d6 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 19 Apr 2011 14:01:32 +0200 Subject: [PATCH 26/57] Bug #12373916 MTR SHOULD FLAG PRESENCE OF VALGRIND MEMORY LEAK REPORTS IN A CLEARER WAY Produce a pseudo test valgrind_reports. Reports are generated by each worker, don't want one test for each. Send message 'VALGREP' if report was found, master thread will pick this up --- mysql-test/mysql-test-run.pl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 4800d4718f1..da07d49c24f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -280,6 +280,7 @@ my $opt_valgrind_mysqltest= 0; my @default_valgrind_args= ("--show-reachable=yes"); my @valgrind_args; my $opt_valgrind_path; +my $valgrind_reports= 0; my $opt_callgrind; my %mysqld_logs; my $opt_debug_sync_timeout= 300; # Default timeout for WAIT_FOR actions. @@ -504,6 +505,25 @@ sub main { push @$completed, run_ctest() if $opt_ctest; + if ($opt_valgrind) { + # Create minimalistic "test" for the reporting + my $tinfo = My::Test->new + ( + name => 'valgrind_report', + ); + # Set dummy worker id to align report with normal tests + $tinfo->{worker} = 0 if $opt_parallel > 1; + if ($valgrind_reports) { + $tinfo->{result}= 'MTR_RES_FAILED'; + $tinfo->{comment}= "Valgrind reported failures at shutdown, see above"; + $tinfo->{failures}= 1; + } else { + $tinfo->{result}= 'MTR_RES_PASSED'; + } + mtr_report_test($tinfo); + push @$completed, $tinfo; + } + mtr_print_line(); if ( $opt_gcov ) { @@ -704,6 +724,9 @@ sub run_test_server ($$$) { elsif ($line =~ /^SPENT/) { add_total_times($line); } + elsif ($line eq 'VALGREP' && $opt_valgrind) { + $valgrind_reports= 1; + } else { mtr_error("Unknown response: '$line' from client"); } @@ -889,6 +912,7 @@ sub run_worker ($) { my $valgrind_reports= 0; if ($opt_valgrind_mysqld) { $valgrind_reports= valgrind_exit_reports(); + print $server "VALGREP\n" if $valgrind_reports; } if ( $opt_gprof ) { gprof_collect (find_mysqld($basedir), keys %gprof_dirs); From 3af2c9a30f8b5d3da07a308493add5ceb41596db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Apr 2011 10:10:54 +0300 Subject: [PATCH 27/57] Remove a debug printout that is no longer needed. --- storage/innobase/ibuf/ibuf0ibuf.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/storage/innobase/ibuf/ibuf0ibuf.c b/storage/innobase/ibuf/ibuf0ibuf.c index 8110bccc162..7d94ebc6438 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.c +++ b/storage/innobase/ibuf/ibuf0ibuf.c @@ -1179,18 +1179,7 @@ ibuf_page_low( ibuf_bitmap_page_no_calc(zip_size, page_no), RW_NO_LATCH, NULL, BUF_GET_NO_LATCH, file, line, &local_mtr)); -# ifdef UNIV_SYNC_DEBUG - /* This is for tracking Bug #58212. This check and message can - be removed once it has been established that our assumptions - about this condition are correct. The bug was only a one-time - occurrence, unable to repeat since then. */ - void* latch = sync_thread_levels_contains(SYNC_IBUF_BITMAP); - if (latch) { - fprintf(stderr, "Bug#58212 UNIV_SYNC_DEBUG" - " levels %p (%u,%u)\n", - latch, (unsigned) space, (unsigned) page_no); - } -# endif /* UNIV_SYNC_DEBUG */ + ret = ibuf_bitmap_page_get_bits_low( bitmap_page, page_no, zip_size, MTR_MEMO_BUF_FIX, &local_mtr, IBUF_BITMAP_IBUF); From 71bb332aa3f9e87baa055434e126a87f8ec70f29 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 20 Apr 2011 11:39:20 +0400 Subject: [PATCH 28/57] Bug#11765923 58937: MANY VALGRIND ERRORS AFTER GROUPING BY RESULT OF DECIMAL COLUMN FUNCTION Bug#11764671 57533: UNINITIALISED VALUES IN COPY_AND_CONVERT (SQL_STRING.CC) WITH CERTAIN CHA When ROUND evaluates decimal result it uses Item::decimal value as fraction value for the result. In some cases Item::decimal is greater than real result fraction value and uninitialised memory of result(decimal) buffer can be used in further calculations. Issue is introduced by Bug33143 fix. The fix is to remove erroneous assignment. --- mysql-test/r/func_math.result | 22 ++++++++++++++++++++++ mysql-test/t/func_math.test | 16 ++++++++++++++++ sql/item_func.cc | 3 --- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index ad0b872145b..b9118feab1a 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -518,4 +518,26 @@ CREATE TABLE t1 SELECT CEIL(LINESTRINGFROMWKB(1) DIV NULL); DROP TABLE t1; CREATE TABLE t1 SELECT FLOOR(LINESTRINGFROMWKB(1) DIV NULL); DROP TABLE t1; +# +# Bug#11765923 58937: MANY VALGRIND ERRORS AFTER GROUPING BY RESULT OF DECIMAL COLUMN FUNCTION +# +CREATE TABLE t1(f1 DECIMAL(22,1)); +INSERT INTO t1 VALUES (0),(1); +SELECT ROUND(f1, f1) FROM t1; +ROUND(f1, f1) +0.0 +1.0 +SELECT ROUND(f1, f1) FROM t1 GROUP BY 1; +ROUND(f1, f1) +0.0 +1.0 +DROP TABLE t1; +# +# Bug#11764671 57533: UNINITIALISED VALUES IN COPY_AND_CONVERT (SQL_STRING.CC) WITH CERTAIN CHA +# +SELECT ROUND(LEAST(15, -4939092, 0.2704), STDDEV('a')); +ROUND(LEAST(15, -4939092, 0.2704), STDDEV('a')) +-4939092.0000 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'a' End of 5.1 tests diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 64b6a3a4ea6..9d51a5c94f9 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -333,4 +333,20 @@ DROP TABLE t1; CREATE TABLE t1 SELECT FLOOR(LINESTRINGFROMWKB(1) DIV NULL); DROP TABLE t1; +--echo # +--echo # Bug#11765923 58937: MANY VALGRIND ERRORS AFTER GROUPING BY RESULT OF DECIMAL COLUMN FUNCTION +--echo # + +CREATE TABLE t1(f1 DECIMAL(22,1)); +INSERT INTO t1 VALUES (0),(1); +SELECT ROUND(f1, f1) FROM t1; +SELECT ROUND(f1, f1) FROM t1 GROUP BY 1; +DROP TABLE t1; + +--echo # +--echo # Bug#11764671 57533: UNINITIALISED VALUES IN COPY_AND_CONVERT (SQL_STRING.CC) WITH CERTAIN CHA +--echo # + +SELECT ROUND(LEAST(15, -4939092, 0.2704), STDDEV('a')); + --echo End of 5.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 595629b51be..6a9c47954b7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2122,10 +2122,7 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) if (!(null_value= (args[0]->null_value || args[1]->null_value || my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec, truncate, decimal_value) > 1))) - { - decimal_value->frac= decimals; return decimal_value; - } return 0; } From dcb5aa6627839e6e8d9f0aa568720943be9b1213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Apr 2011 11:29:10 +0300 Subject: [PATCH 29/57] Clarify a comment. --- storage/innobase/btr/btr0cur.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/btr/btr0cur.c b/storage/innobase/btr/btr0cur.c index c4034d0896a..9fa8fc663c9 100644 --- a/storage/innobase/btr/btr0cur.c +++ b/storage/innobase/btr/btr0cur.c @@ -2429,8 +2429,8 @@ make_external: record on its page? */ was_first = page_cur_is_before_first(page_cursor); - /* The first parameter means that no lock checking and undo logging - is made in the insert */ + /* Lock checks and undo logging were already performed by + btr_cur_upd_lock_and_undo(). */ err = btr_cur_pessimistic_insert(BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG From 73ecffdb2fae16b1831f436ced30231740d7c673 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 20 Apr 2011 11:32:28 +0200 Subject: [PATCH 30/57] BUG#12377872 ASSERTION FAILED: !_ENTERED WHEN GETHOSTBYADDR_R FAILS ON SOLARIS This assertion was triggered if gethostbyaddr_r cannot do a reverse lookup on an ip address. The reason was a missing DBUG_RETURN macro. The problem affected only debug versions of the server. This patch fixes the problem by replacing return with DBUG_RETURN. No test case added. --- sql/hostname.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/hostname.cc b/sql/hostname.cc index c8cf46383a9..9796755e9fb 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2006 MySQL AB +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -183,7 +183,7 @@ char * ip_to_hostname(struct in_addr *in, uint *errors) &tmp_hostent,buff,sizeof(buff),&tmp_errno))) { DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno)); - return 0; + DBUG_RETURN(0); } if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2), &tmp_errno))) From 5cc7844b5c821d7ffe536309dce37ed13ed5c8f0 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 20 Apr 2011 14:58:53 +0200 Subject: [PATCH 31/57] Bug #12379923 60907: MYSQL-TEST/LIB/MY/SAFEPROCESS/SAFE_PROCESS.PL USES HARDCODED SIGNAL NUMBE Replaced the hardcoded 9 with 'KILL' --- mysql-test/lib/My/SafeProcess/safe_process.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess/safe_process.pl b/mysql-test/lib/My/SafeProcess/safe_process.pl index e3114a749d3..54b0073f8df 100644 --- a/mysql-test/lib/My/SafeProcess/safe_process.pl +++ b/mysql-test/lib/My/SafeProcess/safe_process.pl @@ -94,7 +94,7 @@ eval { local $SIG{INT}= \&handle_signal; local $SIG{CHLD}= sub { message("Got signal @_"); - kill(9, -$child_pid); + kill('KILL', -$child_pid); my $ret= waitpid($child_pid, 0); if ($? & 127){ exit(65); # Killed by signal @@ -134,7 +134,7 @@ if ( $@ ) { # Use negative pid in order to kill the whole # process group # -my $ret= kill(9, -$child_pid); +my $ret= kill('KILL', -$child_pid); message("Killed child: $child_pid, ret: $ret"); if ($ret > 0) { message("Killed child: $child_pid"); From f6641998bec64b18abf75df94ff2dad8cd3ed603 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Wed, 20 Apr 2011 17:52:33 +0200 Subject: [PATCH 32/57] Bug#11766249 bug#59316: PARTITIONING AND INDEX_MERGE MEMORY LEAK Update for previous patch according to reviewers comments. Updated the constructors for ha_partitions to use the common init_handler_variables functions Added use of defines for size and offset to get better readability for the code that reads and writes the .par file. Also refactored the get_from_handler_file function. --- sql/ha_partition.cc | 310 +++++++++++++++++++++++++------------------- sql/ha_partition.h | 17 ++- sql/handler.cc | 23 ++-- 3 files changed, 209 insertions(+), 141 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 946ecc652ef..7685b3a8384 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -166,11 +166,6 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(table)"); - m_part_info= NULL; - m_create_handler= FALSE; - m_is_sub_partitioned= 0; - m_is_clone_of= NULL; - m_clone_mem_root= NULL; init_handler_variables(); DBUG_VOID_RETURN; } @@ -192,21 +187,21 @@ ha_partition::ha_partition(handlerton *hton, partition_info *part_info) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); DBUG_ASSERT(part_info); + init_handler_variables(); m_part_info= part_info; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); - init_handler_variables(); DBUG_VOID_RETURN; } /** ha_partition constructor method used by ha_partition::clone() - @param hton Handlerton (partition_hton) - @param share Table share object - @param part_info_arg partition_info to use - @param clone_arg ha_partition to clone - @param clme_mem_root_arg MEM_ROOT to use + @param hton Handlerton (partition_hton) + @param share Table share object + @param part_info_arg partition_info to use + @param clone_arg ha_partition to clone + @param clme_mem_root_arg MEM_ROOT to use @return New partition handler */ @@ -218,14 +213,12 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(clone)"); + init_handler_variables(); m_part_info= part_info_arg; m_create_handler= TRUE; m_is_sub_partitioned= m_part_info->is_sub_partitioned(); m_is_clone_of= clone_arg; m_clone_mem_root= clone_mem_root_arg; - init_handler_variables(); - m_tot_parts= clone_arg->m_tot_parts; - DBUG_ASSERT(m_tot_parts); DBUG_VOID_RETURN; } @@ -286,6 +279,11 @@ void ha_partition::init_handler_variables() this allows blackhole to work properly */ m_no_locks= 0; + m_part_info= NULL; + m_create_handler= FALSE; + m_is_sub_partitioned= 0; + m_is_clone_of= NULL; + m_clone_mem_root= NULL; #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; @@ -2099,18 +2097,16 @@ static uint name_add(char *dest, const char *first_name, const char *sec_name) } -/* +/** Create the special .par file - SYNOPSIS - create_handler_file() - name Full path of table name + @param name Full path of table name - RETURN VALUE - >0 Error code - 0 Success + @return Operation status + @retval FALSE Error code + @retval TRUE Success - DESCRIPTION + @note Method used to create handler file with names of partitions, their engine types and the number of partitions. */ @@ -2174,19 +2170,22 @@ bool ha_partition::create_handler_file(const char *name) Array of engine types n * 4 bytes where n = (m_tot_parts + 3)/4 Length of name part in bytes 4 bytes + (Names in filename format) Name part m * 4 bytes where m = ((length_name_part + 3)/4)*4 All padding bytes are zeroed */ - tot_partition_words= (tot_parts + 3) / 4; - tot_name_words= (tot_name_len + 3) / 4; + tot_partition_words= (tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + tot_name_words= (tot_name_len + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + /* 4 static words (tot words, checksum, tot partitions, name length) */ tot_len_words= 4 + tot_partition_words + tot_name_words; - tot_len_byte= 4 * tot_len_words; + tot_len_byte= PAR_WORD_SIZE * tot_len_words; if (!(file_buffer= (uchar *) my_malloc(tot_len_byte, MYF(MY_ZEROFILL)))) DBUG_RETURN(TRUE); - engine_array= (file_buffer + 12); - name_buffer_ptr= (char*) (file_buffer + ((4 + tot_partition_words) * 4)); + engine_array= (file_buffer + PAR_ENGINES_OFFSET); + name_buffer_ptr= (char*) (engine_array + tot_partition_words * PAR_WORD_SIZE + + PAR_WORD_SIZE); part_it.rewind(); for (i= 0; i < no_parts; i++) { @@ -2224,13 +2223,15 @@ bool ha_partition::create_handler_file(const char *name) } chksum= 0; int4store(file_buffer, tot_len_words); - int4store(file_buffer + 8, tot_parts); - int4store(file_buffer + 12 + (tot_partition_words * 4), tot_name_len); + int4store(file_buffer + PAR_NUM_PARTS_OFFSET, tot_parts); + int4store(file_buffer + PAR_ENGINES_OFFSET + + (tot_partition_words * PAR_WORD_SIZE), + tot_name_len); for (i= 0; i < tot_len_words; i++) - chksum^= uint4korr(file_buffer + 4 * i); - int4store(file_buffer + 4, chksum); + chksum^= uint4korr(file_buffer + PAR_WORD_SIZE * i); + int4store(file_buffer + PAR_CHECKSUM_OFFSET, chksum); /* - Remove .frm extension and replace with .par + Add .par extension to the file name. Create and write and close file to be used at open, delete_table and rename_table */ @@ -2248,14 +2249,9 @@ bool ha_partition::create_handler_file(const char *name) DBUG_RETURN(result); } -/* + +/** Clear handler variables and free some memory - - SYNOPSIS - clear_handler_file() - - RETURN VALUE - NONE */ void ha_partition::clear_handler_file() @@ -2268,16 +2264,15 @@ void ha_partition::clear_handler_file() m_engine_array= NULL; } -/* + +/** Create underlying handler objects - SYNOPSIS - create_handlers() - mem_root Allocate memory through this + @para mem_root Allocate memory through this - RETURN VALUE - TRUE Error - FALSE Success + @return Operation status + @retval TRUE Error + @retval FALSE Success */ bool ha_partition::create_handlers(MEM_ROOT *mem_root) @@ -2315,6 +2310,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root) DBUG_RETURN(FALSE); } + /* Create underlying handler objects from partition info @@ -2386,108 +2382,164 @@ error_end: } -/* - Get info about partition engines and their names from the .par file +/** + Read the .par file to get the partitions engines and names - SYNOPSIS - get_from_handler_file() - name Full path of table name - mem_root Allocate memory through this + @param name Name of table file (without extention) - RETURN VALUE - TRUE Error - FALSE Success + @return Operation status + @retval true Failure + @retval false Success - DESCRIPTION - Open handler file to get partition names, engine types and number of - partitions. + @note On success, m_file_buffer is allocated and must be + freed by the caller. m_name_buffer_ptr and m_tot_parts is also set. */ -bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root, - bool clone) +bool ha_partition::read_par_file(const char *name) { - char buff[FN_REFLEN], *address_tot_name_len; + char buff[FN_REFLEN], *tot_name_len_offset; File file; - char *file_buffer, *name_buffer_ptr; - handlerton **engine_array; + char *file_buffer; uint i, len_bytes, len_words, tot_partition_words, tot_name_words, chksum; - DBUG_ENTER("ha_partition::get_from_handler_file"); + DBUG_ENTER("ha_partition::read_par_file"); DBUG_PRINT("enter", ("table name: '%s'", name)); if (m_file_buffer) - DBUG_RETURN(FALSE); + DBUG_RETURN(false); fn_format(buff, name, "", ha_par_ext, MY_APPEND_EXT); /* Following could be done with my_stat to read in whole file */ if ((file= my_open(buff, O_RDONLY | O_SHARE, MYF(0))) < 0) - DBUG_RETURN(TRUE); - if (my_read(file, (uchar *) & buff[0], 8, MYF(MY_NABP))) + DBUG_RETURN(true); + if (my_read(file, (uchar *) & buff[0], PAR_WORD_SIZE, MYF(MY_NABP))) goto err1; len_words= uint4korr(buff); - len_bytes= 4 * len_words; + len_bytes= PAR_WORD_SIZE * len_words; + if (my_seek(file, 0, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR) + goto err1; if (!(file_buffer= (char*) my_malloc(len_bytes, MYF(0)))) goto err1; - VOID(my_seek(file, 0, MY_SEEK_SET, MYF(0))); if (my_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP))) goto err2; chksum= 0; for (i= 0; i < len_words; i++) - chksum ^= uint4korr((file_buffer) + 4 * i); + chksum ^= uint4korr((file_buffer) + PAR_WORD_SIZE * i); if (chksum) goto err2; - m_tot_parts= uint4korr((file_buffer) + 8); + m_tot_parts= uint4korr((file_buffer) + PAR_NUM_PARTS_OFFSET); DBUG_PRINT("info", ("No of parts = %u", m_tot_parts)); - tot_partition_words= (m_tot_parts + 3) / 4; - if (!clone) - { - engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); - for (i= 0; i < m_tot_parts; i++) - { - engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), - (enum legacy_db_type) - *(uchar *) ((file_buffer) + - 12 + i)); - if (!engine_array[i]) - goto err3; - } - } - address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words; - tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4; + tot_partition_words= (m_tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + + tot_name_len_offset= file_buffer + PAR_ENGINES_OFFSET + + PAR_WORD_SIZE * tot_partition_words; + tot_name_words= (uint4korr(tot_name_len_offset) + PAR_WORD_SIZE - 1) / + PAR_WORD_SIZE; + /* + Verify the total length = tot size word, checksum word, num parts word + + engines array + name length word + name array. + */ if (len_words != (tot_partition_words + tot_name_words + 4)) - goto err3; - name_buffer_ptr= file_buffer + 16 + 4 * tot_partition_words; + goto err2; VOID(my_close(file, MYF(0))); m_file_buffer= file_buffer; // Will be freed in clear_handler_file() - m_name_buffer_ptr= name_buffer_ptr; - - if (!clone) - { - if (!(m_engine_array= (plugin_ref*) - my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME)))) - goto err3; + m_name_buffer_ptr= tot_name_len_offset + PAR_WORD_SIZE; - for (i= 0; i < m_tot_parts; i++) - m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]); + DBUG_RETURN(false); - my_afree((gptr) engine_array); - } - - if (!clone && !m_file && create_handlers(mem_root)) - { - clear_handler_file(); - DBUG_RETURN(TRUE); - } - DBUG_RETURN(FALSE); - -err3: - if (!clone) - my_afree((gptr) engine_array); err2: my_free(file_buffer, MYF(0)); err1: VOID(my_close(file, MYF(0))); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); +} + + +/** + Setup m_engine_array + + @param mem_root MEM_ROOT to use for allocating new handlers + + @return Operation status + @retval false Success + @retval true Failure +*/ + +bool ha_partition::setup_engine_array(MEM_ROOT *mem_root) +{ + uint i; + uchar *buff; + handlerton **engine_array; + + DBUG_ASSERT(!m_file); + DBUG_ENTER("ha_partition::setup_engine_array"); + engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); + if (!engine_array) + DBUG_RETURN(true); + + buff= (uchar *) (m_file_buffer + PAR_ENGINES_OFFSET); + for (i= 0; i < m_tot_parts; i++) + { + engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), + (enum legacy_db_type) + *(buff + i)); + if (!engine_array[i]) + goto err; + } + if (!(m_engine_array= (plugin_ref*) + my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME)))) + goto err; + + for (i= 0; i < m_tot_parts; i++) + m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]); + + my_afree((gptr) engine_array); + + if (create_handlers(mem_root)) + { + clear_handler_file(); + DBUG_RETURN(true); + } + + DBUG_RETURN(false); + +err: + my_afree((gptr) engine_array); + DBUG_RETURN(true); +} + + +/** + Get info about partition engines and their names from the .par file + + @param name Full path of table name + @param mem_root Allocate memory through this + @param is_clone If it is a clone, don't create new handlers + + @return Operation status + @retval true Error + @retval false Success + + @note Open handler file to get partition names, engine types and number of + partitions. +*/ + +bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root, + bool is_clone) +{ + DBUG_ENTER("ha_partition::get_from_handler_file"); + DBUG_PRINT("enter", ("table name: '%s'", name)); + + if (m_file_buffer) + DBUG_RETURN(false); + + if (read_par_file(name)) + DBUG_RETURN(true); + + if (!is_clone && setup_engine_array(mem_root)) + DBUG_RETURN(true); + + DBUG_RETURN(false); } @@ -2615,8 +2667,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, FALSE); - if (!(m_file[i]= file[i]->clone((const char*) name_buff, - m_clone_mem_root))) + if (!(m_file[i]= file[i]->clone(name_buff, m_clone_mem_root))) { error= HA_ERR_INITIALIZATION; file= &m_file[i]; @@ -2632,8 +2683,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, FALSE); - if ((error= (*file)->ha_open(table, (const char*) name_buff, mode, - test_if_locked))) + if ((error= (*file)->ha_open(table, name_buff, mode, test_if_locked))) goto err_handler; m_no_locks+= (*file)->lock_count(); name_buffer_ptr+= strlen(name_buffer_ptr) + 1; @@ -2645,8 +2695,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) check_table_flags= (((*file)->ha_table_flags() & ~(PARTITION_DISABLED_TABLE_FLAGS)) | (PARTITION_ENABLED_TABLE_FLAGS)); - file++; - do + while (*(++file)) { DBUG_ASSERT(ref_length >= (*file)->ref_length); set_if_bigger(ref_length, ((*file)->ref_length)); @@ -2663,7 +2712,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) file = &m_file[m_tot_parts - 1]; goto err_handler; } - } while (*(++file)); + } key_used_on_scan= m_file[0]->key_used_on_scan; implicit_emptied= m_file[0]->implicit_emptied; /* @@ -2742,7 +2791,7 @@ err_alloc: /** Clone the open and locked partitioning handler. - @param mem_root MEM_ROOT to use. + @param mem_root MEM_ROOT to use. @return Pointer to the successfully created clone or NULL @@ -2750,33 +2799,32 @@ err_alloc: This function creates a new ha_partition handler as a clone/copy. The original (this) must already be opened and locked. The clone will use the originals m_part_info. - It also allocates memory to ref + ref_dup. + It also allocates memory for ref + ref_dup. In ha_partition::open() it will clone its original handlers partitions - which will allocate then om the correct MEM_ROOT and also open them. + which will allocate then on the correct MEM_ROOT and also open them. */ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) { ha_partition *new_handler; - + DBUG_ENTER("ha_partition::clone"); new_handler= new (mem_root) ha_partition(ht, table_share, m_part_info, this, mem_root); - if (!new_handler) - DBUG_RETURN(NULL); - /* Allocate new_handler->ref here because otherwise ha_open will allocate it on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ - new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(m_ref_length)*2); - if (!new_handler->ref) - DBUG_RETURN(NULL); + if (new_handler && + !(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(m_ref_length)*2))) + new_handler= NULL; - if (new_handler->ha_open(table, name, + if (new_handler && + new_handler->ha_open(table, name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) - DBUG_RETURN(NULL); + new_handler= NULL; DBUG_RETURN((handler*) new_handler); } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index a38d56af8ff..cd90c4cc1d5 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -55,6 +55,16 @@ typedef struct st_ha_data_partition HA_DUPLICATE_POS | \ HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED) + +/* First 4 bytes in the .par file is the number of 32-bit words in the file */ +#define PAR_WORD_SIZE 4 +/* offset to the .par file checksum */ +#define PAR_CHECKSUM_OFFSET 4 +/* offset to the total number of partitions */ +#define PAR_NUM_PARTS_OFFSET 8 +/* offset to the engines array */ +#define PAR_ENGINES_OFFSET 12 + class ha_partition :public handler { private: @@ -71,7 +81,7 @@ private: /* Data for the partition handler */ int m_mode; // Open mode uint m_open_test_lock; // Open test_if_locked - char *m_file_buffer; // Buffer with names + char *m_file_buffer; // Content of the .par file char *m_name_buffer_ptr; // Pointer to first partition name plugin_ref *m_engine_array; // Array of types of the handlers handler **m_file; // Array of references to handler inst. @@ -281,7 +291,10 @@ private: And one method to read it in. */ bool create_handler_file(const char *name); - bool get_from_handler_file(const char *name, MEM_ROOT *mem_root, bool clone); + bool setup_engine_array(MEM_ROOT *mem_root); + bool read_par_file(const char *name); + bool get_from_handler_file(const char *name, MEM_ROOT *mem_root, + bool is_clone); bool new_handlers_from_part_info(MEM_ROOT *mem_root); bool create_handlers(MEM_ROOT *mem_root); void clear_handler_file(); diff --git a/sql/handler.cc b/sql/handler.cc index 8adb8e061a3..718529fa5fc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2045,14 +2045,21 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ - if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) - return NULL; - if (new_handler && !new_handler->ha_open(table, - name, - table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) - return new_handler; - return NULL; + if (new_handler && + !(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + new_handler= NULL; + /* + TODO: Implement a more efficient way to have more than one index open for + the same table instance. The ha_open call is not cachable for clone. + */ + if (new_handler && new_handler->ha_open(table, + name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + new_handler= NULL; + + return new_handler; } From 4923b866b865cb2d58f55e2d38ba73262a2ee2d7 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 21 Apr 2011 10:48:30 +0300 Subject: [PATCH 33/57] Backport a fix for innodb_bug59641 from mysql-trunk-innodb --- mysql-test/suite/innodb/t/innodb_bug59641.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/suite/innodb/t/innodb_bug59641.test b/mysql-test/suite/innodb/t/innodb_bug59641.test index 0237673061c..b933abd1d14 100644 --- a/mysql-test/suite/innodb/t/innodb_bug59641.test +++ b/mysql-test/suite/innodb/t/innodb_bug59641.test @@ -28,6 +28,8 @@ UPDATE t SET b=4*a WHERE a=32; XA END '789'; XA PREPARE '789'; +CONNECT (con3,localhost,root,,); +CONNECTION con3; # The server would issue this warning on restart. call mtr.add_suppression("Found 3 prepared XA transactions"); From 76f37a023563755c840c3d51c31aa0a5a6b3996e Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 22 Apr 2011 11:20:55 +0400 Subject: [PATCH 34/57] Bug#11756928 48916: SERVER INCORRECTLY PROCESSING HAVING CLAUSES WITH AN ORDER BY CLAUSE Before sorting HAVING condition is split into two parts, first part is a table related condition and the rest of is HAVING part. Extraction of HAVING part does not take into account the fact that some of conditions might be non-const but have 'used_tables' == 0 (independent subqueries) and because of that these conditions are cut off by make_cond_for_table() function. The fix is to use (table_map) 0 instead of used_tables in third argument for make_cond_for_table() function. It allows to extract elements which belong to sorted table and in addition elements which are independend subqueries. --- mysql-test/r/having.result | 22 ++++++++++++++++++++++ mysql-test/t/having.test | 26 ++++++++++++++++++++++++++ sql/sql_select.cc | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index cd1b4ae0218..4253ac7e5c3 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -545,4 +545,26 @@ FROM t1 JOIN t2 ON t2.f2 LIKE 'x' HAVING field1 < 7; field1 DROP TABLE t1,t2; +# +# Bug#48916 Server incorrectly processing HAVING clauses with an ORDER BY clause +# +CREATE TABLE t1 (f1 INT, f2 INT); +INSERT INTO t1 VALUES (1, 0), (2, 1), (3, 2); +CREATE TABLE t2 (f1 INT, f2 INT); +SELECT t1.f1 +FROM t1 +HAVING (3, 2) IN (SELECT f1, f2 FROM t2) AND t1.f1 >= 0 +ORDER BY t1.f1; +f1 +SELECT t1.f1 +FROM t1 +HAVING (3, 2) IN (SELECT 4, 2) AND t1.f1 >= 0 +ORDER BY t1.f1; +f1 +SELECT t1.f1 +FROM t1 +HAVING 2 IN (SELECT f2 FROM t2) AND t1.f1 >= 0 +ORDER BY t1.f1; +f1 +DROP TABLE t1,t2; End of 5.1 tests diff --git a/mysql-test/t/having.test b/mysql-test/t/having.test index c808e747523..2ed8b40b858 100644 --- a/mysql-test/t/having.test +++ b/mysql-test/t/having.test @@ -564,4 +564,30 @@ HAVING field1 < 7; DROP TABLE t1,t2; +--echo # +--echo # Bug#48916 Server incorrectly processing HAVING clauses with an ORDER BY clause +--echo # + +CREATE TABLE t1 (f1 INT, f2 INT); +INSERT INTO t1 VALUES (1, 0), (2, 1), (3, 2); +CREATE TABLE t2 (f1 INT, f2 INT); + +SELECT t1.f1 +FROM t1 +HAVING (3, 2) IN (SELECT f1, f2 FROM t2) AND t1.f1 >= 0 +ORDER BY t1.f1; + +SELECT t1.f1 +FROM t1 +HAVING (3, 2) IN (SELECT 4, 2) AND t1.f1 >= 0 +ORDER BY t1.f1; + +SELECT t1.f1 +FROM t1 +HAVING 2 IN (SELECT f2 FROM t2) AND t1.f1 >= 0 +ORDER BY t1.f1; + +DROP TABLE t1,t2; + + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ab287e57aa1..46e9ad242b3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2215,7 +2215,7 @@ JOIN::exec() Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having, used_tables, - used_tables); + (table_map) 0); if (sort_table_cond) { if (!curr_table->select) @@ -12852,6 +12852,42 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) return 0; // keep test } +/** + Extract a condition that can be checked after reading given table + + @param cond Condition to analyze + @param tables Tables for which "current field values" are available + @param used_table Table that we're extracting the condition for (may + also include PSEUDO_TABLE_BITS, and may be zero) + @param exclude_expensive_cond Do not push expensive conditions + + @retval <>NULL Generated condition + @retval =NULL Already checked, OR error + + @details + Extract the condition that can be checked after reading the table + specified in 'used_table', given that current-field values for tables + specified in 'tables' bitmap are available. + If 'used_table' is 0 + - extract conditions for all tables in 'tables'. + - extract conditions are unrelated to any tables + in the same query block/level(i.e. conditions + which have used_tables == 0). + + The function assumes that + - Constant parts of the condition has already been checked. + - Condition that could be checked for tables in 'tables' has already + been checked. + + The function takes into account that some parts of the condition are + guaranteed to be true by employed 'ref' access methods (the code that + does this is located at the end, search down for "EQ_FUNC"). + + @note + Make sure to keep the implementations of make_cond_for_table() and + make_cond_after_sjm() synchronized. + make_cond_for_info_schema() uses similar algorithm as well. +*/ static COND * make_cond_for_table(COND *cond, table_map tables, table_map used_table) From e7079a323573583c06235a9cb20a66bc1682532e Mon Sep 17 00:00:00 2001 From: Serge Kozlov Date: Mon, 25 Apr 2011 23:49:56 +0400 Subject: [PATCH 35/57] BUG#12371924. Fxi test case --- mysql-test/suite/binlog/r/binlog_bug23533.result | 4 ---- mysql-test/suite/binlog/t/binlog_bug23533.test | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_bug23533.result b/mysql-test/suite/binlog/r/binlog_bug23533.result index 02605839ab0..d5cd93284a2 100644 --- a/mysql-test/suite/binlog/r/binlog_bug23533.result +++ b/mysql-test/suite/binlog/r/binlog_bug23533.result @@ -3,8 +3,6 @@ CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b TEXT, PRIMARY KEY(a)) ENGINE=I SELECT COUNT(*) FROM t1; COUNT(*) 1000 -SET @saved_binlog_cache_size=@@binlog_cache_size; -SET @saved_max_binlog_cache_size=@@max_binlog_cache_size; SET GLOBAL binlog_cache_size=4096; SET GLOBAL max_binlog_cache_size=4096; START TRANSACTION; @@ -14,6 +12,4 @@ COMMIT; SHOW TABLES LIKE 't%'; Tables_in_test (t%) t1 -SET GLOBAL max_binlog_cache_size=@saved_max_binlog_cache_size; -SET GLOBAL binlog_cache_size=@saved_binlog_cache_size; DROP TABLE t1; diff --git a/mysql-test/suite/binlog/t/binlog_bug23533.test b/mysql-test/suite/binlog/t/binlog_bug23533.test index 05fe9fd9523..c05abe788c6 100644 --- a/mysql-test/suite/binlog/t/binlog_bug23533.test +++ b/mysql-test/suite/binlog/t/binlog_bug23533.test @@ -24,11 +24,15 @@ while ($i) SELECT COUNT(*) FROM t1; # Set small value for max_binlog_cache_size -SET @saved_binlog_cache_size=@@binlog_cache_size; -SET @saved_max_binlog_cache_size=@@max_binlog_cache_size; +let $saved_binlog_cache_size= query_get_value(SELECT @@binlog_cache_size AS Value, Value, 1); +let $saved_max_binlog_cache_size= query_get_value(SELECT @@max_binlog_cache_size AS Value, Value, 1); SET GLOBAL binlog_cache_size=4096; SET GLOBAL max_binlog_cache_size=4096; +# New value of max_binlog_cache_size will apply to new session +disconnect default; +connect(default,localhost,root,,test); + # Copied data from t1 into t2 large than max_binlog_cache_size START TRANSACTION; --error 1197 @@ -37,6 +41,10 @@ COMMIT; SHOW TABLES LIKE 't%'; # 5.1 End of Test -SET GLOBAL max_binlog_cache_size=@saved_max_binlog_cache_size; -SET GLOBAL binlog_cache_size=@saved_binlog_cache_size; +--disable_query_log +eval SET GLOBAL max_binlog_cache_size=$saved_max_binlog_cache_size; +eval SET GLOBAL binlog_cache_size=$saved_binlog_cache_size; +--enable_query_log DROP TABLE t1; +disconnect default; +connect(default,localhost,root,,test); From 0ef0c541af1f323bd33982ab97e75f06eb9d4e61 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Tue, 26 Apr 2011 10:21:09 +0200 Subject: [PATCH 36/57] post fix for werror build for bug#11766249. --- sql/ha_partition.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index a0f346f7a64..460d5826a91 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2587,7 +2587,7 @@ void ha_data_partition_destroy(void *ha_data) int ha_partition::open(const char *name, int mode, uint test_if_locked) { char *name_buffer_ptr; - int error; + int error= HA_ERR_INITIALIZATION; uint alloc_len; handler **file; char name_buff[FN_REFLEN]; @@ -2601,7 +2601,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_open_test_lock= test_if_locked; m_part_field_array= m_part_info->full_part_field_array; if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of))) - DBUG_RETURN(1); + DBUG_RETURN(error); name_buffer_ptr= m_name_buffer_ptr; m_start_key.length= 0; m_rec0= table->record[0]; @@ -2612,7 +2612,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { if (!(m_ordered_rec_buffer= (uchar*)my_malloc(alloc_len, MYF(MY_WME)))) { - DBUG_RETURN(1); + DBUG_RETURN(error); } { /* @@ -2635,7 +2635,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) - DBUG_RETURN(1); + DBUG_RETURN(error); bitmap_clear_all(&m_bulk_insert_started); /* Initialize the bitmap we use to determine what partitions are used */ if (!m_is_clone_of) @@ -2644,7 +2644,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) { bitmap_free(&m_bulk_insert_started); - DBUG_RETURN(1); + DBUG_RETURN(error); } bitmap_set_all(&(m_part_info->used_partitions)); } From a3f7f01da66cc7d8be6e04184ef1f2c157f813f8 Mon Sep 17 00:00:00 2001 From: Guilhem Bichot Date: Tue, 26 Apr 2011 11:18:29 +0200 Subject: [PATCH 37/57] Fix for Bug#11892055 - "GCC COMPILER FLAG -WOVERLOADED-VIRTUAL NOT USED, WHICH LETS BUGS IN" --- cmake/maintainer.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake index 468b2f40084..d24211a6ff8 100644 --- a/cmake/maintainer.cmake +++ b/cmake/maintainer.cmake @@ -35,7 +35,7 @@ ENDMACRO() # Setup G++ (GNU C++ compiler) warning options. MACRO(SET_MYSQL_MAINTAINER_GNU_CXX_OPTIONS) SET(MY_MAINTAINER_CXX_WARNINGS - "${MY_MAINTAINER_WARNINGS} -Wno-unused-parameter" + "${MY_MAINTAINER_WARNINGS} -Wno-unused-parameter -Woverloaded-virtual" CACHE STRING "C++ warning options used in maintainer builds.") ENDMACRO() From 6593ca560b9867d185743d2b522672c8f973c139 Mon Sep 17 00:00:00 2001 From: "kevin.lewis@oracle.com" <> Date: Tue, 26 Apr 2011 12:55:52 -0500 Subject: [PATCH 38/57] Bug#60309 - Bug#12356829: MYSQL 5.5.9 FOR MAC OSX HAS BUG WITH FOREIGN KEY CONSTRAINTS The innoDB global variable srv_lower_case_table_names is set to the value of lower_case_table_names declared in mysqld.h server in ha_innodb.cc. Since this variable can change at runtime, it is reset for each handler call to ::create, ::open, ::rename_table & ::delete_table. But it is possible for tables to be implicitly opened before an explicit handler call is made when an engine is first started or restarted. I was able to reproduce that with the testcase in this patch on a version of InnoDB from 2 weeks ago. It seemed like the change buffer entries for the secondary key was getting put into pages after the restart. (But I am not sure, I did not write down the call stack while it was reproducing.) In the current code, the implicit open, which is actually a call to dict_load_foreigns(), does not occur with this testcase. The change is to replace srv_lower_case_table_names by an interface function in innodb.cc that retrieves the server global variable when it is needed. --- .../suite/innodb/r/innodb_bug60196.result | 44 ++++++++++++ .../suite/innodb/t/innodb_bug60196.test | 70 +++++++++++++++++++ storage/innobase/dict/dict0dict.c | 5 +- storage/innobase/dict/dict0load.c | 2 +- storage/innobase/dict/dict0mem.c | 17 +++-- storage/innobase/handler/ha_innodb.cc | 21 ++++-- storage/innobase/include/dict0mem.h | 10 ++- storage/innobase/include/ha_prototypes.h | 11 +++ storage/innobase/include/srv0srv.h | 3 - storage/innobase/srv/srv0srv.c | 6 -- 10 files changed, 157 insertions(+), 32 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb_bug60196.result b/mysql-test/suite/innodb/r/innodb_bug60196.result index 85707f81a28..411950b49dd 100755 --- a/mysql-test/suite/innodb/r/innodb_bug60196.result +++ b/mysql-test/suite/innodb/r/innodb_bug60196.result @@ -71,3 +71,47 @@ FK1_Key FK2_Key DROP TABLE Bug_60196; DROP TABLE Bug_60196_FK1; DROP TABLE Bug_60196_FK2; +CREATE TABLE Bug_60309_FK ( +ID INT PRIMARY KEY, +ID2 INT, +KEY K2(ID2) +) ENGINE=InnoDB; +CREATE TABLE Bug_60309 ( +ID INT PRIMARY KEY, +FK_ID INT, +KEY (FK_ID), +CONSTRAINT FK FOREIGN KEY (FK_ID) REFERENCES Bug_60309_FK (ID) +) ENGINE=InnoDB; +INSERT INTO Bug_60309_FK (ID, ID2) VALUES (1, 1), (2, 2), (3, 3); +INSERT INTO Bug_60309 VALUES (1, 1); +INSERT INTO Bug_60309 VALUES (2, 99); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`bug_60309`, CONSTRAINT `FK` FOREIGN KEY (`FK_ID`) REFERENCES `Bug_60309_FK` (`ID`)) +SELECT * FROM Bug_60309_FK; +ID ID2 +1 1 +2 2 +3 3 +SELECT * FROM Bug_60309; +ID FK_ID +1 1 +# Stop server +# Restart server. +# +# Try to insert more to the example table with foreign keys. +# Bug60309 causes the foreign key file not to be found after +# the resstart above. +# +SELECT * FROM Bug_60309; +ID FK_ID +1 1 +INSERT INTO Bug_60309 VALUES (2, 2); +INSERT INTO Bug_60309 VALUES (3, 3); +SELECT * FROM Bug_60309; +ID FK_ID +1 1 +2 2 +3 3 + +# Clean up. +DROP TABLE Bug_60309; +DROP TABLE Bug_60309_FK; diff --git a/mysql-test/suite/innodb/t/innodb_bug60196.test b/mysql-test/suite/innodb/t/innodb_bug60196.test index 47c646a5a75..ea85653f1af 100755 --- a/mysql-test/suite/innodb/t/innodb_bug60196.test +++ b/mysql-test/suite/innodb/t/innodb_bug60196.test @@ -85,3 +85,73 @@ DROP TABLE Bug_60196; DROP TABLE Bug_60196_FK1; DROP TABLE Bug_60196_FK2; + +# Bug#60309/12356829 +# MYSQL 5.5.9 FOR MAC OSX HAS BUG WITH FOREIGN KEY CONSTRAINTS +# This testcase is different from that for Bug#60196 in that the +# referenced table contains a secondary key. When the engine is +# restarted, the referenced table is opened by the purge thread, +# which does not notice that lower_case_table_names == 2. + +# +# Create test data. +# +CREATE TABLE Bug_60309_FK ( + ID INT PRIMARY KEY, + ID2 INT, + KEY K2(ID2) +) ENGINE=InnoDB; +CREATE TABLE Bug_60309 ( + ID INT PRIMARY KEY, + FK_ID INT, + KEY (FK_ID), + CONSTRAINT FK FOREIGN KEY (FK_ID) REFERENCES Bug_60309_FK (ID) +) ENGINE=InnoDB; + +INSERT INTO Bug_60309_FK (ID, ID2) VALUES (1, 1), (2, 2), (3, 3); +INSERT INTO Bug_60309 VALUES (1, 1); +--error ER_NO_REFERENCED_ROW_2 +INSERT INTO Bug_60309 VALUES (2, 99); + +SELECT * FROM Bug_60309_FK; +SELECT * FROM Bug_60309; + +--echo # Stop server + +# Write file to make mysql-test-run.pl wait for the server to stop +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +# Send a shutdown request to the server +-- shutdown_server 10 + +# Call script that will poll the server waiting for it to disapear +-- source include/wait_until_disconnected.inc + +--echo # Restart server. + +# Write file to make mysql-test-run.pl start up the server again +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +# Turn on reconnect +--enable_reconnect + +# Call script that will poll the server waiting for it to be back online again +--source include/wait_until_connected_again.inc + +# Turn off reconnect again +--disable_reconnect + +--echo # +--echo # Try to insert more to the example table with foreign keys. +--echo # Bug60309 causes the foreign key file not to be found after +--echo # the resstart above. +--echo # +SELECT * FROM Bug_60309; +INSERT INTO Bug_60309 VALUES (2, 2); +INSERT INTO Bug_60309 VALUES (3, 3); +SELECT * FROM Bug_60309; + +--echo +--echo # Clean up. +DROP TABLE Bug_60309; +DROP TABLE Bug_60309_FK; diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index ebc7747640e..df9db4d7428 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -52,7 +52,6 @@ UNIV_INTERN dict_index_t* dict_ind_compact; #include "que0que.h" #include "rem0cmp.h" #include "row0merge.h" -#include "srv0srv.h" /* srv_lower_case_table_names */ #include "m_ctype.h" /* my_isspace() */ #include "ha_prototypes.h" /* innobase_strcasecmp(), innobase_casedn_str()*/ @@ -3029,14 +3028,14 @@ dict_scan_table_name( /* Values; 0 = Store and compare as given; case sensitive 1 = Store and compare in lower; case insensitive 2 = Store as given, compare in lower; case semi-sensitive */ - if (srv_lower_case_table_names == 2) { + if (innobase_get_lower_case_table_names() == 2) { innobase_casedn_str(ref); *table = dict_table_get_low(ref); memcpy(ref, database_name, database_name_len); ref[database_name_len] = '/'; memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); } else { - if (srv_lower_case_table_names == 1) { + if (innobase_get_lower_case_table_names() == 1) { innobase_casedn_str(ref); } *table = dict_table_get_low(ref); diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index 37bf4a1ad59..aad3145f7a4 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -2262,7 +2262,7 @@ loop: may not be the same case, but the previous comparison showed that they match with no-case. */ - if ((srv_lower_case_table_names != 2) + if ((innobase_get_lower_case_table_names() != 2) && (0 != ut_memcmp(field, table_name, len))) { goto next_rec; } diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c index a442a3811d8..8785dfb57ed 100644 --- a/storage/innobase/dict/dict0mem.c +++ b/storage/innobase/dict/dict0mem.c @@ -33,7 +33,6 @@ Created 1/8/1996 Heikki Tuuri #include "data0type.h" #include "mach0data.h" #include "dict0dict.h" -#include "srv0srv.h" /* srv_lower_case_table_names */ #include "ha_prototypes.h" /* innobase_casedn_str()*/ #ifndef UNIV_HOTBACKUP # include "lock0lock.h" @@ -294,9 +293,9 @@ dict_mem_foreign_create(void) /**********************************************************************//** Sets the foreign_table_name_lookup pointer based on the value of -srv_lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup -will point to foreign_table_name. If 2, then another string is allocated -of the heap and set to lower case. */ +lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup +will point to foreign_table_name. If 2, then another string is +allocated from foreign->heap and set to lower case. */ UNIV_INTERN void dict_mem_foreign_table_name_lookup_set( @@ -304,7 +303,7 @@ dict_mem_foreign_table_name_lookup_set( dict_foreign_t* foreign, /*!< in/out: foreign struct */ ibool do_alloc) /*!< in: is an alloc needed */ { - if (srv_lower_case_table_names == 2) { + if (innobase_get_lower_case_table_names() == 2) { if (do_alloc) { foreign->foreign_table_name_lookup = mem_heap_alloc( foreign->heap, @@ -321,9 +320,9 @@ dict_mem_foreign_table_name_lookup_set( /**********************************************************************//** Sets the referenced_table_name_lookup pointer based on the value of -srv_lower_case_table_names. If that is 0 or 1, -referenced_table_name_lookup will point to referenced_table_name. If 2, -then another string is allocated of the heap and set to lower case. */ +lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup +will point to referenced_table_name. If 2, then another string is +allocated from foreign->heap and set to lower case. */ UNIV_INTERN void dict_mem_referenced_table_name_lookup_set( @@ -331,7 +330,7 @@ dict_mem_referenced_table_name_lookup_set( dict_foreign_t* foreign, /*!< in/out: foreign struct */ ibool do_alloc) /*!< in: is an alloc needed */ { - if (srv_lower_case_table_names == 2) { + if (innobase_get_lower_case_table_names() == 2) { if (do_alloc) { foreign->referenced_table_name_lookup = mem_heap_alloc( foreign->heap, diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 27bf0754d36..8efa0523927 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1199,6 +1199,20 @@ innobase_get_stmt( return(stmt->str); } +/**********************************************************************//** +Get the current setting of the lower_case_table_names global parameter from +mysqld.cc. We do a dirty read because for one there is no synchronization +object and secondly there is little harm in doing so even if we get a torn +read. +@return value of lower_case_table_names */ +extern "C" UNIV_INTERN +ulint +innobase_get_lower_case_table_names(void) +/*=====================================*/ +{ + return(lower_case_table_names); +} + #if defined (__WIN__) && defined (MYSQL_DYNAMIC_PLUGIN) extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list; /*******************************************************************//** @@ -3671,7 +3685,6 @@ ha_innobase::open( UT_NOT_USED(test_if_locked); thd = ha_thd(); - srv_lower_case_table_names = lower_case_table_names; /* Under some cases MySQL seems to call this function while holding btr_search_latch. This breaks the latching order as @@ -6362,8 +6375,6 @@ err_col: col_len); } - srv_lower_case_table_names = lower_case_table_names; - error = row_create_table_for_mysql(table, trx); if (error == DB_DUPLICATE_KEY) { @@ -7223,8 +7234,6 @@ ha_innobase::delete_table( /* Drop the table in InnoDB */ - srv_lower_case_table_names = lower_case_table_names; - error = row_drop_table_for_mysql(norm_name, trx, thd_sql_command(thd) == SQLCOM_DROP_DB); @@ -7354,8 +7363,6 @@ innobase_rename_table( row_mysql_lock_data_dictionary(trx); } - srv_lower_case_table_names = lower_case_table_names; - error = row_rename_table_for_mysql( norm_from, norm_to, trx, lock_and_commit); diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 75d3b2c3302..51c9c2d1797 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -240,7 +240,9 @@ dict_mem_foreign_create(void); /**********************************************************************//** Sets the foreign_table_name_lookup pointer based on the value of -srv_lower_case_table_names. */ +lower_case_table_names. If that is 0 or 1, foreign_table_name_lookup +will point to foreign_table_name. If 2, then another string is +allocated from the heap and set to lower case. */ UNIV_INTERN void dict_mem_foreign_table_name_lookup_set( @@ -249,8 +251,10 @@ dict_mem_foreign_table_name_lookup_set( ibool do_alloc); /*!< in: is an alloc needed */ /**********************************************************************//** -Sets the reference_table_name_lookup pointer based on the value of -srv_lower_case_table_names. */ +Sets the referenced_table_name_lookup pointer based on the value of +lower_case_table_names. If that is 0 or 1, referenced_table_name_lookup +will point to referenced_table_name. If 2, then another string is +allocated from the heap and set to lower case. */ UNIV_INTERN void dict_mem_referenced_table_name_lookup_set( diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index dd9e8db82ee..edf7a1a28c1 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -285,4 +285,15 @@ thd_set_lock_wait_time( void* thd, /*!< in: thread handle (THD*) */ ulint value); /*!< in: time waited for the lock */ +/**********************************************************************//** +Get the current setting of the lower_case_table_names global parameter from +mysqld.cc. We do a dirty read because for one there is no synchronization +object and secondly there is little harm in doing so even if we get a torn +read. +@return value of lower_case_table_names */ +UNIV_INTERN +ulint +innobase_get_lower_case_table_names(void); +/*=====================================*/ + #endif diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index fb5bc56920a..fc9401a12f5 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -71,9 +71,6 @@ at a time */ #define SRV_AUTO_EXTEND_INCREMENT \ (srv_auto_extend_increment * ((1024 * 1024) / UNIV_PAGE_SIZE)) -/* This is set to the MySQL server value for this variable. */ -extern uint srv_lower_case_table_names; - /* Mutex for locking srv_monitor_file */ extern mutex_t srv_monitor_file_mutex; /* Temporary file for innodb monitor output */ diff --git a/storage/innobase/srv/srv0srv.c b/storage/innobase/srv/srv0srv.c index 789fe3bf36a..23e690b1105 100644 --- a/storage/innobase/srv/srv0srv.c +++ b/storage/innobase/srv/srv0srv.c @@ -86,12 +86,6 @@ Created 10/8/1995 Heikki Tuuri #include "mysql/plugin.h" #include "mysql/service_thd_wait.h" -/* This is set to the MySQL server value for this variable. It is only -needed for FOREIGN KEY definition parsing since FOREIGN KEY names are not -stored in the server metadata. The server stores and enforces it for -regular database and table names.*/ -UNIV_INTERN uint srv_lower_case_table_names = 0; - /* The following counter is incremented whenever there is some user activity in the server */ UNIV_INTERN ulint srv_activity_count = 0; From 735bab72c51e0ff4ccf5fb688826e87931ee8fe2 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 27 Apr 2011 11:35:57 +0400 Subject: [PATCH 39/57] Bug#11889186 60503: CRASH IN MAKE_DATE_TIME WITH DATE_FORMAT / STR_TO_DATE COMBINATION calc_daynr() function returns negative result if malformed date with zero year and month is used. Attempt to calculate week day on negative value leads to crash. The fix is return NULL for 'W', 'a', 'w' specifiers if zero year and month is used. Additional fix for calc_daynr(): --added assertion that result can not be negative --return 0 if zero year and month is used --- mysql-test/r/func_time.result | 12 ++++++++++++ mysql-test/t/func_time.test | 8 ++++++++ sql-common/my_time.c | 3 ++- sql/item_timefunc.cc | 6 +++--- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index f67171af99f..1e05443d8ac 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -1405,4 +1405,16 @@ NULL SELECT ADDDATE(MONTH(FROM_UNIXTIME(NULL)),INTERVAL 1 HOUR); ADDDATE(MONTH(FROM_UNIXTIME(NULL)),INTERVAL 1 HOUR) NULL +# +# Bug#11889186 60503: CRASH IN MAKE_DATE_TIME WITH DATE_FORMAT / STR_TO_DATE COMBINATION +# +SELECT DATE_FORMAT('0000-00-11', '%W'); +DATE_FORMAT('0000-00-11', '%W') +NULL +SELECT DATE_FORMAT('0000-00-11', '%a'); +DATE_FORMAT('0000-00-11', '%a') +NULL +SELECT DATE_FORMAT('0000-00-11', '%w'); +DATE_FORMAT('0000-00-11', '%w') +NULL End of 5.1 tests diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 938359f8c11..2000d81f80d 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -913,4 +913,12 @@ SELECT CAST((MONTH(FROM_UNIXTIME(@@GLOBAL.SQL_MODE))) AS BINARY(1025)); SELECT ADDDATE(MONTH(FROM_UNIXTIME(NULL)),INTERVAL 1 HOUR); +--echo # +--echo # Bug#11889186 60503: CRASH IN MAKE_DATE_TIME WITH DATE_FORMAT / STR_TO_DATE COMBINATION +--echo # + +SELECT DATE_FORMAT('0000-00-11', '%W'); +SELECT DATE_FORMAT('0000-00-11', '%a'); +SELECT DATE_FORMAT('0000-00-11', '%w'); + --echo End of 5.1 tests diff --git a/sql-common/my_time.c b/sql-common/my_time.c index ca11c54a999..80a7e0daa2c 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -772,7 +772,7 @@ long calc_daynr(uint year,uint month,uint day) int y= year; /* may be < 0 temporarily */ DBUG_ENTER("calc_daynr"); - if (y == 0 && month == 0 && day == 0) + if (y == 0 && month == 0) DBUG_RETURN(0); /* Skip errors */ /* Cast to int to be able to handle month == 0 */ delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day); @@ -783,6 +783,7 @@ long calc_daynr(uint year,uint month,uint day) temp=(int) ((y/100+1)*3)/4; DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld", y+(month <= 2),month,day,delsum+y/4-temp)); + DBUG_ASSERT(delsum+(int) y/4-temp > 0); DBUG_RETURN(delsum+(int) y/4-temp); } /* calc_daynr */ diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index ecf790cc061..1044b4682ef 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -648,7 +648,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, system_charset_info); break; case 'W': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday= calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0); @@ -657,7 +657,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, system_charset_info); break; case 'a': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0); @@ -816,7 +816,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, } break; case 'w': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),1); From a5bd30015637c999dfa83a0324b2b6c8f6f787dd Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 27 Apr 2011 09:45:23 +0200 Subject: [PATCH 40/57] Bug #12373916 MTR SHOULD FLAG PRESENCE OF VALGRIND MEMORY LEAK REPORTS IN A CLEARER WAY Fix: it only worked if some worker had valgrind report from its last test Flag has to be set both places where report is printed --- mysql-test/mysql-test-run.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index da07d49c24f..c288a16d233 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -5677,6 +5677,7 @@ sub valgrind_exit_reports() { @culprits); mtr_print_line(); print ("$valgrind_rep\n"); + $found_err= 1; $err_in_report= 0; } # Make ready to collect new report From 767074e8b1726d02b71598691581db565b0b7678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Wed, 27 Apr 2011 11:02:34 +0200 Subject: [PATCH 41/57] BUG#47741 rpl_ndb_extraCol fails in next-mr (mysql-5.1-rep+2) in RBR - fix the fix to properly detect when engine is NDB and also don't drop the table t9 if it hasn't been created --- mysql-test/extra/rpl_tests/rpl_extra_col_slave.test | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_extra_col_slave.test b/mysql-test/extra/rpl_tests/rpl_extra_col_slave.test index 482c7e44922..119e081878c 100644 --- a/mysql-test/extra/rpl_tests/rpl_extra_col_slave.test +++ b/mysql-test/extra/rpl_tests/rpl_extra_col_slave.test @@ -396,7 +396,7 @@ sync_slave_with_master; # Error reaction is up to sql_mode of the slave sql (bug#38173) #--echo *** Create t9 on slave *** # Please, check BUG#47741 to see why you are not testing NDB. -if ($engine_type != NDB) +if (`SELECT UPPER(LEFT($engine_type, 3)) != 'NDB'`) { STOP SLAVE; RESET SLAVE; @@ -440,12 +440,13 @@ if ($engine_type != NDB) #--let $slave_skip_counter= 2 #--let $show_slave_sql_error= 1 #--source include/wait_for_slave_sql_error_and_skip.inc -} -#--echo *** Drop t9 *** -connection master; -DROP TABLE t9; -sync_slave_with_master; + #--echo *** Drop t9 *** + connection master; + DROP TABLE t9; + sync_slave_with_master; + +} ############################################ # More columns in slave at middle of table # From c08c4e1fd765fff8ae76b2e61b0e04f550228660 Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Wed, 27 Apr 2011 17:24:10 +0530 Subject: [PATCH 42/57] BUG#12329909 - BUILDING MYSQL WITH DEBUG SUPPORT FAILS WITH LIBEDIT Fixed by checking the return value of the write() function calls and handling the open files and fd appropriately. --- cmd-line-utils/libedit/vi.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd-line-utils/libedit/vi.c b/cmd-line-utils/libedit/vi.c index d628f076a1d..beffc7b40b5 100644 --- a/cmd-line-utils/libedit/vi.c +++ b/cmd-line-utils/libedit/vi.c @@ -1012,8 +1012,10 @@ vi_histedit(EditLine *el, int c __attribute__((__unused__))) if (fd < 0) return CC_ERROR; cp = el->el_line.buffer; - write(fd, cp, el->el_line.lastchar - cp +0u); - write(fd, "\n", 1); + if (write(fd, cp, el->el_line.lastchar - cp +0u) == -1) + goto error; + if (write(fd, "\n", 1) == -1) + goto error; pid = fork(); switch (pid) { case -1: @@ -1041,6 +1043,12 @@ vi_histedit(EditLine *el, int c __attribute__((__unused__))) unlink(tempfile); /* return CC_REFRESH; */ return ed_newline(el, 0); + +/* XXXMYSQL: Avoid compiler warnings. */ +error: + close(fd); + unlink(tempfile); + return CC_ERROR; } /* vi_history_word(): From 5759b05f78b0edefa349abb4486e1ca1e077442a Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Wed, 27 Apr 2011 14:57:45 +0200 Subject: [PATCH 43/57] Marked test experimental because it fails due to BUG#12403008 --- mysql-test/collections/default.experimental | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index c8f0053eda3..c741e4a869c 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -15,6 +15,7 @@ main.sp @solaris # Bug#47791 2010-01-20 alik Several tes main.type_float @freebsd # Bug#38965 2010-05-04 alik test cases gis-rtree, type_float, type_newdecimal fail in embedded server main.wait_timeout @solaris # Bug#51244 2010-04-26 alik wait_timeout fails on OpenSolaris +rpl.rpl_heartbeat_basic # BUG#12403008 2011-04-27 sven fails sporadically rpl.rpl_innodb_bug28430 # Bug#46029 sys_vars.max_sp_recursion_depth_func @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun From aefbea7d0b62c0ddfb3f9a6b46c6154e472a2688 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Wed, 27 Apr 2011 17:51:06 +0200 Subject: [PATCH 44/57] Post push fix for bug#11766249 bug#59316 Partitions can have different ref_length (position data length). Removed DBUG_ASSERT which crashed debug builds when using MAX_ROWS on some partitions. --- ...on_not_embedded.result => partition_myisam.result} | 9 +++++++++ ...tition_not_embedded.test => partition_myisam.test} | 11 ++++++++++- sql/ha_partition.cc | 9 ++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) rename mysql-test/r/{partition_not_embedded.result => partition_myisam.result} (87%) rename mysql-test/t/{partition_not_embedded.test => partition_myisam.test} (85%) diff --git a/mysql-test/r/partition_not_embedded.result b/mysql-test/r/partition_myisam.result similarity index 87% rename from mysql-test/r/partition_not_embedded.result rename to mysql-test/r/partition_myisam.result index c942189a956..1995c87eff2 100644 --- a/mysql-test/r/partition_not_embedded.result +++ b/mysql-test/r/partition_myisam.result @@ -79,3 +79,12 @@ a DROP TABLE t1; # Should not be any files left here # End of bug#30102 test. +# Test of post-push fix for bug#11766249/59316 +CREATE TABLE t1 (a INT, b VARCHAR(255), PRIMARY KEY (a)) +ENGINE = MyISAM +PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (0) MAX_ROWS=100, +PARTITION p1 VALUES LESS THAN (100) MAX_ROWS=100, +PARTITION pMax VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (1, "Partition p1, first row"); +DROP TABLE t1; diff --git a/mysql-test/t/partition_not_embedded.test b/mysql-test/t/partition_myisam.test similarity index 85% rename from mysql-test/t/partition_not_embedded.test rename to mysql-test/t/partition_myisam.test index 5c512085a9e..51f46aa71be 100644 --- a/mysql-test/t/partition_not_embedded.test +++ b/mysql-test/t/partition_myisam.test @@ -1,5 +1,4 @@ -- source include/have_partition.inc --- source include/not_embedded.inc --disable_warnings DROP TABLE IF EXISTS t1, t2; --enable_warnings @@ -51,3 +50,13 @@ DROP TABLE t1; --list_files $MYSQLD_DATADIR/test t1* --list_files $MYSQLD_DATADIR/test t2* --echo # End of bug#30102 test. + +--echo # Test of post-push fix for bug#11766249/59316 +CREATE TABLE t1 (a INT, b VARCHAR(255), PRIMARY KEY (a)) +ENGINE = MyISAM +PARTITION BY RANGE (a) +(PARTITION p0 VALUES LESS THAN (0) MAX_ROWS=100, + PARTITION p1 VALUES LESS THAN (100) MAX_ROWS=100, + PARTITION pMax VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (1, "Partition p1, first row"); +DROP TABLE t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 460d5826a91..4883e0a0571 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2697,7 +2697,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) (PARTITION_ENABLED_TABLE_FLAGS)); while (*(++file)) { - DBUG_ASSERT(ref_length >= (*file)->ref_length); + /* MyISAM can have smaller ref_length for partitions with MAX_ROWS set */ set_if_bigger(ref_length, ((*file)->ref_length)); /* Verify that all partitions have the same set of table flags. @@ -3957,12 +3957,15 @@ end_dont_reset_start_part: void ha_partition::position(const uchar *record) { handler *file= m_file[m_last_part]; + uint pad_length; DBUG_ENTER("ha_partition::position"); file->position(record); int2store(ref, m_last_part); - memcpy((ref + PARTITION_BYTES_IN_POS), file->ref, - (ref_length - PARTITION_BYTES_IN_POS)); + memcpy((ref + PARTITION_BYTES_IN_POS), file->ref, file->ref_length); + pad_length= m_ref_length - PARTITION_BYTES_IN_POS - file->ref_length; + if (pad_length) + memset((ref + PARTITION_BYTES_IN_POS + file->ref_length), 0, pad_length); #ifdef SUPPORTING_PARTITION_OVER_DIFFERENT_ENGINES #ifdef HAVE_purify From f575a86a965324df71147adc823e53afb34ab64d Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 28 Apr 2011 13:08:05 +0300 Subject: [PATCH 45/57] Fixed the ABI check files after the push of bug # 12325444. --- include/mysql/client_plugin.h.pp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/mysql/client_plugin.h.pp b/include/mysql/client_plugin.h.pp index e508f821aad..93eaff7501e 100644 --- a/include/mysql/client_plugin.h.pp +++ b/include/mysql/client_plugin.h.pp @@ -35,6 +35,5 @@ mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type); struct st_mysql_client_plugin * mysql_client_register_plugin(struct st_mysql *mysql, struct st_mysql_client_plugin *plugin); -int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin, - const char *option, - const void *value); +int mysql_plugin_options(struct st_mysql_client_plugin *plugin, + const char *option, const void *value); From 843cee8a30341a7152b523b24559df35e2de31bf Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 28 Apr 2011 13:13:36 +0300 Subject: [PATCH 46/57] Backport of (part of) bug #11760838 to 5.5. Enabled the ABI check to run on MacOSX. --- cmake/abi_check.cmake | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmake/abi_check.cmake b/cmake/abi_check.cmake index 2488bcefe33..a671aeff342 100644 --- a/cmake/abi_check.cmake +++ b/cmake/abi_check.cmake @@ -19,8 +19,16 @@ # plugin_audit.h and plugin_ftparser.h. # # We use gcc specific preprocessing command and sed/diff, so it will -# only be run on Unix and only if gcc is used. -IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_SYSTEM_NAME MATCHES "Linux") +# only be run on Unix and only if gcc is used. On some Unixes, +# (Solaris) sed or diff might act differently from GNU, so we run only +# on systems we can trust. +IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(RUN_ABI_CHECK 1) +ELSE() + SET(RUN_ABI_CHECK 0) +ENDIF() + +IF(CMAKE_COMPILER_IS_GNUCC AND RUN_ABI_CHECK) IF(CMAKE_C_COMPILER MATCHES "ccache$") SET(COMPILER ${CMAKE_C_COMPILER_ARG1}) STRING(REGEX REPLACE "^ " "" COMPILER ${COMPILER}) From 6d7aceeead15eeefe720a2cbb1fe8f955ceb40fd Mon Sep 17 00:00:00 2001 From: Rafal Somla Date: Thu, 28 Apr 2011 21:17:29 +0200 Subject: [PATCH 47/57] Bug#11766631 (59780) - Move the client authentication_windows plugin into the server repository This patch adds client windows authentication plugin code to the client library libmysql (only on Windows platform). The plugin is compiled into the library and added to the list of built-in plugins. This way clients should be able to connect to a server which uses windows authentication plugin even as an SQL user which uses such authentication. Note: this makes the client library to depend on Secur32 Windows system library. When building clients, they must be linked against Secur32. Command mysql_config --libs correctly lists Secur32 as a required dependency. --- libmysql/CMakeLists.txt | 12 +- libmysql/authentication_win/CMakeLists.txt | 31 ++ libmysql/authentication_win/common.cc | 492 ++++++++++++++++++ libmysql/authentication_win/common.h | 290 +++++++++++ libmysql/authentication_win/handshake.cc | 288 ++++++++++ libmysql/authentication_win/handshake.h | 168 ++++++ .../authentication_win/handshake_client.cc | 285 ++++++++++ libmysql/authentication_win/log_client.cc | 55 ++ libmysql/authentication_win/plugin_client.cc | 58 +++ sql-common/client.c | 7 + 10 files changed, 1685 insertions(+), 1 deletion(-) create mode 100644 libmysql/authentication_win/CMakeLists.txt create mode 100644 libmysql/authentication_win/common.cc create mode 100644 libmysql/authentication_win/common.h create mode 100644 libmysql/authentication_win/handshake.cc create mode 100644 libmysql/authentication_win/handshake.h create mode 100644 libmysql/authentication_win/handshake_client.cc create mode 100644 libmysql/authentication_win/log_client.cc create mode 100644 libmysql/authentication_win/plugin_client.cc diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index d7426c465d8..d0e383c6640 100644 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -134,6 +134,12 @@ CACHE INTERNAL "Functions exported by client API" ) +IF(WIN32) + ADD_SUBDIRECTORY(authentication_win) + SET(WITH_AUTHENTICATION_WIN 1) + ADD_DEFINITIONS(-DAUTHENTICATION_WIN) +ENDIF(WIN32) + SET(CLIENT_SOURCES get_password.c libmysql.c @@ -151,6 +157,10 @@ ADD_DEPENDENCIES(clientlib GenError) SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) +IF(WITH_AUTHENTICATION_WIN) + LIST(APPEND LIBS auth_win_client) +ENDIF(WITH_AUTHENTICATION_WIN) + # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS} COMPONENT Development) diff --git a/libmysql/authentication_win/CMakeLists.txt b/libmysql/authentication_win/CMakeLists.txt new file mode 100644 index 00000000000..82c9dffb06e --- /dev/null +++ b/libmysql/authentication_win/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# Configuration for building Windows Authentication Plugin (client-side) +# + +ADD_DEFINITIONS(-DSECURITY_WIN32) +ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds + +SET(HEADERS common.h handshake.h) +SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc) + +ADD_CONVENIENCE_LIBRARY(auth_win_client ${PLUGIN_SOURCES} ${HEADERS}) +TARGET_LINK_LIBRARIES(auth_win_client Secur32) + +# In IDE, group headers in a separate folder. + +SOURCE_GROUP(Headers REGULAR_EXPRESSION ".*h$") diff --git a/libmysql/authentication_win/common.cc b/libmysql/authentication_win/common.cc new file mode 100644 index 00000000000..1d1f2938969 --- /dev/null +++ b/libmysql/authentication_win/common.cc @@ -0,0 +1,492 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "common.h" +#include // for ConvertSidToStringSid() +#include // for GetUserNameEx() + + +template <> void error_log_print(const char *fmt, ...); +template <> void error_log_print(const char *fmt, ...); +template <> void error_log_print(const char *fmt, ...); + + +/** Connection class **************************************************/ + +/** + Create connection out of an active MYSQL_PLUGIN_VIO object. + + @param[in] vio pointer to a @c MYSQL_PLUGIN_VIO object used for + connection - it can not be NULL +*/ + +Connection::Connection(MYSQL_PLUGIN_VIO *vio): m_vio(vio), m_error(0) +{ + DBUG_ASSERT(vio); +} + + +/** + Write data to the connection. + + @param[in] blob data to be written + + @return 0 on success, VIO error code on failure. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +int Connection::write(const Blob &blob) +{ + m_error= m_vio->write_packet(m_vio, blob.ptr(), blob.len()); + +#ifndef DBUG_OFF + if (m_error) + DBUG_PRINT("error", ("vio write error %d", m_error)); +#endif + + return m_error; +} + + +/** + Read data from connection. + + @return A Blob containing read packet or null Blob in case of error. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +Blob Connection::read() +{ + unsigned char *ptr; + int len= m_vio->read_packet(m_vio, &ptr); + + if (len < 0) + { + m_error= true; + return Blob(); + } + + return Blob(ptr, len); +} + + +/** Sid class *****************************************************/ + + +/** + Create Sid object corresponding to a given account name. + + @param[in] account_name name of a Windows account + + The account name can be in any form accepted by @c LookupAccountName() + function. + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(const wchar_t *account_name): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD sid_size= 0, domain_size= 0; + bool success; + + // Determine required buffer sizes + + success= LookupAccountNameW(NULL, account_name, NULL, &sid_size, + NULL, &domain_size, &m_type); + + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "LookupAccountName() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + // Query for SID (domain is ignored) + + wchar_t *domain= new wchar_t[domain_size]; + m_data= (TOKEN_USER*) new BYTE[sid_size + sizeof(TOKEN_USER)]; + m_data->User.Sid= (BYTE*)m_data + sizeof(TOKEN_USER); + + success= LookupAccountNameW(NULL, account_name, + m_data->User.Sid, &sid_size, + domain, &domain_size, + &m_type); + + if (!success || !is_valid()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID of '%S', " + "LookupAccountName() failed with error %X (%s)", + account_name, GetLastError(), + get_last_error_message(error_buf))); +#endif + goto fail; + } + + goto end; + +fail: + if (m_data) + delete [] m_data; + m_data= NULL; + +end: + if (domain) + delete [] domain; +} + + +/** + Create Sid object corresponding to a given security token. + + @param[in] token security token of a Windows account + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(HANDLE token): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD req_size= 0; + bool success; + + // Determine required buffer size + + success= GetTokenInformation(token, TokenUser, NULL, 0, &req_size); + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + m_data= (TOKEN_USER*) new BYTE[req_size]; + success= GetTokenInformation(token, TokenUser, m_data, req_size, &req_size); + + if (!success || !is_valid()) + { + delete [] m_data; + m_data= NULL; +#ifndef DBUG_OFF + if (!success) + { + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not read SID from security token, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); + } +#endif + } +} + + +Sid::~Sid() +{ + if (m_data) + delete [] m_data; +#ifndef DBUG_OFF + if (m_as_string) + LocalFree(m_as_string); +#endif +} + +/// Check if Sid object is valid. +bool Sid::is_valid(void) const +{ + return m_data && m_data->User.Sid && IsValidSid(m_data->User.Sid); +} + + +#ifndef DBUG_OFF + +/** + Produces string representation of the SID. + + @return String representation of the SID or NULL in case of errors. + + @note Memory allocated for the string is automatically freed in Sid's + destructor. +*/ + +const char* Sid::as_string() +{ + if (!m_data) + return NULL; + + if (!m_as_string) + { + bool success= ConvertSidToStringSid(m_data->User.Sid, &m_as_string); + + if (!success) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not get textual representation of a SID, " + "ConvertSidToStringSid() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + m_as_string= NULL; + return NULL; + } + } + + return m_as_string; +} + +#endif + + +bool Sid::operator ==(const Sid &other) +{ + if (!is_valid() || !other.is_valid()) + return false; + + return EqualSid(m_data->User.Sid, other.m_data->User.Sid); +} + + +/** Generating User Principal Name *************************/ + +/** + Call Windows API functions to get UPN of the current user and store it + in internal buffer. +*/ + +UPN::UPN(): m_buf(NULL) +{ + wchar_t buf1[MAX_SERVICE_NAME_LENGTH]; + + // First we try to use GetUserNameEx. + + m_len= sizeof(buf1)/sizeof(wchar_t); + + if (!GetUserNameExW(NameUserPrincipal, buf1, (PULONG)&m_len)) + { + if (GetLastError()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("note", ("When determining UPN" + ", GetUserNameEx() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + if (ERROR_MORE_DATA == GetLastError()) + ERROR_LOG(INFO, ("Buffer overrun when determining UPN:" + " need %ul characters but have %ul", + m_len, sizeof(buf1)/sizeof(WCHAR))); + } + + m_len= 0; // m_len == 0 indicates invalid UPN + return; + } + + /* + UPN is stored in buf1 in wide-char format - convert it to utf8 + for sending over network. + */ + + m_buf= wchar_to_utf8(buf1, &m_len); + + if(!m_buf) + ERROR_LOG(ERROR, ("Failed to convert UPN to utf8")); + + // Note: possible error would be indicated by the fact that m_buf is NULL. + return; +} + + +UPN::~UPN() +{ + if (m_buf) + free(m_buf); +} + + +/** + Convert a wide-char string to utf8 representation. + + @param[in] string null-terminated wide-char string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in bytes, excluding terminating + null character) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing utf8 representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +char* wchar_to_utf8(const wchar_t *string, size_t *len) +{ + char *buf= NULL; + size_t str_len= len && *len ? *len : wcslen(string); + + /* + A conversion from utf8 to wchar_t will never take more than 3 bytes per + character, so a buffer of length 3 * str_len schould be sufficient. + We check that assumption with an assertion later. + */ + + size_t buf_len= 3 * str_len; + + buf= (char*)malloc(buf_len + 1); + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting string '%S' to utf8", + string)); + return NULL; + } + + int res= WideCharToMultiByte(CP_UTF8, // convert to UTF-8 + 0, // conversion flags + string, // input buffer + str_len, // its length + buf, buf_len, // output buffer and its size + NULL, NULL); // default character (not used) + + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // res is 0 which indicates error + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert string '%S' to utf8" + ", WideCharToMultiByte() failed with error %X (%s)", + string, GetLastError(), + get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** + Convert an utf8 string to a wide-char string. + + @param[in] string null-terminated utf8 string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in chars) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing wide-char representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +wchar_t* utf8_to_wchar(const char *string, size_t *len) +{ + size_t buf_len; + + /* + Note: length (in bytes) of an utf8 string is always bigger than the + number of characters in this string. Hence a buffer of size len will + be sufficient. We add 1 for the terminating null character. + */ + + buf_len= len && *len ? *len : strlen(string); + wchar_t *buf= (wchar_t*)malloc((buf_len+1)*sizeof(wchar_t)); + + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting utf8 string '%s'" + " to wide-char representation", string)); + return NULL; + } + + size_t res; + res= MultiByteToWideChar(CP_UTF8, // convert from UTF-8 + 0, // conversion flags + string, // input buffer + buf_len, // its size + buf, buf_len); // output buffer and its size + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // error in MultiByteToWideChar() + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert UPN from UTF-8" + ", MultiByteToWideChar() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** Error handling ****************************************************/ + + +/** + Returns error message corresponding to the last Windows error given + by GetLastError(). + + @note Error message is overwritten by next call to + @c get_last_error_message(). +*/ + +const char* get_last_error_message(Error_message_buf buf) +{ + int error= GetLastError(); + + buf[0]= '\0'; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, sizeof(buf), NULL ); + + return buf; +} diff --git a/libmysql/authentication_win/common.h b/libmysql/authentication_win/common.h new file mode 100644 index 00000000000..68f39fe04cc --- /dev/null +++ b/libmysql/authentication_win/common.h @@ -0,0 +1,290 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include // for CtxtHandle +#include // for MYSQL_PLUGIN_VIO + +/// Maximum length of the target service name. +#define MAX_SERVICE_NAME_LENGTH 1024 + + +/** Debugging and error reporting infrastructure ***************************/ + +/* + Note: We use plugin local logging and error reporting mechanisms until + WL#2940 (plugin service: error reporting) is available. +*/ + +#undef INFO +#undef WARNING +#undef ERROR + +struct error_log_level +{ + typedef enum {INFO, WARNING, ERROR} type; +}; + +#undef DBUG_ASSERT +#ifndef DBUG_OFF +#define DBUG_ASSERT(X) assert(X) +#else +#define DBUG_ASSERT(X) do {} while (0) +#endif + +extern "C" int opt_auth_win_client_log; + +/* + Note1: Double level of indirection in definition of DBUG_PRINT allows to + temporary redefine or disable DBUG_PRINT macro and then easily return to + the original definition (in terms of DBUG_PRINT_DO). + + Note2: DBUG_PRINT() can use printf-like format string like this: + + DBUG_PRINT(Keyword, ("format string", args)); + + The implementation should handle it correctly. Currently it is passed + to fprintf() (see debug_msg() function). +*/ + +#ifndef DBUG_OFF +#define DBUG_PRINT_DO(Keyword, Msg) \ + do { \ + if (2 > opt_auth_win_client_log) break; \ + fprintf(stderr, "winauth: %s: ", Keyword); \ + debug_msg Msg; \ + } while (0) +#else +#define DBUG_PRINT_DO(K, M) do {} while (0) +#endif + +#undef DBUG_PRINT +#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) + +/* + If DEBUG_ERROR_LOG is defined then error logging happens only + in debug-copiled code. Otherwise ERROR_LOG() expands to + error_log_print() even in production code. Note that in client + plugin, error_log_print() will print nothing if opt_auth_win_clinet_log + is 0. +*/ +#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF) +#define ERROR_LOG(Level, Msg) do {} while (0) +#else +#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg +#endif + +inline +void debug_msg(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); + va_end(args); +} + + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args); + +template +void error_log_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + error_log_vprint(Level, fmt, args); + va_end(args); +} + +typedef char Error_message_buf[1024]; +const char* get_last_error_message(Error_message_buf); + + +/** Blob class *************************************************************/ + +typedef unsigned char byte; + +/** + Class representing a region of memory (e.g., a string or binary buffer). + + @note This class does not allocate memory. It merely describes a region + of memory which must be allocated externally (if it is dynamic memory). +*/ + +class Blob +{ + byte *m_ptr; ///< Pointer to the first byte of the memory region. + size_t m_len; ///< Length of the memory region. + +public: + + Blob(): m_ptr(NULL), m_len(0) + {} + + Blob(const byte *ptr, const size_t len) + : m_ptr(const_cast(ptr)), m_len(len) + {} + + Blob(const char *str): m_ptr((byte*)str) + { + m_len= strlen(str); + } + + byte* ptr() const + { + return m_ptr; + } + + size_t len() const + { + return m_len; + } + + byte operator[](unsigned pos) const + { + return pos < len() ? m_ptr[pos] : 0x00; + } + + bool is_null() const + { + return m_ptr == NULL; + } +}; + + +/** Connection class *******************************************************/ + +/** + Convenience wrapper around MYSQL_PLUGIN_VIO object providing basic + read/write operations. +*/ + +class Connection +{ + MYSQL_PLUGIN_VIO *m_vio; ///< Pointer to @c MYSQL_PLUGIN_VIO structure. + + /** + If non-zero, indicates that connection is broken. If this has happened + because of failed operation, stores non-zero error code from that failure. + */ + int m_error; + +public: + + Connection(MYSQL_PLUGIN_VIO *vio); + int write(const Blob&); + Blob read(); + + int error() const + { + return m_error; + } +}; + + +/** Sid class **************************************************************/ + +/** + Class for storing and manipulating Windows security identifiers (SIDs). +*/ + +class Sid +{ + TOKEN_USER *m_data; ///< Pointer to structure holding identifier's data. + SID_NAME_USE m_type; ///< Type of identified entity. + +public: + + Sid(const wchar_t*); + Sid(HANDLE sec_token); + ~Sid(); + + bool is_valid(void) const; + + bool is_group(void) const + { + return m_type == SidTypeGroup + || m_type == SidTypeWellKnownGroup + || m_type == SidTypeAlias; + } + + bool is_user(void) const + { + return m_type == SidTypeUser; + } + + bool operator==(const Sid&); + + operator PSID() const + { + return (PSID)m_data->User.Sid; + } + +#ifndef DBUG_OFF + +private: + char *m_as_string; ///< Cached string representation of the SID. +public: + const char* as_string(); + +#endif +}; + + +/** UPN class **************************************************************/ + +/** + An object of this class obtains and stores User Principal Name of the + account under which current process is running. +*/ + +class UPN +{ + char *m_buf; ///< Pointer to UPN in utf8 representation. + size_t m_len; ///< Length of the name. + +public: + + UPN(); + ~UPN(); + + bool is_valid() const + { + return m_len > 0; + } + + const Blob as_blob() const + { + return m_len ? Blob((byte*)m_buf, m_len) : Blob(); + } + + const char* as_string() const + { + return (const char*)m_buf; + } + +}; + + +char* wchar_to_utf8(const wchar_t*, size_t*); +wchar_t* utf8_to_wchar(const char*, size_t*); + +#endif diff --git a/libmysql/authentication_win/handshake.cc b/libmysql/authentication_win/handshake.cc new file mode 100644 index 00000000000..4b33349d0f4 --- /dev/null +++ b/libmysql/authentication_win/handshake.cc @@ -0,0 +1,288 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + + +/** Handshake class implementation **********************************/ + +/** + Create common part of handshake context. + + @param[in] ssp name of the SSP (Security Service Provider) to + be used for authentication + @param[in] side is this handshake object used for server- or + client-side handshake + + Prepare for handshake using the @c ssp security module. We use + "Negotiate" which picks best available module. Parameter @c side + tells if this is preparing for server or client side authentication + and is used to prepare appropriate credentials. +*/ + +Handshake::Handshake(const char *ssp, side_t side) +: m_atts(0L), m_error(0), m_complete(FALSE), + m_have_credentials(false), m_have_sec_context(false) +#ifndef DBUG_OFF + , m_ssp_info(NULL) +#endif +{ + SECURITY_STATUS ret; + + // Obtain credentials for the authentication handshake. + + ret= AcquireCredentialsHandle(NULL, (SEC_CHAR*)ssp, + side == SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, &m_cred, &m_expire); + + if (ret != SEC_E_OK) + { + DBUG_PRINT("error", ("AcqireCredentialsHandle() failed" + " with error %X", ret)); + ERROR_LOG(ERROR, ("Could not obtain local credentials" + " required for authentication")); + m_error= ret; + } + + m_have_credentials= true; +} + + +Handshake::~Handshake() +{ + if (m_have_credentials) + FreeCredentialsHandle(&m_cred); + if (m_have_sec_context) + DeleteSecurityContext(&m_sctx); + m_output.free(); + +#ifndef DBUG_OFF + if (m_ssp_info) + FreeContextBuffer(m_ssp_info); +#endif +} + + +/** + Read and process data packets from the other end of a connection. + + @param[IN] con a connection to read packets from + + Packets are read and processed until authentication handshake is + complete. It is assumed that the peer will send at least one packet. + Packets are processed with @c process_data() method. If new data is + generated during packet processing, this data is sent to the peer and + another round of packet exchange starts. + + @return 0 on success. + + @note In case of error, appropriate error message is logged. +*/ +int Handshake::packet_processing_loop(Connection &con) +{ + unsigned round= 1; + + do { + // Read packet send by the peer + DBUG_PRINT("info", ("Waiting for packet")); + Blob packet= con.read(); + if (con.error() || packet.is_null()) + { + ERROR_LOG(ERROR, ("Error reading packet in round %d", round)); + return 1; + } + DBUG_PRINT("info", ("Got packet of length %d", packet.len())); + + /* + Process received data, possibly generating new data to be sent. + */ + + Blob new_data= process_data(packet); + + if (error()) + { + ERROR_LOG(ERROR, ("Error processing packet in round %d", round)); + return 1; + } + + /* + If new data has been generated, send it to the peer. Otherwise + handshake must be completed. + */ + + if (!new_data.is_null()) + { + ++round; + DBUG_PRINT("info", ("Round %d started", round)); + + DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); + int ret= con.write(new_data); + if (ret) + { + ERROR_LOG(ERROR, ("Error writing packet in round %d", round)); + return 1; + } + DBUG_PRINT("info", ("Data sent")); + } + else if (!is_complete()) + { + ERROR_LOG(ERROR, ("No data to send in round %d" + " but handshake is not complete", round)); + return 1; + } + + /* + To protect against malicious clients, break handshake exchange if + too many rounds. + */ + + if (round > MAX_HANDSHAKE_ROUNDS) + { + ERROR_LOG(ERROR, ("Authentication handshake could not be completed" + " after %d rounds", round)); + return 1; + } + + } while(!is_complete()); + + ERROR_LOG(INFO, ("Handshake completed after %d rounds", round)); + return 0; +} + + +#ifndef DBUG_OFF + +/** + Get name of the security package which was used in authentication. + + This method should be called only after handshake was completed. It is + available only in debug builds. + + @return Name of security package or NULL if it can not be obtained. +*/ + +const char* Handshake::ssp_name() +{ + if (!m_ssp_info && m_complete) + { + SecPkgContext_PackageInfo pinfo; + + int ret= QueryContextAttributes(&m_sctx, SECPKG_ATTR_PACKAGE_INFO, &pinfo); + + if (SEC_E_OK == ret) + { + m_ssp_info= pinfo.PackageInfo; + } + else + DBUG_PRINT("error", + ("Could not obtain SSP info from authentication context" + ", QueryContextAttributes() failed with error %X", ret)); + } + + return m_ssp_info ? m_ssp_info->Name : NULL; +} + +#endif + + +/** + Process result of @c {Initialize,Accept}SecurityContext() function. + + @param[in] ret return code from @c {Initialize,Accept}SecurityContext() + function + + This function analyses return value of Windows + @c {Initialize,Accept}SecurityContext() function. A call to + @c CompleteAuthToken() is done if requested. If authentication is complete, + this fact is marked in the internal state of the Handshake object. + If errors are detected the object is moved to error state. + + @return True if error has been detected. +*/ + +bool Handshake::process_result(int ret) +{ + /* + First check for errors and set the m_complete flag if the result + indicates that handshake is complete. + */ + + switch (ret) + { + case SEC_E_OK: + case SEC_I_COMPLETE_NEEDED: + // Handshake completed + m_complete= true; + break; + + case SEC_I_CONTINUE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + break; + + default: + m_error= ret; + return true; + } + + m_have_sec_context= true; + + /* + If the result indicates a need for this, complete the authentication + token. + */ + + switch (ret) + { + case SEC_I_COMPLETE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + ret= CompleteAuthToken(&m_sctx, &m_output); + if (ret != 0) + { + DBUG_PRINT("error", ("CompleteAuthToken() failed with error %X", ret)); + m_error= ret; + return true; + } + default: + break; + } + + return false; +} + + +/** Security_buffer class implementation **********************************/ + + +Security_buffer::Security_buffer(const Blob &blob): m_allocated(false) +{ + init(blob.ptr(), blob.len()); +} + + +Security_buffer::Security_buffer(): m_allocated(true) +{ + init(NULL, 0); +} + + +void Security_buffer::free(void) +{ + if (!m_allocated) + return; + if (!ptr()) + return; + FreeContextBuffer(ptr()); + m_allocated= false; +} diff --git a/libmysql/authentication_win/handshake.h b/libmysql/authentication_win/handshake.h new file mode 100644 index 00000000000..bb88510a7f8 --- /dev/null +++ b/libmysql/authentication_win/handshake.h @@ -0,0 +1,168 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef HANDSHAKE_H +#define HANDSHAKE_H + +#include "common.h" + +/** + Name of the SSP (Security Support Provider) to be used for authentication. + + We use "Negotiate" which will find the most secure SSP which can be used + and redirect to that SSP. +*/ +#define SSP_NAME "Negotiate" + +/** + Maximal number of rounds in authentication handshake. + + Server will interrupt authentication handshake with error if client's + identity can not be determined within this many rounds. +*/ +#define MAX_HANDSHAKE_ROUNDS 50 + + +/// Convenience wrapper around @c SecBufferDesc. + +class Security_buffer: public SecBufferDesc +{ + SecBuffer m_buf; ///< A @c SecBuffer instance. + + void init(byte *ptr, size_t len) + { + ulVersion= 0; + cBuffers= 1; + pBuffers= &m_buf; + + m_buf.BufferType= SECBUFFER_TOKEN; + m_buf.pvBuffer= ptr; + m_buf.cbBuffer= len; + } + + /// If @c false, no deallocation will be done in the destructor. + bool m_allocated; + + public: + + Security_buffer(const Blob&); + Security_buffer(); + + ~Security_buffer() + { + free(); + } + + byte* ptr() const + { + return (byte*)m_buf.pvBuffer; + } + + size_t len() const + { + return m_buf.cbBuffer; + } + + bool is_valid() const + { + return ptr() != NULL; + } + + const Blob as_blob() const + { + return Blob(ptr(), len()); + } + + void free(void); +}; + + +/// Common base for Handshake_{server,client}. + +class Handshake +{ +public: + + typedef enum {CLIENT, SERVER} side_t; + + Handshake(const char *ssp, side_t side); + virtual ~Handshake(); + + int Handshake::packet_processing_loop(Connection &con); + + bool virtual is_complete() const + { + return m_complete; + } + + int error() const + { + return m_error; + } + +protected: + + /// Security context object created during the handshake. + CtxtHandle m_sctx; + + /// Credentials of the principal performing this handshake. + CredHandle m_cred; + + /// Stores expiry date of the created security context. + TimeStamp m_expire; + + /// Stores attributes of the created security context. + ULONG m_atts; + + /// If non-zero, stores error code of the last failed operation. + int m_error; + + /// @c true when handshake is complete. + bool m_complete; + + /// @c true when the principal credentials has been determined. + bool m_have_credentials; + + /// @c true when the security context has been created. + bool m_have_sec_context; + + /// Buffer for data to be send to the other side. + Security_buffer m_output; + + bool process_result(int); + + /** + This method is used inside @c packet_processing_loop to process + data packets received from the other end. + + @param[IN] data data to be processed + + @return A blob with data to be sent to the other end or null blob if + no more data needs to be exchanged. + */ + virtual Blob process_data(const Blob &data)= 0; + +#ifndef DBUG_OFF + +private: + SecPkgInfo *m_ssp_info; +public: + const char* ssp_name(); + +#endif +}; + + +#endif diff --git a/libmysql/authentication_win/handshake_client.cc b/libmysql/authentication_win/handshake_client.cc new file mode 100644 index 00000000000..e42f055da70 --- /dev/null +++ b/libmysql/authentication_win/handshake_client.cc @@ -0,0 +1,285 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + +#include // for MYSQL structure + + +/// Client-side context for authentication handshake + +class Handshake_client: public Handshake +{ + /** + Name of the server's service for which we authenticate. + + The service name is sent by server in the initial packet. If no + service name is used, this member is @c NULL. + */ + SEC_WCHAR *m_service_name; + + /// Buffer for storing service name obtained from server. + SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH]; + +public: + + Handshake_client(const char *target, size_t len); + ~Handshake_client(); + + Blob first_packet(); + Blob process_data(const Blob&); +}; + + +/** + Create authentication handshake context for client. + + @param target name of the target service with which we will authenticate + (can be NULL if not used) + + Some security packages (like Kerberos) require providing explicit name + of the service with which a client wants to authenticate. The server-side + authentication plugin sends this name in the greeting packet + (see @c win_auth_handshake_{server,client}() functions). +*/ + +Handshake_client::Handshake_client(const char *target, size_t len) +: Handshake(SSP_NAME, CLIENT), m_service_name(NULL) +{ + if (!target || 0 == len) + return; + + // Convert received UPN to internal WCHAR representation. + + m_service_name= utf8_to_wchar(target, &len); + + if (m_service_name) + DBUG_PRINT("info", ("Using target service: %S\n", m_service_name)); + else + { + /* + Note: we ignore errors here - m_target will be NULL, the target name + will not be used and system will fall-back to NTLM authentication. But + we leave trace in error log. + */ + ERROR_LOG(WARNING, ("Could not decode UPN sent by the server" + "; target service name will not be used" + " and Kerberos authentication will not work")); + } +} + + +Handshake_client::~Handshake_client() +{ + if (m_service_name) + free(m_service_name); +} + + +/** + Generate first packet to be sent to the server during packet exchange. + + This first packet should contain some data. In case of error a null blob + is returned and @c error() gives non-zero error code. + + @return Data to be sent in the first packet or null blob in case of error. +*/ + +Blob Handshake_client::first_packet() +{ + SECURITY_STATUS ret; + + m_output.free(); + + ret= InitializeSecurityContextW( + &m_cred, + NULL, // partial context + m_service_name, // service name + ASC_REQ_ALLOCATE_MEMORY, // requested attributes + 0, // reserved + SECURITY_NETWORK_DREP, // data representation + NULL, // input data + 0, // reserved + &m_sctx, // context + &m_output, // output data + &m_atts, // attributes + &m_expire); // expire date + + if (process_result(ret)) + { + DBUG_PRINT("error", + ("InitializeSecurityContext() failed with error %X", ret)); + return Blob(); + } + + return m_output.as_blob(); +} + + +/** + Process data sent by server. + + @param[in] data blob with data from server + + This method analyses data sent by server during authentication handshake. + If client should continue packet exchange, this method returns data to + be sent to the server next. If no more data needs to be exchanged, an + empty blob is returned and @c is_complete() is @c true. In case of error + an empty blob is returned and @c error() gives non-zero error code. + + @return Data to be sent to the server next or null blob if no more data + needs to be exchanged or in case of error. +*/ + +Blob Handshake_client::process_data(const Blob &data) +{ + Security_buffer input(data); + SECURITY_STATUS ret; + + m_output.free(); + + ret= InitializeSecurityContextW( + &m_cred, + &m_sctx, // partial context + m_service_name, // service name + ASC_REQ_ALLOCATE_MEMORY, // requested attributes + 0, // reserved + SECURITY_NETWORK_DREP, // data representation + &input, // input data + 0, // reserved + &m_sctx, // context + &m_output, // output data + &m_atts, // attributes + &m_expire); // expire date + + if (process_result(ret)) + { + DBUG_PRINT("error", + ("InitializeSecurityContext() failed with error %X", ret)); + return Blob(); + } + + return m_output.as_blob(); +} + + +/**********************************************************************/ + + +/** + Perform authentication handshake from client side. + + @param[in] vio pointer to @c MYSQL_PLUGIN_VIO instance to be used + for communication with the server + @param[in] mysql pointer to a MySQL connection for which we authenticate + + After reading the initial packet from server, containing its UPN to be + used as service name, client starts packet exchange by sending the first + packet in this exchange. While handshake is not yet completed, client + reads packets sent by the server and process them, possibly generating new + data to be sent to the server. + + This function reports errors. + + @return 0 on success. +*/ + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + /* + Check if we should enable logging. + */ + { + const char *opt= getenv("AUTHENTICATION_WIN_LOG"); + int opt_val= opt ? atoi(opt) : 0; + if (opt && !opt_val) + { + if (!strncasecmp("on", opt, 2)) opt_val= 1; + if (!strncasecmp("yes", opt, 3)) opt_val= 1; + if (!strncasecmp("true", opt, 4)) opt_val= 1; + if (!strncasecmp("debug", opt, 5)) opt_val= 2; + if (!strncasecmp("dbug", opt, 4)) opt_val= 2; + } + opt_auth_win_client_log= opt_val; + } + + ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user)); + + // Create connection object. + + Connection con(vio); + DBUG_ASSERT(!con.error()); + + // Read initial packet from server containing service name. + + int ret; + Blob service_name= con.read(); + + if (con.error() || service_name.is_null()) + { + ERROR_LOG(ERROR, ("Error reading initial packet")); + return CR_ERROR; + } + DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len())); + + // Create authentication handsake context using the given service name. + + Handshake_client hndshk(service_name[0] ? (char *)service_name.ptr() : NULL, + service_name.len()); + if (hndshk.error()) + { + ERROR_LOG(ERROR, ("Could not create authentication handshake context")); + return CR_ERROR; + } + + /* + The following packet exchange always starts with a packet sent by + the client. Send this first packet now. + */ + + { + Blob packet= hndshk.first_packet(); + if (hndshk.error() || packet.is_null()) + { + ERROR_LOG(ERROR, ("Could not generate first packet")); + return CR_ERROR; + } + DBUG_PRINT("info", ("Sending first packet of length %d", packet.len())); + + ret= con.write(packet); + if (ret) + { + ERROR_LOG(ERROR, ("Error writing first packet")); + return CR_ERROR; + } + DBUG_PRINT("info", ("First packet sent")); + } + + DBUG_ASSERT(!hndshk.error()); + + /* + If handshake is not yet complete and client expects a reply, + read and process packets from server until handshake is complete. + */ + if (!hndshk.is_complete()) + { + if (hndshk.packet_processing_loop(con)) + return CR_ERROR; + } + + DBUG_ASSERT(!hndshk.error() && hndshk.is_complete()); + + return CR_OK; +} diff --git a/libmysql/authentication_win/log_client.cc b/libmysql/authentication_win/log_client.cc new file mode 100644 index 00000000000..df4ce4f9c2a --- /dev/null +++ b/libmysql/authentication_win/log_client.cc @@ -0,0 +1,55 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include "common.h" + +/** + This option is set in win_auth_handshake_client() function + in handshake_client.cc. + + Values: + 0 - no logging + 1 - log error/warning/info messages + 2 - also log debug messages + + Note: No error or debug messages are logged in production code + (see logging macros in common.h). +*/ +int opt_auth_win_client_log= 0; + + +// Client-side logging function + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args) +{ + if (0 == opt_auth_win_client_log) + return; + + const char *level_string= ""; + + switch (level) + { + case error_log_level::INFO: level_string= "Note"; break; + case error_log_level::WARNING: level_string= "Warning"; break; + case error_log_level::ERROR: level_string= "ERROR"; break; + } + + fprintf(stderr, "Windows Authentication Plugin %s: ", level_string); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); +} diff --git a/libmysql/authentication_win/plugin_client.cc b/libmysql/authentication_win/plugin_client.cc new file mode 100644 index 00000000000..72769ada8f6 --- /dev/null +++ b/libmysql/authentication_win/plugin_client.cc @@ -0,0 +1,58 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include + +#include "common.h" + +static int win_auth_client_plugin_init(char*, size_t, int, va_list) +{ + return 0; +} + + +static int win_auth_client_plugin_deinit() +{ + return 0; +} + + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); + + +/* + Client plugin declaration. This is added to mysql_client_builtins[] + in sql-common/client.c +*/ + +extern "C" +st_mysql_client_plugin_AUTHENTICATION win_auth_client_plugin= +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + "authentication_windows_client", + "Rafal Somla", + "Windows Authentication Plugin - client side", + {0,1,0}, + "GPL", + NULL, + win_auth_client_plugin_init, + win_auth_client_plugin_deinit, + NULL, // option handling + win_auth_handshake_client +}; diff --git a/sql-common/client.c b/sql-common/client.c index 90d07f3e409..abaea310aae 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2314,11 +2314,18 @@ static auth_plugin_t clear_password_client_plugin= clear_password_auth_client }; +#ifdef AUTHENTICATION_WIN +extern auth_plugin_t win_auth_client_plugin; +#endif + struct st_mysql_client_plugin *mysql_client_builtins[]= { (struct st_mysql_client_plugin *)&native_password_client_plugin, (struct st_mysql_client_plugin *)&old_password_client_plugin, (struct st_mysql_client_plugin *)&clear_password_client_plugin, +#ifdef AUTHENTICATION_WIN + (struct st_mysql_client_plugin *)&win_auth_client_plugin, +#endif 0 }; From a6acc73bb18f3a662649c40a12c37063bfae37d6 Mon Sep 17 00:00:00 2001 From: Rafal Somla Date: Thu, 28 Apr 2011 21:39:42 +0200 Subject: [PATCH 48/57] BUG#11879051: FIRST REPLY LENGTH LIMIT (255) CAN BE VIOLATED BEFORE: First packet sent by client-side plugin (generated by Windows function InitializeSecurityContext()) could be longer than 255 bytes violating the limitation imposed by authentication protocol. AFTER: Handshake protocol is changed so that if first client's reply is longer than 254 bytes then it is be sent in 2 parts. However, for replies shorter than 255 bytes nothing changes. ADDITIONAL CHANGES: - The generic packet processing loop (Handshake::packet_processing_loop) has been refactored. Communication with the peer has been abstracted into virtual methods read/write_packet() which are implemented in client and server and transparently do the required splitting and gluing of packets. - Make it possible to optionally use dbug library in the plugin. - Add code for testing splitting of long first client reply. --- libmysql/authentication_win/CMakeLists.txt | 4 +- libmysql/authentication_win/common.h | 130 ++++++---- libmysql/authentication_win/handshake.cc | 29 ++- libmysql/authentication_win/handshake.h | 17 +- .../authentication_win/handshake_client.cc | 245 ++++++++++++------ 5 files changed, 284 insertions(+), 141 deletions(-) diff --git a/libmysql/authentication_win/CMakeLists.txt b/libmysql/authentication_win/CMakeLists.txt index 82c9dffb06e..80cd14780e6 100644 --- a/libmysql/authentication_win/CMakeLists.txt +++ b/libmysql/authentication_win/CMakeLists.txt @@ -18,7 +18,9 @@ # ADD_DEFINITIONS(-DSECURITY_WIN32) -ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds +ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds +ADD_DEFINITIONS(-DWINAUTH_USE_DBUG_LIB) # it is OK to use dbug library in statically + # linked plugin SET(HEADERS common.h handshake.h) SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc) diff --git a/libmysql/authentication_win/common.h b/libmysql/authentication_win/common.h index 68f39fe04cc..ff0f7153664 100644 --- a/libmysql/authentication_win/common.h +++ b/libmysql/authentication_win/common.h @@ -41,41 +41,6 @@ struct error_log_level typedef enum {INFO, WARNING, ERROR} type; }; -#undef DBUG_ASSERT -#ifndef DBUG_OFF -#define DBUG_ASSERT(X) assert(X) -#else -#define DBUG_ASSERT(X) do {} while (0) -#endif - -extern "C" int opt_auth_win_client_log; - -/* - Note1: Double level of indirection in definition of DBUG_PRINT allows to - temporary redefine or disable DBUG_PRINT macro and then easily return to - the original definition (in terms of DBUG_PRINT_DO). - - Note2: DBUG_PRINT() can use printf-like format string like this: - - DBUG_PRINT(Keyword, ("format string", args)); - - The implementation should handle it correctly. Currently it is passed - to fprintf() (see debug_msg() function). -*/ - -#ifndef DBUG_OFF -#define DBUG_PRINT_DO(Keyword, Msg) \ - do { \ - if (2 > opt_auth_win_client_log) break; \ - fprintf(stderr, "winauth: %s: ", Keyword); \ - debug_msg Msg; \ - } while (0) -#else -#define DBUG_PRINT_DO(K, M) do {} while (0) -#endif - -#undef DBUG_PRINT -#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) /* If DEBUG_ERROR_LOG is defined then error logging happens only @@ -83,24 +48,23 @@ extern "C" int opt_auth_win_client_log; error_log_print() even in production code. Note that in client plugin, error_log_print() will print nothing if opt_auth_win_clinet_log is 0. + + Note: Macro ERROR_LOG() can use printf-like format string like this: + + ERROR_LOG(Level, ("format string", args)); + + The implementation should handle it correctly. Currently it is passed + to fprintf() (see error_log_vprint() function). */ + +extern "C" int opt_auth_win_client_log; + #if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF) #define ERROR_LOG(Level, Msg) do {} while (0) #else #define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg #endif -inline -void debug_msg(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - fputc('\n', stderr); - fflush(stderr); - va_end(args); -} - void error_log_vprint(error_log_level::type level, const char *fmt, va_list args); @@ -118,6 +82,70 @@ typedef char Error_message_buf[1024]; const char* get_last_error_message(Error_message_buf); +/* + Internal implementation of debug message printing which does not use + dbug library. This is invoked via macro: + + DBUG_PRINT_DO(Keyword, ("format string", args)); + + This is supposed to be used as an implementation of DBUG_PRINT() macro, + unless the dbug library implementation is used or debug messages are disabled. +*/ + +#ifndef DBUG_OFF + +#define DBUG_PRINT_DO(Keyword, Msg) \ + do { \ + if (2 > opt_auth_win_client_log) break; \ + fprintf(stderr, "winauth: %s: ", Keyword); \ + debug_msg Msg; \ + } while (0) + +inline +void debug_msg(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); + va_end(args); +} + +#else +#define DBUG_PRINT_DO(K, M) do {} while (0) +#endif + + +#ifndef WINAUTH_USE_DBUG_LIB + +#undef DBUG_PRINT +#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) + +/* + Redefine few more debug macros to make sure that no symbols from + dbug library are used. +*/ + +#undef DBUG_ENTER +#define DBUG_ENTER(X) do {} while (0) + +#undef DBUG_RETURN +#define DBUG_RETURN(X) return (X) + +#undef DBUG_ASSERT +#ifndef DBUG_OFF +#define DBUG_ASSERT(X) assert (X) +#else +#define DBUG_ASSERT(X) do {} while (0) +#endif + +#undef DBUG_DUMP +#define DBUG_DUMP(A,B,C) do {} while (0) + +#endif + + /** Blob class *************************************************************/ typedef unsigned char byte; @@ -158,15 +186,21 @@ public: return m_len; } - byte operator[](unsigned pos) const + byte& operator[](unsigned pos) const { - return pos < len() ? m_ptr[pos] : 0x00; + static byte out_of_range= 0; // alas, no exceptions... + return pos < len() ? m_ptr[pos] : out_of_range; } bool is_null() const { return m_ptr == NULL; } + + void trim(size_t l) + { + m_len= l; + } }; diff --git a/libmysql/authentication_win/handshake.cc b/libmysql/authentication_win/handshake.cc index 4b33349d0f4..ec665af9ef9 100644 --- a/libmysql/authentication_win/handshake.cc +++ b/libmysql/authentication_win/handshake.cc @@ -90,17 +90,19 @@ Handshake::~Handshake() @note In case of error, appropriate error message is logged. */ -int Handshake::packet_processing_loop(Connection &con) +int Handshake::packet_processing_loop() { - unsigned round= 1; + m_round= 0; do { + ++m_round; // Read packet send by the peer + DBUG_PRINT("info", ("Waiting for packet")); - Blob packet= con.read(); - if (con.error() || packet.is_null()) + Blob packet= read_packet(); + if (error()) { - ERROR_LOG(ERROR, ("Error reading packet in round %d", round)); + ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round)); return 1; } DBUG_PRINT("info", ("Got packet of length %d", packet.len())); @@ -113,7 +115,7 @@ int Handshake::packet_processing_loop(Connection &con) if (error()) { - ERROR_LOG(ERROR, ("Error processing packet in round %d", round)); + ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round)); return 1; } @@ -124,14 +126,13 @@ int Handshake::packet_processing_loop(Connection &con) if (!new_data.is_null()) { - ++round; - DBUG_PRINT("info", ("Round %d started", round)); + DBUG_PRINT("info", ("Round %d started", m_round)); DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); - int ret= con.write(new_data); + int ret= write_packet(new_data); if (ret) { - ERROR_LOG(ERROR, ("Error writing packet in round %d", round)); + ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round)); return 1; } DBUG_PRINT("info", ("Data sent")); @@ -139,7 +140,7 @@ int Handshake::packet_processing_loop(Connection &con) else if (!is_complete()) { ERROR_LOG(ERROR, ("No data to send in round %d" - " but handshake is not complete", round)); + " but handshake is not complete", m_round)); return 1; } @@ -148,16 +149,16 @@ int Handshake::packet_processing_loop(Connection &con) too many rounds. */ - if (round > MAX_HANDSHAKE_ROUNDS) + if (m_round > MAX_HANDSHAKE_ROUNDS) { ERROR_LOG(ERROR, ("Authentication handshake could not be completed" - " after %d rounds", round)); + " after %d rounds", m_round)); return 1; } } while(!is_complete()); - ERROR_LOG(INFO, ("Handshake completed after %d rounds", round)); + ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round)); return 0; } diff --git a/libmysql/authentication_win/handshake.h b/libmysql/authentication_win/handshake.h index bb88510a7f8..5292948b583 100644 --- a/libmysql/authentication_win/handshake.h +++ b/libmysql/authentication_win/handshake.h @@ -100,7 +100,7 @@ public: Handshake(const char *ssp, side_t side); virtual ~Handshake(); - int Handshake::packet_processing_loop(Connection &con); + int Handshake::packet_processing_loop(); bool virtual is_complete() const { @@ -126,6 +126,13 @@ protected: /// Stores attributes of the created security context. ULONG m_atts; + /** + Round of the handshake (starting from round 1). One round + consist of reading packet from the other side, processing it and + optionally sending a reply (see @c packet_processing_loop()). + */ + unsigned int m_round; + /// If non-zero, stores error code of the last failed operation. int m_error; @@ -152,7 +159,13 @@ protected: @return A blob with data to be sent to the other end or null blob if no more data needs to be exchanged. */ - virtual Blob process_data(const Blob &data)= 0; + virtual Blob process_data(const Blob &data) =0; + + /// Read packet from the other end. + virtual Blob read_packet() =0; + + /// Write packet to the other end. + virtual int write_packet(Blob &data) =0; #ifndef DBUG_OFF diff --git a/libmysql/authentication_win/handshake_client.cc b/libmysql/authentication_win/handshake_client.cc index e42f055da70..7e89fc92ae7 100644 --- a/libmysql/authentication_win/handshake_client.cc +++ b/libmysql/authentication_win/handshake_client.cc @@ -33,21 +33,27 @@ class Handshake_client: public Handshake /// Buffer for storing service name obtained from server. SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH]; + Connection &m_con; + public: - Handshake_client(const char *target, size_t len); + Handshake_client(Connection &con, const char *target, size_t len); ~Handshake_client(); Blob first_packet(); Blob process_data(const Blob&); + + Blob read_packet(); + int write_packet(Blob &data); }; /** Create authentication handshake context for client. - @param target name of the target service with which we will authenticate - (can be NULL if not used) + @param con connection for communication with the peer + @param target name of the target service with which we will authenticate + (can be NULL if not used) Some security packages (like Kerberos) require providing explicit name of the service with which a client wants to authenticate. The server-side @@ -55,8 +61,9 @@ public: (see @c win_auth_handshake_{server,client}() functions). */ -Handshake_client::Handshake_client(const char *target, size_t len) -: Handshake(SSP_NAME, CLIENT), m_service_name(NULL) +Handshake_client::Handshake_client(Connection &con, + const char *target, size_t len) +: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con) { if (!target || 0 == len) return; @@ -88,43 +95,97 @@ Handshake_client::~Handshake_client() } -/** - Generate first packet to be sent to the server during packet exchange. - - This first packet should contain some data. In case of error a null blob - is returned and @c error() gives non-zero error code. - - @return Data to be sent in the first packet or null blob in case of error. -*/ - -Blob Handshake_client::first_packet() +Blob Handshake_client::read_packet() { - SECURITY_STATUS ret; - - m_output.free(); - - ret= InitializeSecurityContextW( - &m_cred, - NULL, // partial context - m_service_name, // service name - ASC_REQ_ALLOCATE_MEMORY, // requested attributes - 0, // reserved - SECURITY_NETWORK_DREP, // data representation - NULL, // input data - 0, // reserved - &m_sctx, // context - &m_output, // output data - &m_atts, // attributes - &m_expire); // expire date - - if (process_result(ret)) - { - DBUG_PRINT("error", - ("InitializeSecurityContext() failed with error %X", ret)); + /* + We do a fake read in the first round because first + packet from the server containing UPN must be read + before the handshake context is created and the packet + processing loop starts. We return an empty blob here + and process_data() function will ignore it. + */ + if (m_round == 1) return Blob(); + + // Otherwise we read packet from the connection. + + Blob packet= m_con.read(); + m_error= m_con.error(); + if (!m_error && packet.is_null()) + m_error= true; // (no specific error code assigned) + + if (m_error) + return Blob(); + + DBUG_PRINT("dump", ("Got the following bytes")); + DBUG_DUMP("dump", packet.ptr(), packet.len()); + return packet; +} + + + +int Handshake_client::write_packet(Blob &data) +{ + /* + Length of the first data payload send by client authentication plugin is + limited to 255 bytes (because it is wrapped inside client authentication + packet and is length-encoded with 1 byte for the length). + + If the data payload is longer than 254 bytes, then it is sent in two parts: + first part of length 255 will be embedded in the authentication packet, + second part will be sent in the following packet. Byte 255 of the first + part contains information about the total length of the payload. It is a + number of blocks of size 512 bytes which is sufficient to store the + combined packets. + + Server's logic for reading first client's payload is as follows + (see Handshake_server::read_packet()): + 1. Read data from the authentication packet, if it is shorter than 255 bytes + then that is all data sent by client. + 2. If there is 255 bytes of data in the authentication packet, read another + packet and append it to the data, skipping byte 255 of the first packet + which can be used to allocate buffer of appropriate size. + */ + + size_t len2= 0; // length of the second part of first data payload + byte saved_byte; // for saving byte 255 in which data length is stored + + if (m_round == 1 && data.len() > 254) + { + len2= data.len() - 254; + DBUG_PRINT("info", ("Splitting first packet of length %lu" + ", %lu bytes will be sent in a second part", + data.len(), len2)); + /* + Store in byte 255 the number of 512b blocks that are needed to + keep all the data. + */ + unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0); + DBUG_ASSERT(block_count < (unsigned)0x100); + saved_byte= data[254]; + data[254] = block_count; + + data.trim(255); } - return m_output.as_blob(); + DBUG_PRINT("dump", ("Sending the following data")); + DBUG_DUMP("dump", data.ptr(), data.len()); + int ret= m_con.write(data); + + if (ret) + return ret; + + // Write second part if it is present. + if (len2) + { + data[254]= saved_byte; + Blob data2(data.ptr() + 254, len2); + DBUG_PRINT("info", ("Sending second part of data")); + DBUG_DUMP("info", data2.ptr(), data2.len()); + ret= m_con.write(data2); + } + + return ret; } @@ -139,12 +200,66 @@ Blob Handshake_client::first_packet() empty blob is returned and @c is_complete() is @c true. In case of error an empty blob is returned and @c error() gives non-zero error code. + When invoked for the first time (in the first round of the handshake) + there is no data from the server (data blob is null) and the intial + packet is generated without an input. + @return Data to be sent to the server next or null blob if no more data needs to be exchanged or in case of error. */ Blob Handshake_client::process_data(const Blob &data) { +#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB) + /* + Code for testing the logic for sending the first client payload. + + A fake data of length given by environment variable TEST_PACKET_LENGTH + (or default 255 bytes) is sent to the server. First 2 bytes of the + payload contain its total length (LSB first). The length of test data + is limited to 2048 bytes. + + Upon receiving test data, server will check that data is correct and + refuse connection. If server detects data errors it will crash on + assertion. + + This code is executed if debug flag "winauth_first_packet_test" is + set, e.g. using client option: + + --debug="d,winauth_first_packet_test" + + The same debug flag must be enabled in the server, e.g. using + statement: + + SET GLOBAL debug= '+d,winauth_first_packet_test'; + */ + + static byte test_buf[2048]; + + if (m_round == 1 + && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false)) + { + const char *env= getenv("TEST_PACKET_LENGTH"); + size_t len= env ? atoi(env) : 0; + if (!len) + len= 255; + if (len > sizeof(test_buf)) + len= sizeof(test_buf); + + // Store data length in first 2 bytes. + byte *ptr= test_buf; + *ptr++= len & 0xFF; + *ptr++= len >> 8; + + // Fill remaining bytes with known values. + for (byte b= 0; ptr < test_buf + len; ++ptr, ++b) + *ptr= b; + + return Blob(test_buf, len); + }; + +#endif + Security_buffer input(data); SECURITY_STATUS ret; @@ -152,12 +267,12 @@ Blob Handshake_client::process_data(const Blob &data) ret= InitializeSecurityContextW( &m_cred, - &m_sctx, // partial context + m_round == 1 ? NULL : &m_sctx, // partial context m_service_name, // service name ASC_REQ_ALLOCATE_MEMORY, // requested attributes 0, // reserved SECURITY_NETWORK_DREP, // data representation - &input, // input data + m_round == 1 ? NULL : &input, // input data 0, // reserved &m_sctx, // context &m_output, // output data @@ -198,6 +313,8 @@ Blob Handshake_client::process_data(const Blob &data) int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { + DBUG_ENTER("win_auth_handshake_client"); + /* Check if we should enable logging. */ @@ -224,62 +341,38 @@ int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) // Read initial packet from server containing service name. - int ret; Blob service_name= con.read(); if (con.error() || service_name.is_null()) { ERROR_LOG(ERROR, ("Error reading initial packet")); - return CR_ERROR; + DBUG_RETURN(CR_ERROR); } DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len())); - // Create authentication handsake context using the given service name. + // Create authentication handshake context using the given service name. - Handshake_client hndshk(service_name[0] ? (char *)service_name.ptr() : NULL, + Handshake_client hndshk(con, + service_name[0] ? (char *)service_name.ptr() : NULL, service_name.len()); if (hndshk.error()) { ERROR_LOG(ERROR, ("Could not create authentication handshake context")); - return CR_ERROR; - } - - /* - The following packet exchange always starts with a packet sent by - the client. Send this first packet now. - */ - - { - Blob packet= hndshk.first_packet(); - if (hndshk.error() || packet.is_null()) - { - ERROR_LOG(ERROR, ("Could not generate first packet")); - return CR_ERROR; - } - DBUG_PRINT("info", ("Sending first packet of length %d", packet.len())); - - ret= con.write(packet); - if (ret) - { - ERROR_LOG(ERROR, ("Error writing first packet")); - return CR_ERROR; - } - DBUG_PRINT("info", ("First packet sent")); + DBUG_RETURN(CR_ERROR); } DBUG_ASSERT(!hndshk.error()); /* - If handshake is not yet complete and client expects a reply, - read and process packets from server until handshake is complete. + Read and process packets from server until handshake is complete. + Note that the first read from server is dummy + (see Handshake_client::read_packet()) as we already have read the + first packet to establish service name. */ - if (!hndshk.is_complete()) - { - if (hndshk.packet_processing_loop(con)) - return CR_ERROR; - } + if (hndshk.packet_processing_loop()) + DBUG_RETURN(CR_ERROR); DBUG_ASSERT(!hndshk.error() && hndshk.is_complete()); - return CR_OK; + DBUG_RETURN(CR_OK); } From 3980482fd0742ea5eee2e8ffbc7ab213c79d971f Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 29 Apr 2011 09:48:26 +0200 Subject: [PATCH 49/57] removed dead obsolete code --- sql/ha_partition.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 4883e0a0571..d09181822ee 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3967,12 +3967,6 @@ void ha_partition::position(const uchar *record) if (pad_length) memset((ref + PARTITION_BYTES_IN_POS + file->ref_length), 0, pad_length); -#ifdef SUPPORTING_PARTITION_OVER_DIFFERENT_ENGINES -#ifdef HAVE_purify - bzero(ref + PARTITION_BYTES_IN_POS + ref_length, - max_ref_length-ref_length); -#endif /* HAVE_purify */ -#endif DBUG_VOID_RETURN; } From 00c239558e8266dca9065306fae301e4c550d467 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 29 Apr 2011 11:33:17 +0300 Subject: [PATCH 50/57] Addendum to the fix for bug #12325444 Removed STDCALL from the function definition. --- sql-common/client_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c index 47409e34d9a..382f14c6b3d 100644 --- a/sql-common/client_plugin.c +++ b/sql-common/client_plugin.c @@ -460,7 +460,7 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) /* see for a full description */ -int STDCALL mysql_plugin_options(struct st_mysql_client_plugin *plugin, +int mysql_plugin_options(struct st_mysql_client_plugin *plugin, const char *option, const void *value) { From 2e0e518f3901eb9647e08fbc84dc187e7647e3af Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Fri, 29 Apr 2011 14:04:28 +0300 Subject: [PATCH 51/57] Sync 5.1 .inc file with 5.5 due to a missing changeset Add extra codes to wait_until_disconnected.inc that are present in 5.5, but not in 5.1. The missing codes cause innodb_bug59641 to fail in 5.1 on Windows PB2 runs. The addition of those codes in 5.5 was done in luis.soares@sun.com-20090930233215-aup3kxy4j6ltvjfp --- mysql-test/include/wait_until_disconnected.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/include/wait_until_disconnected.inc b/mysql-test/include/wait_until_disconnected.inc index a4362e52d01..8a989becc18 100644 --- a/mysql-test/include/wait_until_disconnected.inc +++ b/mysql-test/include/wait_until_disconnected.inc @@ -7,7 +7,7 @@ let $counter= 500; let $mysql_errno= 0; while (!$mysql_errno) { - --error 0,1053,2002,2006,2013 + --error 0,1040,1053,2002,2003,2006,2013 show status; dec $counter; From 39f71a296a5e2afacbedb556672190e7d0400520 Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Fri, 29 Apr 2011 18:52:46 +0530 Subject: [PATCH 52/57] Bug#11757855 - 49967: built-in libedit doesn't read .editrc on linux. MySQL client when build with libedit support ignores .editrc at startup. The reason for this regression was the incluison of a safety check, issetugid(), which is not available on some linux platforms. Fixed by adding an equivalent check for platforms which have get[e][u|g]id() set of functions. --- cmd-line-utils/libedit/el.c | 21 ++++++++++++++++----- configure.in | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cmd-line-utils/libedit/el.c b/cmd-line-utils/libedit/el.c index d99946eb68f..c7f8386773d 100644 --- a/cmd-line-utils/libedit/el.c +++ b/cmd-line-utils/libedit/el.c @@ -478,7 +478,13 @@ el_source(EditLine *el, const char *fname) fp = NULL; if (fname == NULL) { -#ifdef HAVE_ISSETUGID +/* XXXMYSQL: Bug#49967 */ +#if defined(HAVE_GETUID) && defined(HAVE_GETEUID) && \ + defined(HAVE_GETGID) && defined(HAVE_GETEGID) +#define HAVE_IDENTITY_FUNCS 1 +#endif + +#if (defined(HAVE_ISSETUGID) || defined(HAVE_IDENTITY_FUNCS)) static const char elpath[] = "/.editrc"; /* XXXMYSQL: Portability fix (for which platforms?) */ #ifdef MAXPATHLEN @@ -486,9 +492,13 @@ el_source(EditLine *el, const char *fname) #else char path[4096]; #endif - +#ifdef HAVE_ISSETUGID if (issetugid()) return (-1); +#elif defined(HAVE_IDENTITY_FUNCS) + if (getuid() != geteuid() || getgid() != getegid()) + return (-1); +#endif if ((ptr = getenv("HOME")) == NULL) return (-1); if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) @@ -498,9 +508,10 @@ el_source(EditLine *el, const char *fname) fname = path; #else /* - * If issetugid() is missing, always return an error, in order - * to keep from inadvertently opening up the user to a security - * hole. + * If issetugid() or the above mentioned get[e][u|g]id() + * functions are missing, always return an error, in order + * to keep from inadvertently opening up the user to a + * security hole. */ return (-1); #endif diff --git a/configure.in b/configure.in index 5bd823ab879..8ba208b1ef5 100644 --- a/configure.in +++ b/configure.in @@ -1963,7 +1963,7 @@ AC_CHECK_HEADER(vis.h, [AC_DEFINE([HAVE_VIS_H], [1],[Found vis.h and the strvis() function])])]) AC_CHECK_FUNCS(strlcat strlcpy) -AC_CHECK_FUNCS(issetugid) +AC_CHECK_FUNCS(issetugid getuid geteuid getgid getegid) AC_CHECK_FUNCS(fgetln) AC_CHECK_FUNCS(getline flockfile) From 3f2a18026c4a6b25dd1708748024b40cf777a864 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 29 Apr 2011 15:28:52 +0200 Subject: [PATCH 53/57] Bug #11765749 58745: MTR SPENDS LONG TIME CHECKING FOR WARNINGS Added code to look for repetitions and only repeat warnings once Reduced time spent in check-warnings by almost 20% for full test suite --- mysql-test/mysql-test-run.pl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c288a16d233..5c7b4fc7d02 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4116,6 +4116,9 @@ sub extract_warning_lines ($$) { ); my $skip_valgrind= 0; + my $last_pat= ""; + my $num_rep= 0; + foreach my $line ( @lines ) { if ($opt_valgrind_mysqld) { @@ -4130,11 +4133,29 @@ sub extract_warning_lines ($$) { { if ( $line =~ /$pat/ ) { - print $Fwarn $line; + # Remove initial timestamp and look for consecutive identical lines + my $line_pat= $line; + $line_pat =~ s/^[0-9: ]*//; + if ($line_pat eq $last_pat) { + $num_rep++; + } else { + # Previous line had been repeated, report that first + if ($num_rep) { + print $Fwarn ".... repeated $num_rep times: $last_pat"; + $num_rep= 0; + } + $last_pat= $line_pat; + print $Fwarn $line; + } last; } } } + # Catch the case of last warning being repeated + if ($num_rep) { + print $Fwarn ".... repeated $num_rep times: $last_pat"; + } + $Fwarn = undef; # Close file } From 04cc55406428f4e90bbbe72e7702dba4f4bb564a Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 29 Apr 2011 15:35:02 +0200 Subject: [PATCH 54/57] Bug #11759877 52223: TEST "PLUGIN_DIR_BASIC" DOES NOT SUPPORT RPM BUILD (TEST) DIRECTORY STRUC Make a qualified guess: if $basedir/lib does not exist but $basedir/lib64 does, then the latter is the proper value for $MYSQL_LIBDIR Tested on the RPMS of 5.5.12. --- mysql-test/mysql-test-run.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5c7b4fc7d02..f3f1181562b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2220,7 +2220,12 @@ sub environment_setup { $ENV{'DEFAULT_MASTER_PORT'}= $mysqld_variables{'port'}; $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; + # Used for guessing default plugin dir, we can't really know for sure $ENV{'MYSQL_LIBDIR'}= "$basedir/lib"; + # Override if this does not exist, but lib64 does (best effort) + if (! -d "$basedir/lib" && -d "$basedir/lib64") { + $ENV{'MYSQL_LIBDIR'}= "$basedir/lib64"; + } $ENV{'MYSQL_BINDIR'}= "$bindir"; $ENV{'MYSQL_SHAREDIR'}= $path_language; $ENV{'MYSQL_CHARSETSDIR'}= $path_charsetsdir; From 2b5b3e1ea70c4653b17fdfbe5fe17fc213a69a99 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Fri, 29 Apr 2011 18:48:23 -0300 Subject: [PATCH 55/57] FIONREAD is located in sys/filio.h on Solaris. --- config.h.cmake | 1 + configure.cmake | 1 + vio/viosocket.c | 8 ++++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index fdff4222417..95193730008 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -125,6 +125,7 @@ #cmakedefine FIONREAD_IN_SYS_IOCTL 1 #cmakedefine GWINSZ_IN_SYS_IOCTL 1 #cmakedefine TIOCSTAT_IN_SYS_IOCTL 1 +#cmakedefine FIONREAD_IN_SYS_FILIO 1 /* Functions we may want to use. */ #cmakedefine HAVE_AIOWAIT 1 diff --git a/configure.cmake b/configure.cmake index c0bc1261d15..46714a0a0e7 100644 --- a/configure.cmake +++ b/configure.cmake @@ -487,6 +487,7 @@ CHECK_SYMBOL_EXISTS(getpagesize "unistd.h" HAVE_GETPAGESIZE) CHECK_SYMBOL_EXISTS(TIOCGWINSZ "sys/ioctl.h" GWINSZ_IN_SYS_IOCTL) CHECK_SYMBOL_EXISTS(FIONREAD "sys/ioctl.h" FIONREAD_IN_SYS_IOCTL) CHECK_SYMBOL_EXISTS(TIOCSTAT "sys/ioctl.h" TIOCSTAT_IN_SYS_IOCTL) +CHECK_SYMBOL_EXISTS(FIONREAD "sys/filio.h" FIONREAD_IN_SYS_FILIO) CHECK_SYMBOL_EXISTS(gettimeofday "sys/time.h" HAVE_GETTIMEOFDAY) CHECK_SYMBOL_EXISTS(finite "math.h" HAVE_FINITE_IN_MATH_H) diff --git a/vio/viosocket.c b/vio/viosocket.c index 0f3a32d62ae..9ad7134312c 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -22,6 +22,10 @@ #include "vio_priv.h" +#ifdef FIONREAD_IN_SYS_FILIO +# include +#endif + int vio_errno(Vio *vio __attribute__((unused))) { return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ @@ -583,13 +587,13 @@ static my_bool socket_poll_read(my_socket sd, uint timeout) static my_bool socket_peek_read(Vio *vio, uint *bytes) { -#ifdef __WIN__ +#if defined(_WIN32) int len; if (ioctlsocket(vio->sd, FIONREAD, &len)) return TRUE; *bytes= len; return FALSE; -#elif FIONREAD_IN_SYS_IOCTL +#elif defined(FIONREAD_IN_SYS_IOCTL) || defined(FIONREAD_IN_SYS_FILIO) int len; if (ioctl(vio->sd, FIONREAD, &len) < 0) return TRUE; From 58ebf8950115aad9d8fa3a8536fc9f9b0982b851 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Mon, 2 May 2011 16:38:25 +0200 Subject: [PATCH 56/57] Marked test experimental. --- mysql-test/collections/default.experimental | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/collections/default.experimental b/mysql-test/collections/default.experimental index c741e4a869c..5ecc8ef8c64 100644 --- a/mysql-test/collections/default.experimental +++ b/mysql-test/collections/default.experimental @@ -17,6 +17,7 @@ main.wait_timeout @solaris # Bug#51244 2010-04-26 alik wait_timeou rpl.rpl_heartbeat_basic # BUG#12403008 2011-04-27 sven fails sporadically rpl.rpl_innodb_bug28430 # Bug#46029 +rpl.rpl_show_slave_hosts # BUG#12416700 2011-05-02 sven fails sporadically sys_vars.max_sp_recursion_depth_func @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun sys_vars.plugin_dir_basic # Bug#52223 2010-11-24 alik Test "plugin_dir_basic" does not support RPM build (test) directory structure From 4f666e8e8130fe5bac078d4c5715c21fd948aa5f Mon Sep 17 00:00:00 2001 From: Kent Boortz Date: Tue, 3 May 2011 16:02:31 +0200 Subject: [PATCH 57/57] Remove soft links in the build directory, not the source directory (Bug#43312) --- client/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/Makefile.am b/client/Makefile.am index ccd0d8aada0..b455a34a58b 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -116,10 +116,10 @@ link_sources: @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ done; \ for f in $(strings_src) ; do \ - rm -f $(srcdir)/$$f; \ + rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ done; \ - rm -f $(srcdir)/my_user.c; \ + rm -f my_user.c; \ @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c; echo timestamp > link_sources;