Commit a7ed6ce4 authored by unknown's avatar unknown
Browse files

Fix bug #14138 ROLLUP and PROCEDURE ANALYSE() hang server

Procedure analyse() redefines select's fields_list. setup_copy_fields() assumes
that fields_list is a part of all_fields_list. Because select have only 
3 columns and analyse() redefines it to have 10 columns, int overrun in
setup_copy_fields() occurs and server goes to almost infinite loop.

Because fields_list used not only to send data ad fields types, it's wrong 
to allow procedure redefine it. This patch separates select's fileds_list 
and procedure's one. Now if procedure is present, copy of fields_list is 
created in procedure_fields_list and it is used for sending data and fields.


mysql-test/t/analyse.test:
  Test case for bug #14138  ROLLUP and PROCEDURE ANALYSE() hang server
mysql-test/r/analyse.result:
  Test case for bug #14138  ROLLUP and PROCEDURE ANALYSE() hang server
sql/sql_select.h:
  Fix bug #14138  ROLLUP and PROCEDURE ANALYSE() hang server
  To JOIN  Added separate fields_list for procedure.
sql/sql_select.cc:
  Fix bug #14138  ROLLUP and PROCEDURE ANALYSE() hang server
  SELECT's fields_list and procedure's fields_list made split. If procedure is defined
  then procedure's fields_list is used to send fields and data.
parent 5e8515d9
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -108,3 +108,29 @@ select * from t1 procedure analyse (1,1);
Field_name	Min_value	Max_value	Min_length	Max_length	Empties_or_zeros	Nulls	Avg_value_or_avg_length	Std	Optimal_fieldtype
test.t1.d	100000	100000	6	6	0	0	100000	0	MEDIUMINT(6) UNSIGNED NOT NULL
drop table t1;
create table t1 (product varchar(32), country_id int not null, year int,
profit int);
insert into t1  values ( 'Computer', 2,2000, 1200),
( 'TV', 1, 1999, 150),
( 'Calculator', 1, 1999,50),
( 'Computer', 1, 1999,1500),
( 'Computer', 1, 2000,1500),
( 'TV', 1, 2000, 150),
( 'TV', 2, 2000, 100),
( 'TV', 2, 2000, 100),
( 'Calculator', 1, 2000,75),
( 'Calculator', 2, 2000,75),
( 'TV', 1, 1999, 100),
( 'Computer', 1, 1999,1200),
( 'Computer', 2, 2000,1500),
( 'Calculator', 2, 2000,75),
( 'Phone', 3, 2003,10)
;
create table t2 (country_id int primary key, country char(20) not null);
insert into t2 values (1, 'USA'),(2,'India'), (3,'Finland');
select product, sum(profit),avg(profit) from t1 group by product with rollup 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.product	Computer	TV	2	8	0	0	4.2500	NULL	ENUM('Computer','Phone','TV') NOT NULL
sum(profit)	10	6900	2	4	0	0	1946	2868	ENUM('10','275','600','6900') NOT NULL
avg(profit)	10.0000	1380.0000	7	9	0	0	394.6875	570.2003	ENUM('10.0000','68.7500','120.0000','1380.0000') NOT NULL
drop table t1,t2;
+25 −0
Original line number Diff line number Diff line
@@ -57,4 +57,29 @@ insert into t1 values (100000);
select * from t1 procedure analyse (1,1);
drop table t1;

#
# Bug #14138 ROLLUP and PROCEDURE ANALYSE() hang server
#
create table t1 (product varchar(32), country_id int not null, year int,
                 profit int);
insert into t1  values ( 'Computer', 2,2000, 1200),
    ( 'TV', 1, 1999, 150),
    ( 'Calculator', 1, 1999,50),
    ( 'Computer', 1, 1999,1500),
    ( 'Computer', 1, 2000,1500),
    ( 'TV', 1, 2000, 150),
    ( 'TV', 2, 2000, 100),
    ( 'TV', 2, 2000, 100),
    ( 'Calculator', 1, 2000,75),
    ( 'Calculator', 2, 2000,75),
    ( 'TV', 1, 1999, 100),
    ( 'Computer', 1, 1999,1200),
    ( 'Computer', 2, 2000,1500),
    ( 'Calculator', 2, 2000,75),
    ( 'Phone', 3, 2003,10)
    ;
create table t2 (country_id int primary key, country char(20) not null); 
insert into t2 values (1, 'USA'),(2,'India'), (3,'Finland');
select product, sum(profit),avg(profit) from t1 group by product with rollup procedure analyse();
drop table t1,t2;
# End of 4.1 tests
+14 −11
Original line number Diff line number Diff line
@@ -1044,18 +1044,21 @@ JOIN::save_join_tab()
void
JOIN::exec()
{
  List<Item> *columns_list= &fields_list;
  int      tmp_error;
  DBUG_ENTER("JOIN::exec");
  
  error= 0;
  if (procedure)
  {
    if (procedure->change_columns(fields_list) ||
	result->prepare(fields_list, unit))
    procedure_fields_list= fields_list;
    if (procedure->change_columns(procedure_fields_list) ||
	result->prepare(procedure_fields_list, unit))
    {
      thd->limit_found_rows= thd->examined_row_count= 0;
      DBUG_VOID_RETURN;
    }
    columns_list= &procedure_fields_list;
  }
  else if (test(select_options & OPTION_BUFFER_RESULT) &&
           result && result->prepare(fields_list, unit))
@@ -1072,7 +1075,7 @@ JOIN::exec()
		      (zero_result_cause?zero_result_cause:"No tables used"));
    else
    {
      result->send_fields(fields_list,1);
      result->send_fields(*columns_list, 1);
      /*
        We have to test for 'conds' here as the WHERE may not be constant
        even if we don't have any tables for prepared statements or if
@@ -1082,9 +1085,9 @@ JOIN::exec()
          (!conds || conds->val_int()) &&
          (!having || having->val_int()))
      {
	if (do_send_rows && (procedure ? (procedure->send_row(fields_list) ||
                                          procedure->end_of_records())
                                       : result->send_data(fields_list)))
	if (do_send_rows &&
            (procedure ? (procedure->send_row(procedure_fields_list) ||
             procedure->end_of_records()) : result->send_data(fields_list)))
	  error= 1;
	else
	{
@@ -1108,7 +1111,7 @@ JOIN::exec()

  if (zero_result_cause)
  {
    (void) return_zero_rows(this, result, tables_list, fields_list,
    (void) return_zero_rows(this, result, tables_list, *columns_list,
			    send_row_on_empty_set(),
			    select_options,
			    zero_result_cause,
@@ -5845,13 +5848,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
  JOIN_TAB *join_tab;
  int (*end_select)(JOIN *, struct st_join_table *,bool);
  DBUG_ENTER("do_select");

  List<Item> *columns_list= procedure ? &join->procedure_fields_list : fields;
  join->procedure=procedure;
  /*
    Tell the client how many fields there are in a row
  */
  if (!table)
    join->result->send_fields(*fields,1);
    join->result->send_fields(*columns_list, 1);
  else
  {
    VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
@@ -5913,7 +5916,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
	error=(*end_select)(join,join_tab,1);
    }
    else if (join->send_row_on_empty_set())
      error= join->result->send_data(*join->fields);
      error= join->result->send_data(*columns_list);
  }
  else
  {
@@ -6612,7 +6615,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
      DBUG_RETURN(0);				// Didn't match having
    error=0;
    if (join->procedure)
      error=join->procedure->send_row(*join->fields);
      error=join->procedure->send_row(join->procedure_fields_list);
    else if (join->do_send_rows)
      error=join->result->send_data(*join->fields);
    if (error)
+1 −0
Original line number Diff line number Diff line
@@ -204,6 +204,7 @@ class JOIN :public Sql_alloc
  //Part, shared with list above, emulate following list
  List<Item> tmp_fields_list1, tmp_fields_list2, tmp_fields_list3;
  List<Item> &fields_list; // hold field list passed to mysql_select
  List<Item> procedure_fields_list;
  int error;

  ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select