Patch for Bug#12374486 - SEVERE MEMORY LEAK IN PREPARED STATEMENTS
THAT CALL STORED PROCEDURES. The bug was introduced by WL#4435. The problem was that if a stored procedure generated a few result sets with different set of columns, a new memory would be allocated after every EXECUTE for every result set. The fix is to introduce a new memory root in scope of MYSQL_STMT, and to store result-set metadata in that memory root.
This commit is contained in:
parent
bc4095643b
commit
cd501675d8
@ -573,6 +573,8 @@ typedef struct st_mysql_bind
|
|||||||
} MYSQL_BIND;
|
} MYSQL_BIND;
|
||||||
|
|
||||||
|
|
||||||
|
struct st_mysql_stmt_extension;
|
||||||
|
|
||||||
/* statement handler */
|
/* statement handler */
|
||||||
typedef struct st_mysql_stmt
|
typedef struct st_mysql_stmt
|
||||||
{
|
{
|
||||||
@ -618,7 +620,7 @@ typedef struct st_mysql_stmt
|
|||||||
metadata fields when doing mysql_stmt_store_result.
|
metadata fields when doing mysql_stmt_store_result.
|
||||||
*/
|
*/
|
||||||
my_bool update_max_length;
|
my_bool update_max_length;
|
||||||
void *extension;
|
struct st_mysql_stmt_extension *extension;
|
||||||
} MYSQL_STMT;
|
} MYSQL_STMT;
|
||||||
|
|
||||||
enum enum_stmt_attr_type
|
enum enum_stmt_attr_type
|
||||||
|
@ -512,6 +512,7 @@ typedef struct st_mysql_bind
|
|||||||
my_bool is_null_value;
|
my_bool is_null_value;
|
||||||
void *extension;
|
void *extension;
|
||||||
} MYSQL_BIND;
|
} MYSQL_BIND;
|
||||||
|
struct st_mysql_stmt_extension;
|
||||||
typedef struct st_mysql_stmt
|
typedef struct st_mysql_stmt
|
||||||
{
|
{
|
||||||
MEM_ROOT mem_root;
|
MEM_ROOT mem_root;
|
||||||
@ -541,7 +542,7 @@ typedef struct st_mysql_stmt
|
|||||||
unsigned char bind_result_done;
|
unsigned char bind_result_done;
|
||||||
my_bool unbuffered_fetch_cancelled;
|
my_bool unbuffered_fetch_cancelled;
|
||||||
my_bool update_max_length;
|
my_bool update_max_length;
|
||||||
void *extension;
|
struct st_mysql_stmt_extension *extension;
|
||||||
} MYSQL_STMT;
|
} MYSQL_STMT;
|
||||||
enum enum_stmt_attr_type
|
enum enum_stmt_attr_type
|
||||||
{
|
{
|
||||||
|
@ -94,6 +94,11 @@ sig_handler my_pipe_sig_handler(int sig);
|
|||||||
static my_bool mysql_client_init= 0;
|
static my_bool mysql_client_init= 0;
|
||||||
static my_bool org_my_init_done= 0;
|
static my_bool org_my_init_done= 0;
|
||||||
|
|
||||||
|
typedef struct st_mysql_stmt_extension
|
||||||
|
{
|
||||||
|
MEM_ROOT fields_mem_root;
|
||||||
|
} MYSQL_STMT_EXT;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialize the MySQL client library
|
Initialize the MySQL client library
|
||||||
@ -1480,11 +1485,16 @@ mysql_stmt_init(MYSQL *mysql)
|
|||||||
MYSQL_STMT *stmt;
|
MYSQL_STMT *stmt;
|
||||||
DBUG_ENTER("mysql_stmt_init");
|
DBUG_ENTER("mysql_stmt_init");
|
||||||
|
|
||||||
if (!(stmt= (MYSQL_STMT *) my_malloc(sizeof(MYSQL_STMT),
|
if (!(stmt=
|
||||||
|
(MYSQL_STMT *) my_malloc(sizeof (MYSQL_STMT),
|
||||||
|
MYF(MY_WME | MY_ZEROFILL))) ||
|
||||||
|
!(stmt->extension=
|
||||||
|
(MYSQL_STMT_EXT *) my_malloc(sizeof (MYSQL_STMT_EXT),
|
||||||
MYF(MY_WME | MY_ZEROFILL))))
|
MYF(MY_WME | MY_ZEROFILL))))
|
||||||
{
|
{
|
||||||
set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
|
set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
|
||||||
DBUG_RETURN(0);
|
my_free(stmt);
|
||||||
|
DBUG_RETURN(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_alloc_root(&stmt->mem_root, 2048, 2048);
|
init_alloc_root(&stmt->mem_root, 2048, 2048);
|
||||||
@ -1499,6 +1509,8 @@ mysql_stmt_init(MYSQL *mysql)
|
|||||||
strmov(stmt->sqlstate, not_error_sqlstate);
|
strmov(stmt->sqlstate, not_error_sqlstate);
|
||||||
/* The rest of statement members was bzeroed inside malloc */
|
/* The rest of statement members was bzeroed inside malloc */
|
||||||
|
|
||||||
|
init_alloc_root(&stmt->extension->fields_mem_root, 2048, 0);
|
||||||
|
|
||||||
DBUG_RETURN(stmt);
|
DBUG_RETURN(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1571,6 +1583,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
|
|||||||
stmt->bind_param_done= stmt->bind_result_done= FALSE;
|
stmt->bind_param_done= stmt->bind_result_done= FALSE;
|
||||||
stmt->param_count= stmt->field_count= 0;
|
stmt->param_count= stmt->field_count= 0;
|
||||||
free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
|
free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
|
||||||
|
free_root(&stmt->extension->fields_mem_root, MYF(0));
|
||||||
|
|
||||||
int4store(buff, stmt->stmt_id);
|
int4store(buff, stmt->stmt_id);
|
||||||
|
|
||||||
@ -1631,21 +1644,21 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
|
|||||||
static void alloc_stmt_fields(MYSQL_STMT *stmt)
|
static void alloc_stmt_fields(MYSQL_STMT *stmt)
|
||||||
{
|
{
|
||||||
MYSQL_FIELD *fields, *field, *end;
|
MYSQL_FIELD *fields, *field, *end;
|
||||||
MEM_ROOT *alloc= &stmt->mem_root;
|
MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root;
|
||||||
MYSQL *mysql= stmt->mysql;
|
MYSQL *mysql= stmt->mysql;
|
||||||
|
|
||||||
DBUG_ASSERT(mysql->field_count);
|
DBUG_ASSERT(stmt->field_count);
|
||||||
|
|
||||||
stmt->field_count= mysql->field_count;
|
free_root(fields_mem_root, MYF(0));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the field information for non-select statements
|
Get the field information for non-select statements
|
||||||
like SHOW and DESCRIBE commands
|
like SHOW and DESCRIBE commands
|
||||||
*/
|
*/
|
||||||
if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc,
|
if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root,
|
||||||
sizeof(MYSQL_FIELD) *
|
sizeof(MYSQL_FIELD) *
|
||||||
stmt->field_count)) ||
|
stmt->field_count)) ||
|
||||||
!(stmt->bind= (MYSQL_BIND *) alloc_root(alloc,
|
!(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root,
|
||||||
sizeof(MYSQL_BIND) *
|
sizeof(MYSQL_BIND) *
|
||||||
stmt->field_count)))
|
stmt->field_count)))
|
||||||
{
|
{
|
||||||
@ -1658,18 +1671,36 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt)
|
|||||||
field && fields < end; fields++, field++)
|
field && fields < end; fields++, field++)
|
||||||
{
|
{
|
||||||
*field= *fields; /* To copy all numeric parts. */
|
*field= *fields; /* To copy all numeric parts. */
|
||||||
field->catalog= strmake_root(alloc, fields->catalog,
|
field->catalog= strmake_root(fields_mem_root,
|
||||||
|
fields->catalog,
|
||||||
fields->catalog_length);
|
fields->catalog_length);
|
||||||
field->db= strmake_root(alloc, fields->db, fields->db_length);
|
field->db= strmake_root(fields_mem_root,
|
||||||
field->table= strmake_root(alloc, fields->table, fields->table_length);
|
fields->db,
|
||||||
field->org_table= strmake_root(alloc, fields->org_table,
|
fields->db_length);
|
||||||
|
field->table= strmake_root(fields_mem_root,
|
||||||
|
fields->table,
|
||||||
|
fields->table_length);
|
||||||
|
field->org_table= strmake_root(fields_mem_root,
|
||||||
|
fields->org_table,
|
||||||
fields->org_table_length);
|
fields->org_table_length);
|
||||||
field->name= strmake_root(alloc, fields->name, fields->name_length);
|
field->name= strmake_root(fields_mem_root,
|
||||||
field->org_name= strmake_root(alloc, fields->org_name,
|
fields->name,
|
||||||
|
fields->name_length);
|
||||||
|
field->org_name= strmake_root(fields_mem_root,
|
||||||
|
fields->org_name,
|
||||||
fields->org_name_length);
|
fields->org_name_length);
|
||||||
field->def= fields->def ? strmake_root(alloc, fields->def,
|
if (fields->def)
|
||||||
fields->def_length) : 0;
|
{
|
||||||
field->def_length= field->def ? fields->def_length : 0;
|
field->def= strmake_root(fields_mem_root,
|
||||||
|
fields->def,
|
||||||
|
fields->def_length);
|
||||||
|
field->def_length= fields->def_length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
field->def= NULL;
|
||||||
|
field->def_length= 0;
|
||||||
|
}
|
||||||
field->extension= 0; /* Avoid dangling links. */
|
field->extension= 0; /* Avoid dangling links. */
|
||||||
field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */
|
field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */
|
||||||
}
|
}
|
||||||
@ -2387,6 +2418,9 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt)
|
|||||||
prepared statements can't send result set metadata for these queries
|
prepared statements can't send result set metadata for these queries
|
||||||
on prepare stage. Read it now.
|
on prepare stage. Read it now.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
stmt->field_count= stmt->mysql->field_count;
|
||||||
|
|
||||||
alloc_stmt_fields(stmt);
|
alloc_stmt_fields(stmt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -4605,6 +4639,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
|
|||||||
|
|
||||||
free_root(&stmt->result.alloc, MYF(0));
|
free_root(&stmt->result.alloc, MYF(0));
|
||||||
free_root(&stmt->mem_root, MYF(0));
|
free_root(&stmt->mem_root, MYF(0));
|
||||||
|
free_root(&stmt->extension->fields_mem_root, MYF(0));
|
||||||
|
|
||||||
if (mysql)
|
if (mysql)
|
||||||
{
|
{
|
||||||
@ -4639,6 +4674,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my_free(stmt->extension);
|
||||||
my_free(stmt);
|
my_free(stmt);
|
||||||
|
|
||||||
DBUG_RETURN(test(rc));
|
DBUG_RETURN(test(rc));
|
||||||
@ -4805,16 +4841,13 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
|
|||||||
|
|
||||||
stmt->state= MYSQL_STMT_EXECUTE_DONE;
|
stmt->state= MYSQL_STMT_EXECUTE_DONE;
|
||||||
stmt->bind_result_done= FALSE;
|
stmt->bind_result_done= FALSE;
|
||||||
|
stmt->field_count= mysql->field_count;
|
||||||
|
|
||||||
if (mysql->field_count)
|
if (mysql->field_count)
|
||||||
{
|
{
|
||||||
alloc_stmt_fields(stmt);
|
alloc_stmt_fields(stmt);
|
||||||
prepare_to_fetch_result(stmt);
|
prepare_to_fetch_result(stmt);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
stmt->field_count= mysql->field_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user