Commit 954a16c6 authored by sergefp@mysql.com's avatar sergefp@mysql.com
Browse files

BUG#27732 "Possible memory leak with index_merge"

The bug was that handler::clone/handler::ha_open() call caused allocation of 
cloned_copy->ref on the handler->table->mem_root. The allocated memory could not 
be reclaimed until the table is flushed, so it was possible to exhaust memory by 
repeatedly running index_merge queries without doing table flushes.  

The fix:
- make handler::clone() allocate new_handler->ref on the passed mem_root 
- make handler::ha_open() not allocate this->ref if it has already been allocated
There is no testcase as it is not possible to check small leaks from testsuite.
parent 368a8bb4
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -1381,6 +1381,13 @@ int ha_delete_table(THD *thd, enum db_type table_type, const char *path,
handler *handler::clone(MEM_ROOT *mem_root)
{
  handler *new_handler= get_new_handler(table, mem_root, table->s->db_type);
  /*
    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 
    when the clone handler object is destroyed.
  */
  if (!(new_handler->ref= (byte*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2)))
    return NULL;
  if (new_handler && !new_handler->ha_open(table->s->path, table->db_stat,
                                           HA_OPEN_IGNORE_IF_LOCKED))
    return new_handler;
@@ -1420,8 +1427,9 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
    (void) extra(HA_EXTRA_NO_READCHECK);	// Not needed in SQL

    DBUG_ASSERT(alloc_root_inited(&table->mem_root));

    if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2)))
    /* ref is already allocated for us if we're called from handler::clone() */
    if (!ref && !(ref= (byte*) alloc_root(&table->mem_root, 
                                          ALIGN_SIZE(ref_length)*2)))
    {
      close();
      error=HA_ERR_OUT_OF_MEM;