diff options
author | Alexander Nozdrin <alexander.nozdrin@oracle.com> | 2011-05-06 17:39:20 +0400 |
---|---|---|
committer | Alexander Nozdrin <alexander.nozdrin@oracle.com> | 2011-05-06 17:39:20 +0400 |
commit | cd501675d8b88698d2a4a0d24ec638ed7e78a95b (patch) | |
tree | de9a20a6c8fddf18d59a1dd66965c327e19b2505 /libmysql | |
parent | bc4095643b955e76f349daaf7472f33da7638597 (diff) | |
download | mariadb-git-cd501675d8b88698d2a4a0d24ec638ed7e78a95b.tar.gz |
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.
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/libmysql.c | 75 |
1 files changed, 54 insertions, 21 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index f802387cf9a..ec48720a2f5 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -94,6 +94,11 @@ sig_handler my_pipe_sig_handler(int sig); static my_bool mysql_client_init= 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 @@ -1480,11 +1485,16 @@ mysql_stmt_init(MYSQL *mysql) MYSQL_STMT *stmt; 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)))) { 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); @@ -1499,6 +1509,8 @@ mysql_stmt_init(MYSQL *mysql) strmov(stmt->sqlstate, not_error_sqlstate); /* The rest of statement members was bzeroed inside malloc */ + init_alloc_root(&stmt->extension->fields_mem_root, 2048, 0); + 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->param_count= stmt->field_count= 0; free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); 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) { 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; - 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 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) * stmt->field_count)) || - !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, + !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root, sizeof(MYSQL_BIND) * stmt->field_count))) { @@ -1658,18 +1671,36 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt) field && fields < end; fields++, field++) { *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); - field->db= strmake_root(alloc, fields->db, fields->db_length); - field->table= strmake_root(alloc, fields->table, fields->table_length); - field->org_table= strmake_root(alloc, fields->org_table, + field->db= strmake_root(fields_mem_root, + fields->db, + 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); - field->name= strmake_root(alloc, fields->name, fields->name_length); - field->org_name= strmake_root(alloc, fields->org_name, + field->name= strmake_root(fields_mem_root, + fields->name, + fields->name_length); + field->org_name= strmake_root(fields_mem_root, + fields->org_name, fields->org_name_length); - field->def= fields->def ? strmake_root(alloc, fields->def, - fields->def_length) : 0; - field->def_length= field->def ? fields->def_length : 0; + if (fields->def) + { + 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->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 on prepare stage. Read it now. */ + + stmt->field_count= stmt->mysql->field_count; + alloc_stmt_fields(stmt); } else @@ -2404,7 +2438,7 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt) previous branch always works. TODO: send metadata only when it's really necessary and add a warning 'Metadata changed' when it's sent twice. - */ + */ update_stmt_fields(stmt); } } @@ -4605,6 +4639,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) free_root(&stmt->result.alloc, MYF(0)); free_root(&stmt->mem_root, MYF(0)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); if (mysql) { @@ -4639,6 +4674,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) } } + my_free(stmt->extension); my_free(stmt); DBUG_RETURN(test(rc)); @@ -4805,16 +4841,13 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) stmt->state= MYSQL_STMT_EXECUTE_DONE; stmt->bind_result_done= FALSE; + stmt->field_count= mysql->field_count; if (mysql->field_count) { alloc_stmt_fields(stmt); prepare_to_fetch_result(stmt); } - else - { - stmt->field_count= mysql->field_count; - } DBUG_RETURN(0); } |