Commit 3f3318c3 authored by Sergey Glukhov's avatar Sergey Glukhov

Bug#11756242 48137: PROCEDURE ANALYSE() LEAKS MEMORY WHEN RETURNING NULL

There are two problems with ANALYSE():

1. Memory leak 
   it happens because do_select() can overwrite
   JOIN::procedure field(with zero value in our case) and
   JOIN destructor don't free the memory allocated for
   JOIN::procedure. The fix is to save original JOIN::procedure
   before do_select() call and restore it after do_select
   execution.

2. Wrong result
   If ANALYSE() procedure is used for the statement with LIMIT clause
   it could retrun empty result set. It happens because of missing 
   analyse::end_of_records() call. First end_send() function call
   returns NESTED_LOOP_QUERY_LIMIT and second call of end_send() with
   end_of_records flag enabled does not happen. The fix is to return
   NESTED_LOOP_OK from end_send() if procedure is active.
parent 58cb12c9
...@@ -135,4 +135,17 @@ SELECT * FROM t1 PROCEDURE ANALYSE(); ...@@ -135,4 +135,17 @@ SELECT * FROM t1 PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t1.a e e- 1 2 0 0 1.3333 NULL ENUM('e','e-') NOT NULL test.t1.a e e- 1 2 0 0 1.3333 NULL ENUM('e','e-') NOT NULL
DROP TABLE t1; DROP TABLE t1;
#
# Bug#11756242 48137: PROCEDURE ANALYSE() LEAKS MEMORY WHEN RETURNING NULL
#
CREATE TABLE t1(f1 INT) ENGINE=MYISAM;
CREATE TABLE t2(f2 INT) ENGINE=INNODB;
INSERT INTO t2 VALUES (1);
SELECT DISTINCTROW f1 FROM t1 NATURAL RIGHT OUTER JOIN t2 PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t1.f1 NULL NULL 0 0 0 1 0.0 0.0 CHAR(0)
SELECT * FROM t2 LIMIT 1 PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t2.f2 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
DROP TABLE t1, t2;
End of 5.1 tests End of 5.1 tests
# #
# Test of procedure analyse # Test of procedure analyse
# #
-- source include/have_innodb.inc
--disable_warnings --disable_warnings
drop table if exists t1,t2; drop table if exists t1,t2;
...@@ -144,4 +145,15 @@ INSERT INTO t1 VALUES ('e'),('e'),('e-'); ...@@ -144,4 +145,15 @@ INSERT INTO t1 VALUES ('e'),('e'),('e-');
SELECT * FROM t1 PROCEDURE ANALYSE(); SELECT * FROM t1 PROCEDURE ANALYSE();
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug#11756242 48137: PROCEDURE ANALYSE() LEAKS MEMORY WHEN RETURNING NULL
--echo #
CREATE TABLE t1(f1 INT) ENGINE=MYISAM;
CREATE TABLE t2(f2 INT) ENGINE=INNODB;
INSERT INTO t2 VALUES (1);
SELECT DISTINCTROW f1 FROM t1 NATURAL RIGHT OUTER JOIN t2 PROCEDURE ANALYSE();
SELECT * FROM t2 LIMIT 1 PROCEDURE ANALYSE();
DROP TABLE t1, t2;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -1929,7 +1929,11 @@ JOIN::exec() ...@@ -1929,7 +1929,11 @@ JOIN::exec()
if (!curr_join->sort_and_group && if (!curr_join->sort_and_group &&
curr_join->const_tables != curr_join->tables) curr_join->const_tables != curr_join->tables)
curr_join->join_tab[curr_join->const_tables].sorted= 0; curr_join->join_tab[curr_join->const_tables].sorted= 0;
if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
Procedure *save_proc= curr_join->procedure;
tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0);
curr_join->procedure= save_proc;
if (tmp_error)
{ {
error= tmp_error; error= tmp_error;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -12354,10 +12358,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -12354,10 +12358,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int error; int error;
if (join->having && join->having->val_int() == 0) if (join->having && join->having->val_int() == 0)
DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having
error=0;
if (join->procedure) if (join->procedure)
error=join->procedure->send_row(join->procedure_fields_list); {
else if (join->do_send_rows) if (join->procedure->send_row(join->procedure_fields_list))
DBUG_RETURN(NESTED_LOOP_ERROR);
DBUG_RETURN(NESTED_LOOP_OK);
}
error=0;
if (join->do_send_rows)
error=join->result->send_data(*join->fields); error=join->result->send_data(*join->fields);
if (error) if (error)
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
......
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