From a6b70da9a34bafd55b14daa4c81f3aecc10ae815 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 25 Mar 2011 12:36:02 +0100 Subject: [PATCH 01/15] 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 08bf3ddde99f6a6014efe9ecf10a976bdd741a8d Mon Sep 17 00:00:00 2001 From: Guilhem Bichot Date: Thu, 7 Apr 2011 15:09:19 +0200 Subject: [PATCH 02/15] 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 7b4cafe9b9357cfc6f080c55f1bafab5c01d7cf1 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Mon, 18 Apr 2011 10:44:41 +0200 Subject: [PATCH 03/15] 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 5038060b3bd21c08a1129092e2a62f3f7929a953 Mon Sep 17 00:00:00 2001 From: Sven Sandberg Date: Mon, 18 Apr 2011 14:42:14 +0200 Subject: [PATCH 04/15] 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 410ee93cdea97dbabb114c231fc430aee5f62059 Mon Sep 17 00:00:00 2001 From: Serge Kozlov Date: Mon, 18 Apr 2011 23:59:15 +0400 Subject: [PATCH 05/15] 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 71bb332aa3f9e87baa055434e126a87f8ec70f29 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 20 Apr 2011 11:39:20 +0400 Subject: [PATCH 06/15] 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 73ecffdb2fae16b1831f436ced30231740d7c673 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 20 Apr 2011 11:32:28 +0200 Subject: [PATCH 07/15] 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 f6641998bec64b18abf75df94ff2dad8cd3ed603 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Wed, 20 Apr 2011 17:52:33 +0200 Subject: [PATCH 08/15] 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 76f37a023563755c840c3d51c31aa0a5a6b3996e Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Fri, 22 Apr 2011 11:20:55 +0400 Subject: [PATCH 09/15] 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 10/15] 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 11/15] 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 735bab72c51e0ff4ccf5fb688826e87931ee8fe2 Mon Sep 17 00:00:00 2001 From: Sergey Glukhov Date: Wed, 27 Apr 2011 11:35:57 +0400 Subject: [PATCH 12/15] 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 c08c4e1fd765fff8ae76b2e61b0e04f550228660 Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Wed, 27 Apr 2011 17:24:10 +0530 Subject: [PATCH 13/15] 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 aefbea7d0b62c0ddfb3f9a6b46c6154e472a2688 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Wed, 27 Apr 2011 17:51:06 +0200 Subject: [PATCH 14/15] 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 3980482fd0742ea5eee2e8ffbc7ab213c79d971f Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 29 Apr 2011 09:48:26 +0200 Subject: [PATCH 15/15] 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; }