From 663ac86f6cd55b4a22a91c312ccc17fa4fdf158c Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Jun 2003 18:15:17 +0200 Subject: [PATCH] sp_head now has its own mem_root (WL#961). Also fixed some difficult memory leaks that became apparent in this task. sql/sp.cc: sp_head now has its own mem_root. sql/sp_head.cc: sp_head now has its own mem_root. Also fixed some difficult memory leaks. sql/sp_head.h: sp_head now has its own mem_root. sql/sql_lex.h: Fixed some memory leaks in sp_head. Need to keep track on used lex:es. sql/sql_parse.cc: sp_head now has its own mem_root. Fixed SP memory leaks. sql/sql_prepare.cc: Fixed SP memory leaks. sql/sql_yacc.yy: sp_head now has its own mem_root. --- sql/sp.cc | 5 ++- sql/sp_head.cc | 91 ++++++++++++++++++++++++++++++++++++++-------- sql/sp_head.h | 41 ++++++++++++++++++--- sql/sql_lex.h | 1 + sql/sql_parse.cc | 34 +++++++++++------ sql/sql_prepare.cc | 12 +++++- sql/sql_yacc.yy | 7 +++- 7 files changed, 155 insertions(+), 36 deletions(-) diff --git a/sql/sp.cc b/sql/sp.cc index f44335a6c75..8fd0273fcce 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -151,7 +151,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) { if (oldlex != thd->lex) thd->lex->sphead->restore_lex(thd); - thd->lex->sphead->destroy(); + delete thd->lex->sphead; + thd->lex= NULL; } ret= SP_PARSE_ERROR; } @@ -444,7 +445,7 @@ sp_clear_function_cache(THD *thd) sp_head *sp; while ((sp= li++)) - sp->destroy(); + delete sp; thd->spfuns.empty(); } diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 04a7ef8aa09..51fc7b1ef94 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -81,7 +81,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type) String *s= it->val_str(&tmp); DBUG_PRINT("info",("default result: %*s",s->length(),s->c_ptr_quick())); - it= new Item_string(sql_strmake(s->c_ptr_quick(), s->length()), + it= new Item_string(thd->strmake(s->c_ptr_quick(), s->length()), s->length(), it->charset()); break; } @@ -91,6 +91,34 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type) DBUG_RETURN(it); } +void * +sp_head::operator new(size_t size) +{ + DBUG_ENTER("sp_head::operator new"); + MEM_ROOT own_root; + sp_head *sp; + + bzero((char *)&own_root, sizeof(own_root)); + init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + sp= (sp_head *)alloc_root(&own_root, size); + sp->m_mem_root= own_root; + + DBUG_RETURN(sp); +} + +void +sp_head::operator delete(void *ptr, size_t size) +{ + DBUG_ENTER("sp_head::operator delete"); + MEM_ROOT own_root; + sp_head *sp= (sp_head *)ptr; + + memcpy(&own_root, (const void *)&sp->m_mem_root, sizeof(MEM_ROOT)); + free_root(&own_root, MYF(0)); + + DBUG_VOID_RETURN; +} + sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid) : Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE) { @@ -102,6 +130,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid) m_name.str= name->str; m_defstr.length= lex->end_of_query - lex->buf; m_defstr.str= lex->thd->strmake(dstr, m_defstr.length); + m_free_list= NULL; m_comment.length= 0; m_comment.str= 0; @@ -115,6 +144,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid) m_pcont= lex->spcont; my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_backpatch.empty(); + m_lex.empty(); DBUG_VOID_RETURN; } @@ -142,13 +172,31 @@ sp_head::create(THD *thd) DBUG_RETURN(ret); } +sp_head::~sp_head() +{ + destroy(); + if (m_thd) + restore_thd_mem_root(m_thd); +} + void sp_head::destroy() { DBUG_ENTER("sp_head::destroy"); DBUG_PRINT("info", ("name: %s", m_name.str)); + sp_instr *i; + LEX *lex; + + for (uint ip = 0 ; (i = get_instr(ip)) ; ip++) + delete i; delete_dynamic(&m_instr); m_pcont->destroy(); + free_items(m_free_list); + while ((lex= (LEX *)m_lex.pop())) + { + if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left + delete lex; + } DBUG_VOID_RETURN; } @@ -367,20 +415,22 @@ sp_head::reset_lex(THD *thd) { DBUG_ENTER("sp_head::reset_lex"); LEX *sublex; + LEX *oldlex= thd->lex; - m_lex= thd->lex; + (void)m_lex.push_front(oldlex); thd->lex= sublex= new st_lex; - sublex->yylineno= m_lex->yylineno; + sublex->yylineno= oldlex->yylineno; /* Reset most stuff. The length arguments doesn't matter here. */ - lex_start(thd, m_lex->buf, m_lex->end_of_query - m_lex->ptr); + lex_start(thd, oldlex->buf, oldlex->end_of_query - oldlex->ptr); /* We must reset ptr and end_of_query again */ - sublex->ptr= m_lex->ptr; - sublex->end_of_query= m_lex->end_of_query; - sublex->tok_start= m_lex->tok_start; + sublex->ptr= oldlex->ptr; + sublex->end_of_query= oldlex->end_of_query; + sublex->tok_start= oldlex->tok_start; /* And keep the SP stuff too */ - sublex->sphead= m_lex->sphead; - sublex->spcont= m_lex->spcont; + sublex->sphead= oldlex->sphead; + sublex->spcont= oldlex->spcont; mysql_init_query(thd, true); // Only init lex + sublex->sp_lex_in_use= FALSE; DBUG_VOID_RETURN; } @@ -390,14 +440,18 @@ sp_head::restore_lex(THD *thd) { DBUG_ENTER("sp_head::restore_lex"); LEX *sublex= thd->lex; + LEX *oldlex= (LEX *)m_lex.pop(); + + if (! oldlex) + return; // Nothing to restore // Update some state in the old one first - m_lex->ptr= sublex->ptr; - m_lex->next_state= sublex->next_state; + oldlex->ptr= sublex->ptr; + oldlex->next_state= sublex->next_state; // Collect some data from the sub statement lex. - sp_merge_funs(m_lex, sublex); -#if 0 + sp_merge_funs(oldlex, sublex); +#ifdef NOT_USED_NOW // QQ We're not using this at the moment. if (sublex.sql_command == SQLCOM_CALL) { @@ -438,8 +492,9 @@ sp_head::restore_lex(THD *thd) } } #endif - - thd->lex= m_lex; + if (! sublex->sp_lex_in_use) + delete sublex; + thd->lex= oldlex; DBUG_VOID_RETURN; } @@ -478,6 +533,12 @@ sp_head::backpatch(sp_label_t *lab) // // sp_instr_stmt // +sp_instr_stmt::~sp_instr_stmt() +{ + if (m_lex) + delete m_lex; +} + int sp_instr_stmt::execute(THD *thd, uint *nextp) { diff --git a/sql/sp_head.h b/sql/sp_head.h index f25e141cd18..dc27da5c5b2 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -54,11 +54,19 @@ public: List m_tables; // Used tables. #endif + static void * + operator new(size_t size); + + static void + operator delete(void *ptr, size_t size); + sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid); int create(THD *thd); + virtual ~sp_head(); + // Free memory void destroy(); @@ -87,6 +95,7 @@ public: // Restores lex in 'thd' from our copy, but keeps some status from the // one in 'thd', like ptr, tables, fields, etc. + // If 'delete_lex' is true, we delete the current lex. void restore_lex(THD *thd); @@ -122,10 +131,33 @@ public: m_comment.length= commentlen; m_comment.str= comment; m_suid= suid; - } + } + + inline void reset_thd_mem_root(THD *thd) + { + m_thd_root= thd->mem_root; + thd->mem_root= m_mem_root; + m_free_list= thd->free_list; // Keep the old list + thd->free_list= NULL; // Start a new one + m_thd= thd; + } + + inline void restore_thd_mem_root(THD *thd) + { + Item *flist= m_free_list; // The old list + m_free_list= thd->free_list; // Get the new one + thd->free_list= flist; // Restore the old one + m_mem_root= thd->mem_root; + thd->mem_root= m_thd_root; + m_thd= NULL; + } private: + MEM_ROOT m_mem_root; // My own mem_root + MEM_ROOT m_thd_root; // Temp. store for thd's mem_root + Item *m_free_list; // Where the items go + THD *m_thd; // Set if we have reset mem_root LEX_STRING m_name; LEX_STRING m_defstr; LEX_STRING m_comment; @@ -136,7 +168,7 @@ private: bool m_suid; sp_pcontext *m_pcont; // Parse context - LEX *m_lex; // Temp. store for the other lex + List m_lex; // Temp. store for the other lex DYNAMIC_ARRAY m_instr; // The "instructions" typedef struct { @@ -211,11 +243,10 @@ class sp_instr_stmt : public sp_instr public: sp_instr_stmt(uint ip) - : sp_instr(ip) + : sp_instr(ip), m_lex(NULL) {} - virtual ~sp_instr_stmt() - {} + virtual ~sp_instr_stmt(); virtual int execute(THD *thd, uint *nextp); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 09cbe107ffa..ffb1b2b0df7 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -495,6 +495,7 @@ typedef struct st_lex char *help_arg; SQL_LIST *gorder_list; sp_head *sphead; + bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ sp_pcontext *spcont; List spfuns; /* Called functions */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e434524957e..b29e1734302 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2829,7 +2829,7 @@ mysql_execute_command(THD *thd) sp_head *sph= sp_find_function(thd, &lex->udf.name); if (sph) { - sph->destroy(); // QQ Free memory. Remove this when caching!!! + delete sph; // QQ Free memory. Remove this when caching!!! net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str); goto error; } @@ -3029,18 +3029,22 @@ mysql_execute_command(THD *thd) #endif res= lex->sphead->create(thd); - lex->sphead->destroy(); // QQ Free memory. Remove this when caching!!! - switch (res) { case SP_OK: send_ok(thd); + delete lex->sphead; // QQ Free memory. Remove this when caching!!! + lex->sphead= NULL; break; case SP_WRITE_ROW_FAILED: net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name); + delete lex->sphead; // QQ Free memory. Remove this when caching!!! + lex->sphead= NULL; goto error; default: net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); + delete lex->sphead; // QQ Free memory. Remove this when caching!!! + lex->sphead= NULL; goto error; } break; @@ -3064,7 +3068,7 @@ mysql_execute_command(THD *thd) if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || (res= open_and_lock_tables(thd, tables)))) { - sp->destroy(); // QQ Free memory. Remove this when caching!!! + delete sp; // Free memory. Remove this when caching!!! break; } fix_tables_pointers(lex->all_selects_list); @@ -3083,7 +3087,7 @@ mysql_execute_command(THD *thd) #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; #endif - sp->destroy(); // QQ Free memory. Remove this when caching!!! + delete sp; // QQ Free memory. Remove this when caching!!! goto error; } smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS; @@ -3101,7 +3105,7 @@ mysql_execute_command(THD *thd) thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; } - sp->destroy(); // QQ Free memory. Remove this when caching!!! + delete sp; // QQ Free memory. Remove this when caching!!! if (res == 0) send_ok(thd); @@ -3128,7 +3132,7 @@ mysql_execute_command(THD *thd) { /* QQ This is an no-op right now, since we haven't put the characteristics in yet. */ - sp->destroy(); // QQ Free memory. Remove this when caching!!! + delete sp; // QQ Free memory. Remove this when caching!!! send_ok(thd); } break; @@ -3588,7 +3592,12 @@ mysql_parse(THD *thd, char *inBuf, uint length) { send_error(thd, 0, NullS); if (thd->lex->sphead) - thd->lex->sphead->destroy(); + { + if (lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } } else { @@ -3606,11 +3615,14 @@ mysql_parse(THD *thd, char *inBuf, uint length) #ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ query_cache_abort(&thd->net); if (thd->lex->sphead) - thd->lex->sphead->destroy(); + { + if (lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } #endif } - if (thd->lex->sphead && lex != thd->lex) - thd->lex->sphead->restore_lex(thd); thd->proc_info="freeing items"; free_items(thd->free_list); /* Free strings used by items */ lex_end(lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bd0a3a09422..155e7b4b461 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -762,8 +762,16 @@ static bool parse_prepare_query(PREP_STMT *stmt, thd->lex->param_count= 0; if (!yyparse((void *)thd) && !thd->is_fatal_error) error= send_prepare_results(stmt); - if (thd->lex->sphead && lex != thd->lex) - thd->lex->sphead->restore_lex(thd); + else + { + if (thd->lex->sphead) + { + if (lex != thd->lex) + thd->lex->sphead->restore_lex(thd); + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } + } lex_end(lex); DBUG_RETURN(error); } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 742a834e447..98270b93cb7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -948,6 +948,7 @@ create: lex->sphead->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + lex->sphead->reset_thd_mem_root(YYTHD); } '(' sp_pdparam_list ')' { @@ -961,6 +962,7 @@ create: /* Restore flag if it was cleared above */ if (lex->sphead->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + lex->sphead->restore_thd_mem_root(YYTHD); } ; @@ -997,6 +999,7 @@ create_function_tail: lex->sphead->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; + lex->sphead->reset_thd_mem_root(YYTHD); } sp_fdparam_list ')' { @@ -1014,6 +1017,7 @@ create_function_tail: /* Restore flag if it was cleared above */ if (lex->sphead->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + lex->sphead->restore_thd_mem_root(YYTHD); } ; @@ -1174,7 +1178,7 @@ sp_proc_stmt: ** which get their set instructions generated separately.) */ if (lex->sql_command != SQLCOM_SET_OPTION || - !lex->var_list.is_empty()) + ! lex->var_list.is_empty()) { /* Currently we can't handle queries inside a FUNCTION, ** because of the way table locking works. @@ -1194,6 +1198,7 @@ sp_proc_stmt: i->set_lex(lex); lex->sphead->add_instr(i); + lex->sp_lex_in_use= TRUE; } } lex->sphead->restore_lex(YYTHD);