Commit 841ea461 authored by unknown's avatar unknown
Browse files

Bug#21798: memory leak during query execution with subquery in column

            list using a function
When executing dependent subqueries they are re-inited and re-exec() for 
each row of the outer context.
The cause for the bug is that during subquery reinitialization/re-execution,
the optimizer reallocates JOIN::join_tab, JOIN::table in make_simple_join()
and the local variable in 'sortorder' in create_sort_index(), which is
allocated by make_unireg_sortorder().
Care must be taken not to allocate anything into the thread's memory pool
while re-initializing query plan structures between subquery re-executions.
All such items mush be cached and reused because the thread's memory pool
is freed at the end of the whole query.
Note that they must be cached and reused even for queries that are not 
otherwise cacheable because otherwise it will grow the thread's memory 
pool every time a cacheable query is re-executed. 
We provide additional members to the JOIN structure to store references 
to the items that need to be cached.


mysql-test/r/subselect.result:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - test case
mysql-test/t/subselect.test:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - test case
sql/mysql_priv.h:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the entities allocated in the threads memory pool by
     JOIN::exec ().
sql/sql_delete.cc:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the SORT_ORDER, TABLE * and JOIN_TAB allocated in the thread's 
     memory pool by JOIN::exec ().
sql/sql_select.cc:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the SORT_ORDER, TABLE * and JOIN_TAB allocated in the thread's 
     memory pool by JOIN::exec ().
sql/sql_select.h:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the SORT_ORDER, TABLE * and JOIN_TAB allocated in the thread's 
     memory pool by JOIN::exec ().
sql/sql_table.cc:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the SORT_ORDER, TABLE * and JOIN_TAB allocated in the thread's 
     memory pool by JOIN::exec ().
sql/sql_update.cc:
  Bug#21798: memory leak during query execution with subquery in column
              list using a function
   - cache the SORT_ORDER, TABLE * and JOIN_TAB allocated in the thread's 
     memory pool by JOIN::exec ().
parent 21be389b
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -3393,3 +3393,29 @@ id select_type table type possible_keys key key_len ref rows Extra
4	UNION	t12	system	NULL	NULL	NULL	NULL	0	const row not found
NULL	UNION RESULT	<union2,4>	ALL	NULL	NULL	NULL	NULL	NULL	
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(250), b INT auto_increment, PRIMARY KEY (b));
insert into t1 (a) values (FLOOR(rand() * 100));
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
SELECT a, 
(SELECT REPEAT(' ',250) FROM t1 i1 
WHERE i1.b=t1.a ORDER BY RAND() LIMIT 1) AS a 
FROM t1 ORDER BY a LIMIT 5;
a	a
0	NULL
0	NULL
0	NULL
0	NULL
0	NULL
DROP TABLE t1;
+26 −0
Original line number Diff line number Diff line
@@ -2306,3 +2306,29 @@ explain select * from t1 where not exists
  ((select t11.i from t1 t11) union (select t12.i from t1 t12));

DROP TABLE t1;

#
# Bug#21798: memory leak during query execution with subquery in column 
#             list using a function
#
CREATE TABLE t1 (a VARCHAR(250), b INT auto_increment, PRIMARY KEY (b));
insert into t1 (a) values (FLOOR(rand() * 100));
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;
insert into t1 (a) select FLOOR(rand() * 100) from t1;

SELECT a, 
       (SELECT REPEAT(' ',250) FROM t1 i1 
        WHERE i1.b=t1.a ORDER BY RAND() LIMIT 1) AS a 
FROM t1 ORDER BY a LIMIT 5;
DROP TABLE t1;
+2 −1
Original line number Diff line number Diff line
@@ -708,7 +708,8 @@ bool mysql_xa_recover(THD *thd);

bool check_simple_select();

SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length);
SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length,
                                  SORT_FIELD *sortorder);
int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
		List<Item> &fields, List <Item> &all_fields, ORDER *order);
int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+1 −1
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
                                                   MYF(MY_FAE | MY_ZEROFILL));
    
      if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
                                             &length)) ||
                                             &length, NULL)) ||
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
                                                select, HA_POS_ERROR,
                                                &examined_rows))
+32 −8
Original line number Diff line number Diff line
@@ -1574,6 +1574,7 @@ JOIN::exec()
	{
	  DBUG_VOID_RETURN;
	}
        sortorder= curr_join->sortorder;
      }
      
      thd->proc_info="Copying to group table";
@@ -1783,6 +1784,7 @@ JOIN::exec()
			    (select_options & OPTION_FOUND_ROWS ?
			     HA_POS_ERROR : unit->select_limit_cnt)))
	DBUG_VOID_RETURN;
      sortorder= curr_join->sortorder;
    }
  }
  /* XXX: When can we have here thd->net.report_error not zero? */
@@ -4949,9 +4951,28 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
  JOIN_TAB *join_tab;
  DBUG_ENTER("make_simple_join");

  if (!(tableptr=(TABLE**) join->thd->alloc(sizeof(TABLE*))) ||
      !(join_tab=(JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB))))
    DBUG_RETURN(TRUE);
  /*
    Reuse TABLE * and JOIN_TAB if already allocated by a previous call
    to this function through JOIN::exec (may happen for sub-queries).
  */
  if (!join->table_cache)
  {
    if (!(join->table_cache= (TABLE**) join->thd->alloc(sizeof(TABLE*))))
      DBUG_RETURN(TRUE);                        /* purecov: inspected */
    if (join->tmp_join)
      join->tmp_join->table_cache= join->table_cache;
  }
  if (!join->join_tab_cache)
  {
    if (!(join->join_tab_cache=
          (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB))))
      DBUG_RETURN(TRUE);                        /* purecov: inspected */
    if (join->tmp_join)
      join->tmp_join->join_tab_cache= join->join_tab_cache;
  }
  tableptr= join->table_cache;
  join_tab= join->join_tab_cache;

  join->join_tab=join_tab;
  join->table=tableptr; tableptr[0]=tmp_table;
  join->tables=1;
@@ -11971,7 +11992,6 @@ static int
create_sort_index(THD *thd, JOIN *join, ORDER *order,
		  ha_rows filesort_limit, ha_rows select_limit)
{
  SORT_FIELD *sortorder;
  uint length;
  ha_rows examined_rows;
  TABLE *table;
@@ -11987,7 +12007,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,

  if (test_if_skip_sort_order(tab,order,select_limit,0))
    DBUG_RETURN(0);
  if (!(sortorder=make_unireg_sortorder(order,&length)))
  if (!(join->sortorder= 
        make_unireg_sortorder(order,&length,join->sortorder)))
    goto err;				/* purecov: inspected */
  /* It's not fatal if the following alloc fails */
  table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
@@ -12034,7 +12055,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,

  if (table->s->tmp_table)
    table->file->info(HA_STATUS_VARIABLE);	// Get record count
  table->sort.found_records=filesort(thd, table,sortorder, length,
  table->sort.found_records=filesort(thd, table,join->sortorder, length,
                                     select, filesort_limit, &examined_rows);
  tab->records= table->sort.found_records;	// For SQL_CALC_ROWS
  if (select)
@@ -12381,7 +12402,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}


SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length)
SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
                                  SORT_FIELD *sortorder)
{
  uint count;
  SORT_FIELD *sort,*pos;
@@ -12390,7 +12412,9 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length)
  count=0;
  for (ORDER *tmp = order; tmp; tmp=tmp->next)
    count++;
  pos=sort=(SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1));
  if (!sortorder)
    sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1));
  pos=sort=sortorder;
  if (!pos)
    return 0;

Loading