Commit 2c44ef17 authored by unknown's avatar unknown
Browse files

Fixed BUG#10968: Stored procedures: crash if long loop

  Free memory after all SP invokation.


sql/sp_head.cc:
  Fixed memory leaks in SP invokation. Have to release memory after each call,
  or items and contexts are not released until it's too late.
parent 29f3b76f
Loading
Loading
Loading
Loading
+71 −4
Original line number Diff line number Diff line
@@ -696,6 +696,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
  sp_rcontext *nctx = NULL;
  uint i;
  int ret;
  MEM_ROOT *old_mem_root, call_mem_root;
  Item *old_free_list, *call_free_list;

  if (argcount != params)
  {
@@ -706,6 +708,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
    DBUG_RETURN(-1);
  }

  init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
  old_mem_root= thd->mem_root;
  thd->mem_root= &call_mem_root;
  old_free_list= thd->free_list; // Keep the old list
  thd->free_list= NULL;	// Start a new one

  // QQ Should have some error checking here? (types, etc...)
  nctx= new sp_rcontext(csize, hmax, cmax);
  for (i= 0 ; i < params && i < argcount ; i++)
@@ -736,13 +744,20 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)

  ret= execute(thd);

  // Partially restore context now.
  // We still need the call mem root and free list for processing
  // of the result.
  call_free_list= thd->free_list;
  thd->free_list= old_free_list;
  thd->mem_root= old_mem_root;

  if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
  {
    /* We need result only in function but not in trigger */
    Item *it= nctx->get_result();

    if (it)
      *resp= it;
      *resp= sp_eval_func_item(thd, &it, m_returns, NULL);
    else
    {
      my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
@@ -752,6 +767,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)

  nctx->pop_all_cursors();	// To avoid memory leaks after an error
  thd->spcont= octx;

  // Now get rid of the rest of the callee context
  cleanup_items(call_free_list);
  free_items(call_free_list);
  free_root(&call_mem_root, MYF(0));

  DBUG_RETURN(ret);
}

@@ -781,6 +802,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
  sp_rcontext *octx = thd->spcont;
  sp_rcontext *nctx = NULL;
  my_bool tmp_octx = FALSE;	// True if we have allocated a temporary octx
  MEM_ROOT *old_mem_root, call_mem_root;
  Item *old_free_list, *call_free_list;

  if (args->elements != params)
  {
@@ -789,6 +812,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
    DBUG_RETURN(-1);
  }

  init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
  old_mem_root= thd->mem_root;
  thd->mem_root= &call_mem_root;
  old_free_list= thd->free_list; // Keep the old list
  thd->free_list= NULL;	// Start a new one

  if (csize > 0 || hmax > 0 || cmax > 0)
  {
    Item_null *nit= NULL;	// Re-use this, and only create if needed
@@ -854,9 +883,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
  if (! ret)
    ret= execute(thd);

  // Partially restore context now.
  // We still need the call mem root and free list for processing
  // of out parameters.
  call_free_list= thd->free_list;
  thd->free_list= old_free_list;
  thd->mem_root= old_mem_root;

  if (!ret && csize > 0)
  {
    List_iterator_fast<Item> li(*args);
    List_iterator<Item> li(*args);
    Item *it;

    // Copy back all OUT or INOUT values to the previous frame, or
@@ -868,8 +904,34 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
      if (pvar->mode != sp_param_in)
      {
	if (it->is_splocal())
	  octx->set_item(static_cast<Item_splocal *>(it)->get_offset(),
			 nctx->get_item(i));
	{
	  // Have to copy the item to the caller's mem_root
	  Item *copy;
	  uint offset= static_cast<Item_splocal *>(it)->get_offset();
	  Item *val= nctx->get_item(i);
	  Item *orig= octx->get_item(offset);
	  Item *o_item_next;
	  Item *o_free_list= thd->free_list;
	  LINT_INIT(o_item_next);

	  if (orig)
	    o_item_next= orig->next;
	  copy= sp_eval_func_item(thd, &val, pvar->type, orig); // Copy
	  if (!copy)
	  {
	    ret= -1;
	    break;
	  }
	  if (copy != orig)
	    octx->set_item(offset, copy);
	  if (orig && copy == orig)
	  {
	    // A reused item slot, where the constructor put it in the
	    // free_list, so we have to restore the list.
	    thd->free_list= o_free_list;
	    copy->next= o_item_next;
	  }
	}
	else
	{
	  Item_func_get_user_var *guv= item_is_user_var(it);
@@ -900,6 +962,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
    nctx->pop_all_cursors();	// To avoid memory leaks after an error
  thd->spcont= octx;

  // Now get rid of the rest of the callee context
  cleanup_items(call_free_list);
  free_items(call_free_list);
  free_root(&call_mem_root, MYF(0));

  DBUG_RETURN(ret);
}