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)
{
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();
}
......
......@@ -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)
{
......
......@@ -54,11 +54,19 @@ class sp_head : public Sql_alloc
List<char *> 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 @@ class sp_head : public Sql_alloc
// 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 @@ class sp_head : public Sql_alloc
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 @@ class sp_head : public Sql_alloc
bool m_suid;
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"
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);
......
......@@ -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<char> spfuns; /* Called functions */
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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);
......
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