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.
This commit is contained in:
parent
2c7505b263
commit
b2ff38202d
@ -152,3 +152,23 @@ EXECUTE my_stmt;
|
|||||||
b count(*)
|
b count(*)
|
||||||
deallocate prepare my_stmt;
|
deallocate prepare my_stmt;
|
||||||
drop table t1,t2;
|
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;
|
||||||
|
@ -161,3 +161,25 @@ deallocate prepare my_stmt;
|
|||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# 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;
|
||||||
|
|
||||||
|
@ -1413,6 +1413,12 @@ void subselect_union_engine::cleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool subselect_union_engine::is_executed() const
|
||||||
|
{
|
||||||
|
return unit->executed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void subselect_uniquesubquery_engine::cleanup()
|
void subselect_uniquesubquery_engine::cleanup()
|
||||||
{
|
{
|
||||||
DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
|
DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
|
||||||
|
@ -109,6 +109,12 @@ public:
|
|||||||
engine_changed= 1;
|
engine_changed= 1;
|
||||||
return eng == 0;
|
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
|
Used by max/min subquery to initialize value presence registration
|
||||||
mechanism. Engine call this method before rexecution query.
|
mechanism. Engine call this method before rexecution query.
|
||||||
@ -317,6 +323,7 @@ public:
|
|||||||
virtual void print(String *str)= 0;
|
virtual void print(String *str)= 0;
|
||||||
virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
|
virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
|
||||||
virtual bool no_tables()= 0;
|
virtual bool no_tables()= 0;
|
||||||
|
virtual bool is_executed() const { return FALSE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -342,6 +349,7 @@ public:
|
|||||||
void print (String *str);
|
void print (String *str);
|
||||||
bool change_result(Item_subselect *si, select_subselect *result);
|
bool change_result(Item_subselect *si, select_subselect *result);
|
||||||
bool no_tables();
|
bool no_tables();
|
||||||
|
bool is_executed() const { return executed; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -363,6 +371,7 @@ public:
|
|||||||
void print (String *str);
|
void print (String *str);
|
||||||
bool change_result(Item_subselect *si, select_subselect *result);
|
bool change_result(Item_subselect *si, select_subselect *result);
|
||||||
bool no_tables();
|
bool no_tables();
|
||||||
|
bool is_executed() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -411,3 +420,10 @@ public:
|
|||||||
int exec();
|
int exec();
|
||||||
void print (String *str);
|
void print (String *str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline bool Item_subselect::is_evaluated() const
|
||||||
|
{
|
||||||
|
return engine->is_executed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -386,12 +386,12 @@ protected:
|
|||||||
select_result *result;
|
select_result *result;
|
||||||
ulong found_rows_for_union;
|
ulong found_rows_for_union;
|
||||||
bool res;
|
bool res;
|
||||||
|
public:
|
||||||
bool prepared, // prepare phase already performed for UNION (unit)
|
bool prepared, // prepare phase already performed for UNION (unit)
|
||||||
optimized, // optimize phase already performed for UNION (unit)
|
optimized, // optimize phase already performed for UNION (unit)
|
||||||
executed, // already executed
|
executed, // already executed
|
||||||
cleaned;
|
cleaned;
|
||||||
|
|
||||||
public:
|
|
||||||
// list of fields which points to temporary table for union
|
// list of fields which points to temporary table for union
|
||||||
List<Item> item_list;
|
List<Item> item_list;
|
||||||
/*
|
/*
|
||||||
@ -638,6 +638,11 @@ public:
|
|||||||
SELECT_LEX and all nested SELECT_LEXes and SELECT_LEX_UNITs).
|
SELECT_LEX and all nested SELECT_LEXes and SELECT_LEX_UNITs).
|
||||||
*/
|
*/
|
||||||
bool cleanup();
|
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;
|
typedef class st_select_lex SELECT_LEX;
|
||||||
|
|
||||||
|
@ -1377,7 +1377,7 @@ JOIN::exec()
|
|||||||
DBUG_PRINT("info",("Creating group table"));
|
DBUG_PRINT("info",("Creating group table"));
|
||||||
|
|
||||||
/* Free first data from old join */
|
/* Free first data from old join */
|
||||||
curr_join->join_free(0);
|
curr_join->join_free();
|
||||||
if (make_simple_join(curr_join, curr_tmp_table))
|
if (make_simple_join(curr_join, curr_tmp_table))
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
calc_group_buffer(curr_join, group_list);
|
calc_group_buffer(curr_join, group_list);
|
||||||
@ -1475,7 +1475,7 @@ JOIN::exec()
|
|||||||
if (curr_tmp_table->distinct)
|
if (curr_tmp_table->distinct)
|
||||||
curr_join->select_distinct=0; /* Each row is unique */
|
curr_join->select_distinct=0; /* Each row is unique */
|
||||||
|
|
||||||
curr_join->join_free(0); /* Free quick selects */
|
curr_join->join_free(); /* Free quick selects */
|
||||||
if (curr_join->select_distinct && ! curr_join->group_list)
|
if (curr_join->select_distinct && ! curr_join->group_list)
|
||||||
{
|
{
|
||||||
thd->proc_info="Removing duplicates";
|
thd->proc_info="Removing duplicates";
|
||||||
@ -5718,34 +5718,88 @@ void JOIN_TAB::cleanup()
|
|||||||
end_read_record(&read_record);
|
end_read_record(&read_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Partially cleanup JOIN after it has executed: close index or rnd read
|
||||||
|
(table cursors), free quick selects.
|
||||||
|
|
||||||
void JOIN::join_free(bool full)
|
DESCRIPTION
|
||||||
|
This function is called in the end of execution of a JOIN, before the used
|
||||||
|
tables are unlocked and closed.
|
||||||
|
|
||||||
|
For a join that is resolved using a temporary table, the first sweep is
|
||||||
|
performed against actual tables and an intermediate result is inserted
|
||||||
|
into the temprorary table.
|
||||||
|
The last sweep is performed against the temporary table. Therefore,
|
||||||
|
the base tables and associated buffers used to fill the temporary table
|
||||||
|
are no longer needed, and this function is called to free them.
|
||||||
|
|
||||||
|
For a join that is performed without a temporary table, this function
|
||||||
|
is called after all rows are sent, but before EOF packet is sent.
|
||||||
|
|
||||||
|
For a simple SELECT with no subqueries this function performs a full
|
||||||
|
cleanup of the JOIN and calls mysql_unlock_read_tables to free used base
|
||||||
|
tables.
|
||||||
|
|
||||||
|
If a JOIN is executed for a subquery or if it has a subquery, we can't
|
||||||
|
do the full cleanup and need to do a partial cleanup only.
|
||||||
|
o If a JOIN is not the top level join, we must not unlock the tables
|
||||||
|
because the outer select may not have been evaluated yet, and we
|
||||||
|
can't unlock only selected tables of a query.
|
||||||
|
|
||||||
|
o Additionally, if this JOIN corresponds to a correlated subquery, we
|
||||||
|
should not free quick selects and join buffers because they will be
|
||||||
|
needed for the next execution of the correlated subquery.
|
||||||
|
|
||||||
|
o However, if this is a JOIN for a [sub]select, which is not
|
||||||
|
a correlated subquery itself, but has subqueries, we can free it
|
||||||
|
fully and also free JOINs of all its subqueries. The exception
|
||||||
|
is a subquery in SELECT list, e.g:
|
||||||
|
SELECT a, (select max(b) from t1) group by c
|
||||||
|
This subquery will not be evaluated at first sweep and its value will
|
||||||
|
not be inserted into the temporary table. Instead, it's evaluated
|
||||||
|
when selecting from the temporary table. Therefore, it can't be freed
|
||||||
|
here even though it's not correlated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void JOIN::join_free()
|
||||||
{
|
{
|
||||||
SELECT_LEX_UNIT *unit;
|
SELECT_LEX_UNIT *unit;
|
||||||
SELECT_LEX *sl;
|
SELECT_LEX *sl;
|
||||||
DBUG_ENTER("JOIN::join_free");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Optimization: if not EXPLAIN and we are done with the JOIN,
|
Optimization: if not EXPLAIN and we are done with the JOIN,
|
||||||
free all tables.
|
free all tables.
|
||||||
*/
|
*/
|
||||||
full= full || (!select_lex->uncacheable && !thd->lex->describe);
|
bool full= (!select_lex->uncacheable && !thd->lex->describe);
|
||||||
|
bool can_unlock= full;
|
||||||
|
DBUG_ENTER("JOIN::join_free");
|
||||||
|
|
||||||
cleanup(full);
|
cleanup(full);
|
||||||
|
|
||||||
for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit())
|
for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit())
|
||||||
for (sl= unit->first_select(); sl; sl= sl->next_select())
|
for (sl= unit->first_select(); sl; sl= sl->next_select())
|
||||||
{
|
{
|
||||||
JOIN *join= sl->join;
|
Item_subselect *subselect= sl->master_unit()->item;
|
||||||
if (join)
|
bool full_local= full && (!subselect || subselect->is_evaluated());
|
||||||
join->join_free(full);
|
/*
|
||||||
|
If this join is evaluated, we can fully clean it up and clean up all
|
||||||
|
its underlying joins even if they are correlated -- they will not be
|
||||||
|
used any more anyway.
|
||||||
|
If this join is not yet evaluated, we still must clean it up to
|
||||||
|
close its table cursors -- it may never get evaluated, as in case of
|
||||||
|
... HAVING FALSE OR a IN (SELECT ...))
|
||||||
|
but all table cursors must be closed before the unlock.
|
||||||
|
*/
|
||||||
|
sl->cleanup_all_joins(full_local);
|
||||||
|
/* Can't unlock if at least one JOIN is still needed */
|
||||||
|
can_unlock= can_unlock && full_local;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We are not using tables anymore
|
We are not using tables anymore
|
||||||
Unlock all tables. We may be in an INSERT .... SELECT statement.
|
Unlock all tables. We may be in an INSERT .... SELECT statement.
|
||||||
*/
|
*/
|
||||||
if (full && lock && thd->lock && !(select_options & SELECT_NO_UNLOCK) &&
|
if (can_unlock && lock && thd->lock &&
|
||||||
|
!(select_options & SELECT_NO_UNLOCK) &&
|
||||||
!select_lex->subquery_in_having &&
|
!select_lex->subquery_in_having &&
|
||||||
(select_lex == (thd->lex->unit.fake_select_lex ?
|
(select_lex == (thd->lex->unit.fake_select_lex ?
|
||||||
thd->lex->unit.fake_select_lex : &thd->lex->select_lex)))
|
thd->lex->unit.fake_select_lex : &thd->lex->select_lex)))
|
||||||
@ -6059,7 +6113,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
|
|||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
join->join_free(0);
|
join->join_free();
|
||||||
|
|
||||||
if (send_row)
|
if (send_row)
|
||||||
{
|
{
|
||||||
@ -9004,7 +9058,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
|
|||||||
The following will unlock all cursors if the command wasn't an
|
The following will unlock all cursors if the command wasn't an
|
||||||
update command
|
update command
|
||||||
*/
|
*/
|
||||||
join->join_free(0); // Unlock all cursors
|
join->join_free(); // Unlock all cursors
|
||||||
if (join->result->send_eof())
|
if (join->result->send_eof())
|
||||||
rc= 1; // Don't send error
|
rc= 1; // Don't send error
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,7 @@ class JOIN :public Sql_alloc
|
|||||||
the end of execution in order to increase concurrency and reduce
|
the end of execution in order to increase concurrency and reduce
|
||||||
memory consumption.
|
memory consumption.
|
||||||
*/
|
*/
|
||||||
void join_free(bool full);
|
void join_free();
|
||||||
/* Cleanup this JOIN, possibly for reuse */
|
/* Cleanup this JOIN, possibly for reuse */
|
||||||
void cleanup(bool full);
|
void cleanup(bool full);
|
||||||
void clear();
|
void clear();
|
||||||
|
@ -720,3 +720,17 @@ bool st_select_lex::cleanup()
|
|||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void st_select_lex::cleanup_all_joins(bool full)
|
||||||
|
{
|
||||||
|
SELECT_LEX_UNIT *unit;
|
||||||
|
SELECT_LEX *sl;
|
||||||
|
|
||||||
|
if (join)
|
||||||
|
join->cleanup(full);
|
||||||
|
|
||||||
|
for (unit= first_inner_unit(); unit; unit= unit->next_unit())
|
||||||
|
for (sl= unit->first_select(); sl; sl= sl->next_select())
|
||||||
|
sl->cleanup_all_joins(full);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user