Commit 9f14ae75 authored by pem@mysql.com's avatar pem@mysql.com

sp_head now has its own mem_root (WL#961).

Also fixed some difficult memory leaks that became apparent
in this task.
parent 541c94fc
...@@ -151,7 +151,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ...@@ -151,7 +151,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
{ {
if (oldlex != thd->lex) if (oldlex != thd->lex)
thd->lex->sphead->restore_lex(thd); thd->lex->sphead->restore_lex(thd);
thd->lex->sphead->destroy(); delete thd->lex->sphead;
thd->lex= NULL;
} }
ret= SP_PARSE_ERROR; ret= SP_PARSE_ERROR;
} }
...@@ -444,7 +445,7 @@ sp_clear_function_cache(THD *thd) ...@@ -444,7 +445,7 @@ sp_clear_function_cache(THD *thd)
sp_head *sp; sp_head *sp;
while ((sp= li++)) while ((sp= li++))
sp->destroy(); delete sp;
thd->spfuns.empty(); thd->spfuns.empty();
} }
......
...@@ -81,7 +81,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type) ...@@ -81,7 +81,7 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
String *s= it->val_str(&tmp); String *s= it->val_str(&tmp);
DBUG_PRINT("info",("default result: %*s",s->length(),s->c_ptr_quick())); 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()); s->length(), it->charset());
break; break;
} }
...@@ -91,6 +91,34 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type) ...@@ -91,6 +91,34 @@ eval_func_item(THD *thd, Item *it, enum enum_field_types type)
DBUG_RETURN(it); 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) sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
: Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE) : 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) ...@@ -102,6 +130,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
m_name.str= name->str; m_name.str= name->str;
m_defstr.length= lex->end_of_query - lex->buf; m_defstr.length= lex->end_of_query - lex->buf;
m_defstr.str= lex->thd->strmake(dstr, m_defstr.length); m_defstr.str= lex->thd->strmake(dstr, m_defstr.length);
m_free_list= NULL;
m_comment.length= 0; m_comment.length= 0;
m_comment.str= 0; m_comment.str= 0;
...@@ -115,6 +144,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid) ...@@ -115,6 +144,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
m_pcont= lex->spcont; m_pcont= lex->spcont;
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
m_backpatch.empty(); m_backpatch.empty();
m_lex.empty();
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -142,13 +172,31 @@ sp_head::create(THD *thd) ...@@ -142,13 +172,31 @@ sp_head::create(THD *thd)
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
sp_head::~sp_head()
{
destroy();
if (m_thd)
restore_thd_mem_root(m_thd);
}
void void
sp_head::destroy() sp_head::destroy()
{ {
DBUG_ENTER("sp_head::destroy"); DBUG_ENTER("sp_head::destroy");
DBUG_PRINT("info", ("name: %s", m_name.str)); 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); delete_dynamic(&m_instr);
m_pcont->destroy(); 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; DBUG_VOID_RETURN;
} }
...@@ -367,20 +415,22 @@ sp_head::reset_lex(THD *thd) ...@@ -367,20 +415,22 @@ sp_head::reset_lex(THD *thd)
{ {
DBUG_ENTER("sp_head::reset_lex"); DBUG_ENTER("sp_head::reset_lex");
LEX *sublex; LEX *sublex;
LEX *oldlex= thd->lex;
m_lex= thd->lex; (void)m_lex.push_front(oldlex);
thd->lex= sublex= new st_lex; 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. */ /* 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 */ /* We must reset ptr and end_of_query again */
sublex->ptr= m_lex->ptr; sublex->ptr= oldlex->ptr;
sublex->end_of_query= m_lex->end_of_query; sublex->end_of_query= oldlex->end_of_query;
sublex->tok_start= m_lex->tok_start; sublex->tok_start= oldlex->tok_start;
/* And keep the SP stuff too */ /* And keep the SP stuff too */
sublex->sphead= m_lex->sphead; sublex->sphead= oldlex->sphead;
sublex->spcont= m_lex->spcont; sublex->spcont= oldlex->spcont;
mysql_init_query(thd, true); // Only init lex mysql_init_query(thd, true); // Only init lex
sublex->sp_lex_in_use= FALSE;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -390,14 +440,18 @@ sp_head::restore_lex(THD *thd) ...@@ -390,14 +440,18 @@ sp_head::restore_lex(THD *thd)
{ {
DBUG_ENTER("sp_head::restore_lex"); DBUG_ENTER("sp_head::restore_lex");
LEX *sublex= thd->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 // Update some state in the old one first
m_lex->ptr= sublex->ptr; oldlex->ptr= sublex->ptr;
m_lex->next_state= sublex->next_state; oldlex->next_state= sublex->next_state;
// Collect some data from the sub statement lex. // Collect some data from the sub statement lex.
sp_merge_funs(m_lex, sublex); sp_merge_funs(oldlex, sublex);
#if 0 #ifdef NOT_USED_NOW
// QQ We're not using this at the moment. // QQ We're not using this at the moment.
if (sublex.sql_command == SQLCOM_CALL) if (sublex.sql_command == SQLCOM_CALL)
{ {
...@@ -438,8 +492,9 @@ sp_head::restore_lex(THD *thd) ...@@ -438,8 +492,9 @@ sp_head::restore_lex(THD *thd)
} }
} }
#endif #endif
if (! sublex->sp_lex_in_use)
thd->lex= m_lex; delete sublex;
thd->lex= oldlex;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -478,6 +533,12 @@ sp_head::backpatch(sp_label_t *lab) ...@@ -478,6 +533,12 @@ sp_head::backpatch(sp_label_t *lab)
// //
// sp_instr_stmt // sp_instr_stmt
// //
sp_instr_stmt::~sp_instr_stmt()
{
if (m_lex)
delete m_lex;
}
int int
sp_instr_stmt::execute(THD *thd, uint *nextp) sp_instr_stmt::execute(THD *thd, uint *nextp)
{ {
......
...@@ -54,11 +54,19 @@ class sp_head : public Sql_alloc ...@@ -54,11 +54,19 @@ class sp_head : public Sql_alloc
List<char *> m_tables; // Used tables. List<char *> m_tables; // Used tables.
#endif #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); sp_head(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
int int
create(THD *thd); create(THD *thd);
virtual ~sp_head();
// Free memory // Free memory
void void
destroy(); destroy();
...@@ -87,6 +95,7 @@ class sp_head : public Sql_alloc ...@@ -87,6 +95,7 @@ class sp_head : public Sql_alloc
// Restores lex in 'thd' from our copy, but keeps some status from the // Restores lex in 'thd' from our copy, but keeps some status from the
// one in 'thd', like ptr, tables, fields, etc. // one in 'thd', like ptr, tables, fields, etc.
// If 'delete_lex' is true, we delete the current lex.
void void
restore_lex(THD *thd); restore_lex(THD *thd);
...@@ -122,10 +131,33 @@ class sp_head : public Sql_alloc ...@@ -122,10 +131,33 @@ class sp_head : public Sql_alloc
m_comment.length= commentlen; m_comment.length= commentlen;
m_comment.str= comment; m_comment.str= comment;
m_suid= suid; 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: 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_name;
LEX_STRING m_defstr; LEX_STRING m_defstr;
LEX_STRING m_comment; LEX_STRING m_comment;
...@@ -136,7 +168,7 @@ class sp_head : public Sql_alloc ...@@ -136,7 +168,7 @@ class sp_head : public Sql_alloc
bool m_suid; bool m_suid;
sp_pcontext *m_pcont; // Parse context sp_pcontext *m_pcont; // Parse context
LEX *m_lex; // Temp. store for the other lex List<LEX> m_lex; // Temp. store for the other lex
DYNAMIC_ARRAY m_instr; // The "instructions" DYNAMIC_ARRAY m_instr; // The "instructions"
typedef struct typedef struct
{ {
...@@ -211,11 +243,10 @@ class sp_instr_stmt : public sp_instr ...@@ -211,11 +243,10 @@ class sp_instr_stmt : public sp_instr
public: public:
sp_instr_stmt(uint ip) 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); virtual int execute(THD *thd, uint *nextp);
......
...@@ -495,6 +495,7 @@ typedef struct st_lex ...@@ -495,6 +495,7 @@ typedef struct st_lex
char *help_arg; char *help_arg;
SQL_LIST *gorder_list; SQL_LIST *gorder_list;
sp_head *sphead; sp_head *sphead;
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
sp_pcontext *spcont; sp_pcontext *spcont;
List<char> spfuns; /* Called functions */ List<char> spfuns; /* Called functions */
......
...@@ -2829,7 +2829,7 @@ mysql_execute_command(THD *thd) ...@@ -2829,7 +2829,7 @@ mysql_execute_command(THD *thd)
sp_head *sph= sp_find_function(thd, &lex->udf.name); sp_head *sph= sp_find_function(thd, &lex->udf.name);
if (sph) 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); net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
goto error; goto error;
} }
...@@ -3029,18 +3029,22 @@ mysql_execute_command(THD *thd) ...@@ -3029,18 +3029,22 @@ mysql_execute_command(THD *thd)
#endif #endif
res= lex->sphead->create(thd); res= lex->sphead->create(thd);
lex->sphead->destroy(); // QQ Free memory. Remove this when caching!!!
switch (res) switch (res)
{ {
case SP_OK: case SP_OK:
send_ok(thd); send_ok(thd);
delete lex->sphead; // QQ Free memory. Remove this when caching!!!
lex->sphead= NULL;
break; break;
case SP_WRITE_ROW_FAILED: case SP_WRITE_ROW_FAILED:
net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name); 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; goto error;
default: default:
net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); 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; goto error;
} }
break; break;
...@@ -3064,7 +3068,7 @@ mysql_execute_command(THD *thd) ...@@ -3064,7 +3068,7 @@ mysql_execute_command(THD *thd)
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) ||
(res= open_and_lock_tables(thd, 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; break;
} }
fix_tables_pointers(lex->all_selects_list); fix_tables_pointers(lex->all_selects_list);
...@@ -3083,7 +3087,7 @@ mysql_execute_command(THD *thd) ...@@ -3083,7 +3087,7 @@ mysql_execute_command(THD *thd)
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok; thd->net.no_send_ok= nsok;
#endif #endif
sp->destroy(); // QQ Free memory. Remove this when caching!!! delete sp; // QQ Free memory. Remove this when caching!!!
goto error; goto error;
} }
smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS; smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
...@@ -3101,7 +3105,7 @@ mysql_execute_command(THD *thd) ...@@ -3101,7 +3105,7 @@ mysql_execute_command(THD *thd)
thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; 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) if (res == 0)
send_ok(thd); send_ok(thd);
...@@ -3128,7 +3132,7 @@ mysql_execute_command(THD *thd) ...@@ -3128,7 +3132,7 @@ mysql_execute_command(THD *thd)
{ {
/* QQ This is an no-op right now, since we haven't /* QQ This is an no-op right now, since we haven't
put the characteristics in yet. */ 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); send_ok(thd);
} }
break; break;
...@@ -3588,7 +3592,12 @@ mysql_parse(THD *thd, char *inBuf, uint length) ...@@ -3588,7 +3592,12 @@ mysql_parse(THD *thd, char *inBuf, uint length)
{ {
send_error(thd, 0, NullS); send_error(thd, 0, NullS);
if (thd->lex->sphead) 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 else
{ {
...@@ -3606,11 +3615,14 @@ mysql_parse(THD *thd, char *inBuf, uint length) ...@@ -3606,11 +3615,14 @@ mysql_parse(THD *thd, char *inBuf, uint length)
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ #ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
query_cache_abort(&thd->net); query_cache_abort(&thd->net);
if (thd->lex->sphead) 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 #endif
} }
if (thd->lex->sphead && lex != thd->lex)
thd->lex->sphead->restore_lex(thd);
thd->proc_info="freeing items"; thd->proc_info="freeing items";
free_items(thd->free_list); /* Free strings used by items */ free_items(thd->free_list); /* Free strings used by items */
lex_end(lex); lex_end(lex);
......
...@@ -762,8 +762,16 @@ static bool parse_prepare_query(PREP_STMT *stmt, ...@@ -762,8 +762,16 @@ static bool parse_prepare_query(PREP_STMT *stmt,
thd->lex->param_count= 0; thd->lex->param_count= 0;
if (!yyparse((void *)thd) && !thd->is_fatal_error) if (!yyparse((void *)thd) && !thd->is_fatal_error)
error= send_prepare_results(stmt); error= send_prepare_results(stmt);
if (thd->lex->sphead && lex != thd->lex) else
thd->lex->sphead->restore_lex(thd); {
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); lex_end(lex);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
...@@ -948,6 +948,7 @@ create: ...@@ -948,6 +948,7 @@ create:
lex->sphead->m_old_cmq= lex->sphead->m_old_cmq=
YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
lex->sphead->reset_thd_mem_root(YYTHD);
} }
'(' sp_pdparam_list ')' '(' sp_pdparam_list ')'
{ {
...@@ -961,6 +962,7 @@ create: ...@@ -961,6 +962,7 @@ create:
/* Restore flag if it was cleared above */ /* Restore flag if it was cleared above */
if (lex->sphead->m_old_cmq) if (lex->sphead->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
lex->sphead->restore_thd_mem_root(YYTHD);
} }
; ;
...@@ -997,6 +999,7 @@ create_function_tail: ...@@ -997,6 +999,7 @@ create_function_tail:
lex->sphead->m_old_cmq= lex->sphead->m_old_cmq=
YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES; YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
lex->sphead->reset_thd_mem_root(YYTHD);
} }
sp_fdparam_list ')' sp_fdparam_list ')'
{ {
...@@ -1014,6 +1017,7 @@ create_function_tail: ...@@ -1014,6 +1017,7 @@ create_function_tail:
/* Restore flag if it was cleared above */ /* Restore flag if it was cleared above */
if (lex->sphead->m_old_cmq) if (lex->sphead->m_old_cmq)
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
lex->sphead->restore_thd_mem_root(YYTHD);
} }
; ;
...@@ -1174,7 +1178,7 @@ sp_proc_stmt: ...@@ -1174,7 +1178,7 @@ sp_proc_stmt:
** which get their set instructions generated separately.) ** which get their set instructions generated separately.)
*/ */
if (lex->sql_command != SQLCOM_SET_OPTION || 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, /* Currently we can't handle queries inside a FUNCTION,
** because of the way table locking works. ** because of the way table locking works.
...@@ -1194,6 +1198,7 @@ sp_proc_stmt: ...@@ -1194,6 +1198,7 @@ sp_proc_stmt:
i->set_lex(lex); i->set_lex(lex);
lex->sphead->add_instr(i); lex->sphead->add_instr(i);
lex->sp_lex_in_use= TRUE;
} }
} }
lex->sphead->restore_lex(YYTHD); lex->sphead->restore_lex(YYTHD);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment