Commit d31e997d authored by unknown's avatar unknown
Browse files

A fix and a test case for Bug#12736 "Server crash during a select".

The bug was in JOIN::join_free which was wrongly determining that
all joins have been already executed and therefore all used tables
can be closed.


mysql-test/r/subselect_innodb.result:
  - test results fixed (Bug#12736 "Server crash during a select
mysql-test/t/subselect_innodb.test:
  - a test case for Bug#12736 "Server crash during a select": test
  that ha_index_or_rnd_end and mysql_unlock_tables are called
  for all used tables in proper order.
sql/item_subselect.cc:
  - implement subselect_union_engine::is_executed
sql/item_subselect.h:
  - implement Item_subselect::is_evaluated. This function is used
  to check whether we can clean up a non-correlated join of a subquery
  when cleaning up the join of the outer query
sql/sql_lex.h:
  - declare st_select_lex::cleanup_all_joins
sql/sql_select.cc:
  - remove an argument from JOIN::join_free, it's now not used
  - reimplement JOIN::join_free to not unlock tables if there
    is a subquery that has not yet been evaluated. Make sure that the
    new implementation calls ha_index_or_rnd_end for every table in
    the join and inner joins, because all table cursors must be closed
    before mysql_unlock_tables.
sql/sql_select.h:
  - JOIN::join_free signature changed
sql/sql_union.cc:
  - implement a helper method st_select_lex::cleanup_all_joins, which
    recursively walks over a tree of joins and calls cleanup() for
    each join.
parent 20a6f152
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -152,3 +152,23 @@ EXECUTE my_stmt;
b	count(*)
deallocate prepare my_stmt;
drop table t1,t2;
CREATE TABLE t1 (
school_name varchar(45) NOT NULL,
country varchar(45) NOT NULL,    
funds_requested float NOT NULL,
schooltype varchar(45) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into t1 values ("the school", "USA", 1200, "Human");
select count(country) as countrycount, sum(funds_requested) as smcnt,
country, (select sum(funds_requested) from t1) as total_funds
from t1
group by country;
countrycount	smcnt	country	total_funds
1	1200	USA	1200
select count(country) as countrycount, sum(funds_requested) as smcnt,
country, (select sum(funds_requested) from t1) as total_funds
from t1
group by country;
countrycount	smcnt	country	total_funds
1	1200	USA	1200
drop table t1;
+22 −0
Original line number Diff line number Diff line
@@ -161,3 +161,25 @@ deallocate prepare my_stmt;
drop table t1,t2;

# End of 4.1 tests

CREATE TABLE t1 (
  school_name varchar(45) NOT NULL,
  country varchar(45) NOT NULL,    
  funds_requested float NOT NULL,
  schooltype varchar(45) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into t1 values ("the school", "USA", 1200, "Human");

select count(country) as countrycount, sum(funds_requested) as smcnt,
       country, (select sum(funds_requested) from t1) as total_funds
from t1
group by country;

select count(country) as countrycount, sum(funds_requested) as smcnt,
       country, (select sum(funds_requested) from t1) as total_funds
from t1
group by country;

drop table t1;
+6 −0
Original line number Diff line number Diff line
@@ -1413,6 +1413,12 @@ void subselect_union_engine::cleanup()
}


bool subselect_union_engine::is_executed() const
{
  return unit->executed;
}


void subselect_uniquesubquery_engine::cleanup()
{
  DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
+16 −0
Original line number Diff line number Diff line
@@ -109,6 +109,12 @@ class Item_subselect :public Item_result_field
    engine_changed= 1;
    return eng == 0;
  }
  /*
    True if this subquery has been already evaluated. Implemented only for
    single select and union subqueries only.
  */
  bool is_evaluated() const;

  /*
    Used by max/min subquery to initialize value presence registration
    mechanism. Engine call this method before rexecution query.
@@ -317,6 +323,7 @@ class subselect_engine: public Sql_alloc
  virtual void print(String *str)= 0;
  virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
  virtual bool no_tables()= 0;
  virtual bool is_executed() const { return FALSE; }
};


@@ -342,6 +349,7 @@ class subselect_single_select_engine: public subselect_engine
  void print (String *str);
  bool change_result(Item_subselect *si, select_subselect *result);
  bool no_tables();
  bool is_executed() const { return executed; }
};


@@ -363,6 +371,7 @@ class subselect_union_engine: public subselect_engine
  void print (String *str);
  bool change_result(Item_subselect *si, select_subselect *result);
  bool no_tables();
  bool is_executed() const;
};


@@ -411,3 +420,10 @@ class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
  int exec();
  void print (String *str);
};


inline bool Item_subselect::is_evaluated() const
{
  return engine->is_executed();
}
+6 −1
Original line number Diff line number Diff line
@@ -386,12 +386,12 @@ class st_select_lex_unit: public st_select_lex_node {
  select_result *result;
  ulong found_rows_for_union;
  bool res;
public:
  bool  prepared, // prepare phase already performed for UNION (unit)
    optimized, // optimize phase already performed for UNION (unit)
    executed, // already executed
    cleaned;

public:
  // list of fields which points to temporary table for union
  List<Item> item_list;
  /*
@@ -638,6 +638,11 @@ class st_select_lex: public st_select_lex_node
    SELECT_LEX and all nested SELECT_LEXes and SELECT_LEX_UNITs).
  */
  bool cleanup();
  /*
    Recursively cleanup the join of this select lex and of all nested
    select lexes.
  */
  void cleanup_all_joins(bool full);
};
typedef class st_select_lex SELECT_LEX;

Loading