3346 Roy Lyseng 2011-03-01
Bug#11763382: Assertion 'inited=INDEX in sql/handler.h
Used to be bug#56080.
The problem occurs because some TABLE objects are associated with
the optimizer execution structures even after close_thread_tables()
has been called. In turn, this means that final cleanup of some
TABLE objects is done from within THD::cleanup_after_query().
At this point in time, the TABLE objects may already have been
given to another session, meaning that we may end another session's
index or table scan.
The safest solution seems to be to always perform a thorough cleanup
of all execution data structures, including releasing the TABLE
objects, before close_thread_tables() is called. In other words,
this will have to be done at the end of each execution.
However, this is currently not possible in the implementation of
subquery materialization. This part of the optimizer assigns some
objects, including a TABLE object, for the lifetime of the
statement object, and not for each execution. This gives an odd
situation, where some parts of the data structures are set up for
reuse and others are not. Combine this with the fact that the
user may decide to use another strategy for a subsequent
subquery execution, the "permanent" data structures may not even
be used later.
It is therefore reasonable to allocate materialization execution
structures for the lifetime of one execution. It means that the
functions init_permanent() and init_runtime() of class
subselect_hash_sj_engine are combined into one setup() function.
In addition, the function Item_subselect::cleanup() will be called
when the cleanup() function for the underlying query expression
is called, and this function will cleanup and destroy the
materialization data structures, so that the subquery item object
is ready for a new optimization process in the next round of
execution.
However, the EXPLAIN functionality relies on these data
structures being present when explaining a query containing
a materialized subquery. Thus, we have two conflicting
requirements for query cleanup:
- For maximum efficiency we should release resources as early as
possible after execution.
- We cannot release resources that are needed for EXPLAIN queries.
A partial solution to this problem is to split resource cleanup in two:
1. Delete JOIN objects as soon as possible - this makes sense
because TABLE objects are associated with them through JOIN_TAB.
JOIN::destroy() is extended so that associations to joined tables
are removed.
But notice that JOIN objects may be needed for EXPLAIN queries,
so sometimes deleting them must be delayed.
Setting free_join= 0 inside mysql_select() when SELECT_DESCRIBE
is part of select options is evidence of that.
2. Cleanup query expression objects (ie st_select_lex and
st_select_lex_unit) later (in practice at end of query execution).
One example: In mysql_explain_union() the call to unit->cleanup()
was deleted, so now it is done by the general cleanup.
The bug fix does only a limited amount of refactoring in this area.
mysql-test/r/optimizer_debug_sync.result
Results for new test case.
mysql-test/t/optimizer_debug_sync.test
Test case for bug. Requires the debug sync facility, and often fails
without the bug fix.
mysql-test/r/union.result
Change in EXPLAIN EXTENDED output that shows an optimized predicate
in the two query specifications within a UNION in a subquery.
AFAIK, this is similar to how a non-subquery is reported, so the
change is an improvement.
sql/ha_partition.cc
Comment change.
sql/item_subselect.cc
In Item_subselect::cleanup(), "new" engine is always deleted after
an execution.
Item_in_subselect::setup_engine() now builds the materialization
engine for one execution only. Specific arena is no longer needed.
In subselect_union_engine::cleanup(), call to
unit->reinit_exec_mechanism is deleted because it is redundant.
Implementation of cleanup() functions for subclasses of
subselect_engine has been refactored.
subselect_hash_sj_engine::setup() replaces init_permanent() and
init_runtime().
For class subselect_hash_sj_engine, freeing of the temporary
result table is moved from the destructor to ::cleanup().
Some changes necessary because of interface changes.
sql/item_subselect.h
Some cleanup of the subselect_engine classes:
- All virtual functions now marked virtual in all derived classes.
- Functions that returned bool result are now declared accordingly.
- Slight cleanup of private/protected/public performed.
- Improved constructors.
sql/sql_lex.h
Change in friend declaration.
sql/sql_select.cc
Function JOIN::reinit() is renamed to JOIN::reset().
JOIN::destroy() will now delete connection to associated tables.
The st_select_lex_unit object is no longer cleaned up as early as
before, but instead relying on the general cleanup of lex->unit
in mysql_execute_command().
sql/sql_select.h
A few small interface changes.
sql/sql_union.cc
Cleaned up some confusing error handling in st_select_lex_unit::exec()
and st_select_lex::cleanup().
added:
mysql-test/r/optimizer_debug_sync.result
mysql-test/t/optimizer_debug_sync.test
modified:
mysql-test/r/union.result
sql/ha_partition.cc
sql/item_subselect.cc
sql/item_subselect.h
sql/sql_lex.h
sql/sql_select.cc
sql/sql_select.h
sql/sql_union.cc
3345 Jorgen Loland 2011-02-17 [merge]
Null-merge mysql-trunk -> opt-backporting
=== added file 'mysql-test/r/optimizer_debug_sync.result'
--- a/mysql-test/r/optimizer_debug_sync.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/optimizer_debug_sync.result 2011-03-01 14:57:53 +0000
@@ -0,0 +1,23 @@
+
+BUG#11763382 Assertion 'inited==INDEX' on SELECT MAX(...)
+
+CREATE TABLE t(i INT NOT NULL PRIMARY KEY, f INT) ENGINE = InnoDB;
+INSERT INTO t VALUES (1,1),(2,2);
+BEGIN;
+UPDATE t SET f=100 WHERE i=2;
+set optimizer_switch='semijoin=off';
+SET DEBUG_SYNC='before_index_end_in_subselect WAIT_FOR callit';
+SELECT f FROM t WHERE i IN ( SELECT i FROM t );
+SELECT MAX(i) FROM t FOR UPDATE;
+SELECT MAX(i) FROM t FOR UPDATE;
+SET DEBUG_SYNC='now SIGNAL callit';
+COMMIT;
+f
+1
+2
+SET DEBUG_SYNC='RESET';
+MAX(i)
+2
+MAX(i)
+2
+DROP TABLE t;
=== modified file 'mysql-test/r/union.result'
--- a/mysql-test/r/union.result 2011-01-10 12:45:53 +0000
+++ b/mysql-test/r/union.result 2011-03-01 14:57:53 +0000
@@ -480,7 +480,7 @@ id select_type table type possible_keys
2 UNION t2 const PRIMARY PRIMARY 4 const 1 100.00
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 (select '1' AS `a`,'1' AS `b` from `test`.`t1` where ('1' = 1)) union (select '1' AS `a`,'10' AS `b` from `test`.`t2` where ('1' = 1))
+Note 1003 (select '1' AS `a`,'1' AS `b` from `test`.`t1` where 1) union (select '1' AS `a`,'10' AS `b` from `test`.`t2` where 1)
(select * from t1 where a=5) union (select * from t2 where a=1);
a b
1 10
=== added file 'mysql-test/t/optimizer_debug_sync.test'
--- a/mysql-test/t/optimizer_debug_sync.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/optimizer_debug_sync.test 2011-03-01 14:57:53 +0000
@@ -0,0 +1,60 @@
+# This test checks various optimizer-related functions using
+# the debug-sync facility.
+
+--source include/have_debug_sync.inc
+--source include/have_innodb.inc
+
+--echo
+--echo BUG#11763382 Assertion 'inited==INDEX' on SELECT MAX(...)
+--echo
+
+CREATE TABLE t(i INT NOT NULL PRIMARY KEY, f INT) ENGINE = InnoDB;
+INSERT INTO t VALUES (1,1),(2,2);
+
+BEGIN;
+UPDATE t SET f=100 WHERE i=2;
+
+--connect (con1,localhost,root,,)
+
+set optimizer_switch='semijoin=off';
+
+SET DEBUG_SYNC='before_index_end_in_subselect WAIT_FOR callit';
+--send SELECT f FROM t WHERE i IN ( SELECT i FROM t )
+
+--connection default
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'debug sync point: before_index_end_in_subselect';
+--source include/wait_show_condition.inc
+
+--connect (con2,localhost,root,,)
+--send SELECT MAX(i) FROM t FOR UPDATE
+
+--connect (con3,localhost,root,,)
+--send SELECT MAX(i) FROM t FOR UPDATE
+
+--connection default
+
+let $wait_condition=
+ SELECT COUNT(*) = 2 FROM information_schema.processlist
+ WHERE state = 'Optimizing' and info = 'SELECT MAX(i) FROM t FOR UPDATE';
+--source include/wait_condition.inc
+
+SET DEBUG_SYNC='now SIGNAL callit';
+COMMIT;
+
+--connection con1
+--reap
+SET DEBUG_SYNC='RESET';
+
+--connection con2
+--reap
+
+--connection con3
+--reap
+
+DROP TABLE t;
+
+--exit
+
+--echo # End of BUG#56080
=== modified file 'sql/ha_partition.cc'
--- a/sql/ha_partition.cc 2011-01-26 21:12:56 +0000
+++ b/sql/ha_partition.cc 2011-03-01 14:57:53 +0000
@@ -3369,7 +3369,7 @@ int ha_partition::delete_row(const uchar
Called from item_sum.cc by Item_func_group_concat::clear(),
Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
Called from sql_delete.cc by mysql_delete().
- Called from sql_select.cc by JOIN::reinit().
+ Called from sql_select.cc by JOIN::reset().
Called from sql_union.cc by st_select_lex_unit::exec().
*/
=== modified file 'sql/item_subselect.cc'
--- a/sql/item_subselect.cc 2011-01-18 11:42:09 +0000
+++ b/sql/item_subselect.cc 2011-03-01 14:57:53 +0000
@@ -38,6 +38,7 @@
#include "set_var.h"
#include "sql_select.h"
#include "sql_parse.h" // check_stack_overrun
+#include "debug_sync.h"
inline Item * and_items(Item* cond, Item *item)
{
@@ -120,7 +121,10 @@ void Item_subselect::cleanup()
if (old_engine)
{
if (engine)
+ {
engine->cleanup();
+ delete engine;
+ }
engine= old_engine;
old_engine= 0;
}
@@ -279,8 +283,6 @@ bool Item_subselect::walk(Item_processor
bool Item_subselect::exec()
{
- int res;
-
/*
Do not execute subselect in case of a fatal error
or if the query has been killed.
@@ -295,7 +297,7 @@ bool Item_subselect::exec()
*/
DBUG_EXECUTE_IF("subselect_exec_fail", return 1;);
- res= engine->exec();
+ const bool res= engine->exec();
if (engine_changed)
{
@@ -1886,18 +1888,13 @@ void Item_in_subselect::fix_after_pullou
/**
- Try to create an engine to compute the subselect via materialization,
- and if this fails, revert to execution via the IN=>EXISTS transformation.
+ Create an engine to compute the subquery via materialization.
@details
The purpose of this method is to hide the implementation details
of this Item's execution. The method creates a new engine for
materialized execution, and initializes the engine.
- The initialization of the new engine is divided in two parts - a permanent
- one that lives across prepared statements, and one that is repeated for each
- execution.
-
@returns
@retval TRUE memory allocation error occurred, or was not able to create
temporary table
@@ -1906,43 +1903,29 @@ void Item_in_subselect::fix_after_pullou
bool Item_in_subselect::setup_engine()
{
- subselect_hash_sj_engine *hash_engine;
DBUG_ENTER("Item_in_subselect::setup_engine");
- if (engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE)
- {
- /* Create/initialize objects in permanent memory. */
- Query_arena *arena= thd->stmt_arena;
- Query_arena backup;
- if (arena->is_conventional())
- arena= 0;
- else
- thd->set_n_backup_active_arena(arena, &backup);
+ /* The decision whether to materialize is already taken. */
+ DBUG_ASSERT(exec_method == Item_exists_subselect::EXEC_MATERIALIZATION);
- subselect_single_select_engine *old_engine=
- static_cast<subselect_single_select_engine*>(engine);
- if (!(hash_engine= new subselect_hash_sj_engine(thd, this,
- old_engine)) ||
- hash_engine->init_permanent(unit->get_unit_column_types()))
- {
- /*
- For some reason we cannot use materialization for this IN predicate.
- Delete all materialization-related objects, and return error.
- */
- delete hash_engine;
- if (arena)
- thd->restore_active_arena(arena, &backup);
- DBUG_RETURN(TRUE);
- }
- engine= hash_engine;
+ DBUG_ASSERT(engine->engine_type() == subselect_engine::SINGLE_SELECT_ENGINE);
- if (arena)
- thd->restore_active_arena(arena, &backup);
- }
- else
+ old_engine= engine;
+
+ if (!(engine= new subselect_hash_sj_engine(thd, this,
+ static_cast<subselect_single_select_engine*>(old_engine))))
+ DBUG_RETURN(true);
+ if (((subselect_hash_sj_engine *) engine)
+ ->setup(unit->get_unit_column_types()))
{
- DBUG_ASSERT(engine->engine_type() == subselect_engine::HASH_SJ_ENGINE);
- hash_engine= static_cast<subselect_hash_sj_engine*>(engine);
+ /*
+ For some reason we cannot use materialization for this IN predicate.
+ Delete all materialization-related objects, and return error.
+ */
+ delete engine;
+ engine= old_engine;
+ old_engine= NULL;
+ DBUG_RETURN(true);
}
/*
@@ -1955,10 +1938,7 @@ bool Item_in_subselect::setup_engine()
*/
unit->global_parameters->select_limit= NULL;
- /* Initializations done in runtime memory, repeated for each execution. */
- const bool res= hash_engine->init_runtime();
-
- DBUG_RETURN(res);
+ DBUG_RETURN(false);
}
@@ -2096,7 +2076,6 @@ void subselect_single_select_engine::cle
void subselect_union_engine::cleanup()
{
DBUG_ENTER("subselect_union_engine::cleanup");
- unit->reinit_exec_mechanism();
result->cleanup();
DBUG_VOID_RETURN;
}
@@ -2123,23 +2102,13 @@ bool subselect_union_engine::is_executed
FALSE - Otherwise
*/
-bool subselect_union_engine::no_rows()
+bool subselect_union_engine::no_rows() const
{
/* Check if we got any rows when reading UNION result from temp. table: */
return test(!unit->fake_select_lex->join->send_records);
}
-void subselect_uniquesubquery_engine::cleanup()
-{
- DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
- /* Tell handler we don't need the index anymore */
- if (tab->table->file->inited)
- tab->table->file->ha_index_end();
- DBUG_VOID_RETURN;
-}
-
-
subselect_union_engine::subselect_union_engine(st_select_lex_unit *u,
select_result_interceptor *result_arg,
Item_subselect *item_arg)
@@ -2176,7 +2145,7 @@ subselect_union_engine::subselect_union_
@retval 1 if error
*/
-int subselect_single_select_engine::prepare()
+bool subselect_single_select_engine::prepare()
{
if (prepared)
return 0;
@@ -2203,12 +2172,14 @@ int subselect_single_select_engine::prep
return 0;
}
-int subselect_union_engine::prepare()
+
+bool subselect_union_engine::prepare()
{
return unit->prepare(thd, result, SELECT_NO_UNLOCK);
}
-int subselect_uniquesubquery_engine::prepare()
+
+bool subselect_uniquesubquery_engine::prepare()
{
/* Should never be called. */
DBUG_ASSERT(FALSE);
@@ -2231,7 +2202,7 @@ int subselect_uniquesubquery_engine::pre
FALSE - Otherwise
*/
-bool subselect_single_select_engine::no_rows()
+bool subselect_single_select_engine::no_rows() const
{
return !item->assigned();
}
@@ -2301,7 +2272,7 @@ int rr_sequential(READ_RECORD *info);
int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info);
-int subselect_single_select_engine::exec()
+bool subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
int rc= 0;
@@ -2334,11 +2305,7 @@ int subselect_single_select_engine::exec
select_lex->uncacheable != UNCACHEABLE_EXPLAIN
&& executed)
{
- if (join->reinit())
- {
- rc= 1;
- goto exit;
- }
+ join->reset();
item->reset();
item->assigned((executed= 0));
}
@@ -2404,10 +2371,10 @@ exit:
DBUG_RETURN(rc);
}
-int subselect_union_engine::exec()
+bool subselect_union_engine::exec()
{
char const *save_where= thd->where;
- int res= unit->exec();
+ const bool res= unit->exec();
thd->where= save_where;
return res;
}
@@ -2431,7 +2398,7 @@ int subselect_union_engine::exec()
TRUE - Error
*/
-int subselect_uniquesubquery_engine::scan_table()
+bool subselect_uniquesubquery_engine::scan_table()
{
int error;
TABLE *table= tab->table;
@@ -2596,7 +2563,7 @@ bool subselect_uniquesubquery_engine::co
TRUE - an error occured while scanning
*/
-int subselect_uniquesubquery_engine::exec()
+bool subselect_uniquesubquery_engine::exec()
{
DBUG_ENTER("subselect_uniquesubquery_engine::exec");
int error;
@@ -2699,7 +2666,7 @@ int subselect_uniquesubquery_engine::exe
1
*/
-int subselect_indexsubquery_engine::exec()
+bool subselect_indexsubquery_engine::exec()
{
DBUG_ENTER("subselect_indexsubquery_engine::exec");
int error;
@@ -2786,26 +2753,26 @@ int subselect_indexsubquery_engine::exec
}
-uint subselect_single_select_engine::cols()
+uint subselect_single_select_engine::cols() const
{
return select_lex->item_list.elements;
}
-uint subselect_union_engine::cols()
+uint subselect_union_engine::cols() const
{
DBUG_ASSERT(unit->is_prepared()); // should be called after fix_fields()
return unit->types.elements;
}
-uint8 subselect_single_select_engine::uncacheable()
+uint8 subselect_single_select_engine::uncacheable() const
{
return select_lex->uncacheable;
}
-uint8 subselect_union_engine::uncacheable()
+uint8 subselect_union_engine::uncacheable() const
{
return unit->uncacheable;
}
@@ -2892,13 +2859,13 @@ subselect_single_select_engine::save_joi
return FALSE;
}
-table_map subselect_single_select_engine::upper_select_const_tables()
+table_map subselect_single_select_engine::upper_select_const_tables() const
{
return calc_const_tables(select_lex->outer_select()->leaf_tables);
}
-table_map subselect_union_engine::upper_select_const_tables()
+table_map subselect_union_engine::upper_select_const_tables() const
{
return calc_const_tables(unit->outer_select()->leaf_tables);
}
@@ -3066,7 +3033,7 @@ bool subselect_uniquesubquery_engine::ch
@retval
FALSE there are some tables in subquery
*/
-bool subselect_single_select_engine::no_tables()
+bool subselect_single_select_engine::no_tables() const
{
return(select_lex->table_list.elements == 0);
}
@@ -3082,7 +3049,7 @@ bool subselect_single_select_engine::no_
FALSE can guarantee that the subquery never return NULL
TRUE otherwise
*/
-bool subselect_single_select_engine::may_be_null()
+bool subselect_single_select_engine::may_be_null() const
{
return ((no_tables() && !join->conds && !join->having) ? maybe_null : 1);
}
@@ -3096,7 +3063,7 @@ bool subselect_single_select_engine::may
@retval
FALSE there are some tables in subquery
*/
-bool subselect_union_engine::no_tables()
+bool subselect_union_engine::no_tables() const
{
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
{
@@ -3116,7 +3083,7 @@ bool subselect_union_engine::no_tables()
FALSE there are some tables in subquery
*/
-bool subselect_uniquesubquery_engine::no_tables()
+bool subselect_uniquesubquery_engine::no_tables() const
{
/* returning value is correct, but this method should never be called */
return 0;
@@ -3129,8 +3096,7 @@ bool subselect_uniquesubquery_engine::no
/**
- Create all structures needed for IN execution that can live between PS
- reexecution.
+ Create all structures needed for subquery execution using hash semijoin.
@detail
- Create a temporary table to store the result of the IN subquery. The
@@ -3148,7 +3114,7 @@ bool subselect_uniquesubquery_engine::no
@retval FALSE otherwise
*/
-bool subselect_hash_sj_engine::init_permanent(List<Item> *tmp_columns)
+bool subselect_hash_sj_engine::setup(List<Item> *tmp_columns)
{
/* The result sink where we will materialize the subquery result. */
select_union *tmp_result_sink;
@@ -3158,7 +3124,7 @@ bool subselect_hash_sj_engine::init_perm
uint tmp_key_parts; /* Number of keyparts in tmp_key. */
Item_in_subselect *item_in= (Item_in_subselect *) item;
- DBUG_ENTER("subselect_hash_sj_engine::init_permanent");
+ DBUG_ENTER("subselect_hash_sj_engine::setup");
/* 1. Create/initialize materialization related objects. */
@@ -3204,8 +3170,8 @@ bool subselect_hash_sj_engine::init_perm
Make sure there is only one index on the temp table, and it doesn't have
the extra key part created when s->uniques > 0.
*/
- DBUG_ASSERT(tmp_table->s->keys == 1 && tmp_columns->elements == tmp_key_parts);
-
+ DBUG_ASSERT(tmp_table->s->keys == 1 &&
+ tmp_columns->elements == tmp_key_parts);
/* 2. Create/initialize execution related objects. */
@@ -3300,43 +3266,24 @@ bool subselect_hash_sj_engine::init_perm
if (cond->fix_fields(thd, &cond))
DBUG_RETURN(TRUE);
- DBUG_RETURN(FALSE);
-}
-
-
-/**
- Initialize members of the engine that need to be re-initilized at each
- execution.
-
- @retval TRUE if a memory allocation error occurred
- @retval FALSE if success
-*/
-
-bool subselect_hash_sj_engine::init_runtime()
-{
/*
Create and optimize the JOIN that will be used to materialize
the subquery if not yet created.
*/
materialize_engine->prepare();
- /*
- Repeat name resolution for 'cond' since cond is not part of any
- clause of the query, and it is not 'fixed' during JOIN::prepare.
- */
- if (cond && !cond->fixed && cond->fix_fields(thd, &cond))
- return TRUE;
/* Let our engine reuse this query plan for materialization. */
- materialize_join= materialize_engine->join;
- materialize_join->change_result(result);
- return FALSE;
+ materialize_engine->join->change_result(result);
+
+ DBUG_RETURN(FALSE);
}
subselect_hash_sj_engine::~subselect_hash_sj_engine()
{
+ /* Assure that cleanup has been called for this engine. */
+ DBUG_ASSERT(!tab);
+
delete result;
- if (tab)
- free_tmp_table(thd, tab->table);
}
@@ -3349,10 +3296,16 @@ subselect_hash_sj_engine::~subselect_has
void subselect_hash_sj_engine::cleanup()
{
- is_materialized= FALSE;
+ DBUG_ENTER("subselect_hash_sj_engine::cleanup");
+ is_materialized= false;
result->cleanup(); /* Resets the temp table as well. */
+ DEBUG_SYNC(thd, "before_index_end_in_subselect");
+ if (tab->table->file->inited)
+ tab->table->file->ha_index_end(); // Close the scan over the index
+ free_tmp_table(thd, tab->table);
+ tab= NULL;
materialize_engine->cleanup();
- subselect_uniquesubquery_engine::cleanup();
+ DBUG_VOID_RETURN;
}
@@ -3367,7 +3320,7 @@ void subselect_hash_sj_engine::cleanup()
@retval FALSE otherwise
*/
-int subselect_hash_sj_engine::exec()
+bool subselect_hash_sj_engine::exec()
{
Item_in_subselect *item_in= (Item_in_subselect *) item;
@@ -3379,17 +3332,17 @@ int subselect_hash_sj_engine::exec()
*/
if (!is_materialized)
{
- int res= 0;
+ bool res= false;
SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= materialize_engine->select_lex;
- if ((res= materialize_join->optimize()))
+ if ((res= materialize_engine->join->optimize()))
goto err; /* purecov: inspected */
if (materialize_engine->save_join_if_explain())
goto err;
- materialize_join->exec();
- if ((res= test(materialize_join->error || thd->is_fatal_error)))
+ materialize_engine->join->exec();
+ if ((res= test(materialize_engine->join->error || thd->is_fatal_error)))
goto err;
/*
=== modified file 'sql/item_subselect.h'
--- a/sql/item_subselect.h 2011-01-24 14:17:03 +0000
+++ b/sql/item_subselect.h 2011-03-01 14:57:53 +0000
@@ -44,7 +44,8 @@ typedef Comp_creator* (*chooser_compare_
class Item_subselect :public Item_result_field
{
- my_bool value_assigned; /* value already assigned to subselect */
+private:
+ bool value_assigned; /* value already assigned to subselect */
public:
/* thread handler, will be assigned in fix_fields only */
THD *thd;
@@ -109,13 +110,13 @@ public:
select_result_interceptor *result);
~Item_subselect();
- void cleanup();
+ virtual void cleanup();
virtual void reset()
{
null_value= 1;
}
virtual trans_res select_transformer(JOIN *join);
- bool assigned() { return value_assigned; }
+ bool assigned() const { return value_assigned; }
void assigned(bool a) { value_assigned= a; }
enum Type type() const;
bool is_null()
@@ -190,10 +191,10 @@ public:
Item_singlerow_subselect(st_select_lex *select_lex);
Item_singlerow_subselect() :Item_subselect(), value(0), row (0) {}
- void cleanup();
+ virtual void cleanup();
subs_type substype() { return SINGLEROW_SUBS; }
- void reset();
+ virtual void reset();
trans_res select_transformer(JOIN *join);
void store(uint i, Item* item);
double val_real();
@@ -240,7 +241,7 @@ public:
Item_maxmin_subselect(THD *thd, Item_subselect *parent,
st_select_lex *select_lex, bool max);
virtual void print(String *str, enum_query_type query_type);
- void cleanup();
+ virtual void cleanup();
bool any_value() { return was_values; }
void register_value() { was_values= TRUE; }
void reset_value_registration() { was_values= FALSE; }
@@ -292,7 +293,7 @@ public:
return RES_OK;
}
subs_type substype() { return EXISTS_SUBS; }
- void reset()
+ virtual void reset()
{
value= 0;
}
@@ -380,9 +381,9 @@ public:
optimizer(NULL), was_null(FALSE), abort_on_null(FALSE),
pushed_cond_guards(NULL), upper_item(NULL)
{}
- void cleanup();
+ virtual void cleanup();
subs_type substype() { return IN_SUBS; }
- void reset()
+ virtual void reset()
{
value= 0;
null_value= 0;
@@ -455,15 +456,13 @@ public:
INDEXSUBQUERY_ENGINE, HASH_SJ_ENGINE};
subselect_engine(Item_subselect *si, select_result_interceptor *res)
- :thd(0)
- {
- result= res;
- item= si;
- res_type= STRING_RESULT;
- res_field_type= MYSQL_TYPE_VAR_STRING;
- maybe_null= 0;
- }
+ :result(res), thd(NULL), item(si), res_type(STRING_RESULT),
+ res_field_type(MYSQL_TYPE_VAR_STRING), maybe_null(false)
+ {}
virtual ~subselect_engine() {}; // to satisfy compiler
+ /**
+ Cleanup engine after complete query execution, free all resources.
+ */
virtual void cleanup()= 0;
/*
@@ -471,8 +470,8 @@ public:
Should be called before prepare().
*/
void set_thd(THD *thd_arg);
- THD * get_thd() { return thd; }
- virtual int prepare()= 0;
+ THD * get_thd() const { return thd; }
+ virtual bool prepare()= 0;
virtual void fix_length_and_dec(Item_cache** row)= 0;
/*
Execute the engine
@@ -494,23 +493,23 @@ public:
1 - Either an execution error, or the engine was "changed", and the
caller should call exec() again for the new engine.
*/
- virtual int exec()= 0;
- virtual uint cols()= 0; /* return number of columns in select */
- virtual uint8 uncacheable()= 0; /* query is uncacheable */
- enum Item_result type() { return res_type; }
- enum_field_types field_type() { return res_field_type; }
+ virtual bool exec()= 0;
+ virtual uint cols() const = 0; /* return number of columns in select */
+ virtual uint8 uncacheable() const = 0; /* query is uncacheable */
+ virtual enum Item_result type() const { return res_type; }
+ virtual enum_field_types field_type() const { return res_field_type; }
virtual void exclude()= 0;
- virtual bool may_be_null() { return maybe_null; };
- virtual table_map upper_select_const_tables()= 0;
+ virtual bool may_be_null() const { return maybe_null; };
+ virtual table_map upper_select_const_tables() const = 0;
static table_map calc_const_tables(TABLE_LIST *);
virtual void print(String *str, enum_query_type query_type)= 0;
virtual bool change_result(Item_subselect *si,
select_result_interceptor *result)= 0;
- virtual bool no_tables()= 0;
+ virtual bool no_tables() const = 0;
virtual bool is_executed() const { return FALSE; }
/* Check if subquery produced any rows during last query execution */
- virtual bool no_rows() = 0;
- virtual enum_engine_type engine_type() { return ABSTRACT_ENGINE; }
+ virtual bool no_rows() const = 0;
+ virtual enum_engine_type engine_type() const { return ABSTRACT_ENGINE; }
protected:
void set_row(List<Item> &item_list, Item_cache **row);
@@ -519,30 +518,31 @@ protected:
class subselect_single_select_engine: public subselect_engine
{
- my_bool prepared; /* simple subselect is prepared */
- my_bool optimized; /* simple subselect is optimized */
- my_bool executed; /* simple subselect is executed */
+private:
+ bool prepared; /* simple subselect is prepared */
+ bool executed; /* simple subselect is executed */
st_select_lex *select_lex; /* corresponding select_lex */
JOIN * join; /* corresponding JOIN structure */
public:
subselect_single_select_engine(st_select_lex *select,
select_result_interceptor *result,
Item_subselect *item);
- void cleanup();
- int prepare();
- void fix_length_and_dec(Item_cache** row);
- int exec();
- uint cols();
- uint8 uncacheable();
- void exclude();
- table_map upper_select_const_tables();
+ virtual void cleanup();
+ virtual bool prepare();
+ virtual void fix_length_and_dec(Item_cache** row);
+ virtual bool exec();
+ virtual uint cols() const;
+ virtual uint8 uncacheable() const;
+ virtual void exclude();
+ virtual table_map upper_select_const_tables() const;
virtual void print (String *str, enum_query_type query_type);
- bool change_result(Item_subselect *si, select_result_interceptor *result);
- bool no_tables();
- bool may_be_null();
- bool is_executed() const { return executed; }
- bool no_rows();
- virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; }
+ virtual bool change_result(Item_subselect *si,
+ select_result_interceptor *result);
+ virtual bool no_tables() const;
+ virtual bool may_be_null() const;
+ virtual bool is_executed() const { return executed; }
+ virtual bool no_rows() const;
+ virtual enum_engine_type engine_type() const { return SINGLE_SELECT_ENGINE; }
bool save_join_if_explain();
friend class subselect_hash_sj_engine;
@@ -552,25 +552,28 @@ public:
class subselect_union_engine: public subselect_engine
{
- st_select_lex_unit *unit; /* corresponding unit structure */
public:
subselect_union_engine(st_select_lex_unit *u,
select_result_interceptor *result,
Item_subselect *item);
- void cleanup();
- int prepare();
- void fix_length_and_dec(Item_cache** row);
- int exec();
- uint cols();
- uint8 uncacheable();
- void exclude();
- table_map upper_select_const_tables();
+ virtual void cleanup();
+ virtual bool prepare();
+ virtual void fix_length_and_dec(Item_cache** row);
+ virtual bool exec();
+ virtual uint cols() const;
+ virtual uint8 uncacheable() const;
+ virtual void exclude();
+ virtual table_map upper_select_const_tables() const;
virtual void print (String *str, enum_query_type query_type);
- bool change_result(Item_subselect *si, select_result_interceptor *result);
- bool no_tables();
- bool is_executed() const;
- bool no_rows();
- virtual enum_engine_type engine_type() { return UNION_ENGINE; }
+ virtual bool change_result(Item_subselect *si,
+ select_result_interceptor *result);
+ virtual bool no_tables() const;
+ virtual bool is_executed() const;
+ virtual bool no_rows() const;
+ virtual enum_engine_type engine_type() const { return UNION_ENGINE; }
+
+private:
+ st_select_lex_unit *unit; /* corresponding unit structure */
};
@@ -614,26 +617,28 @@ public:
{
set_thd(thd_arg);
}
- void cleanup();
- int prepare();
- void fix_length_and_dec(Item_cache** row);
- int exec();
- uint cols() { return 1; }
- uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
- void exclude();
- table_map upper_select_const_tables() { return 0; }
+ virtual void cleanup() {}
+ virtual bool prepare();
+ virtual void fix_length_and_dec(Item_cache** row);
+ virtual bool exec();
+ virtual uint cols() const { return 1; }
+ virtual uint8 uncacheable() const { return UNCACHEABLE_DEPENDENT; }
+ virtual void exclude();
+ virtual table_map upper_select_const_tables() const { return 0; }
virtual void print (String *str, enum_query_type query_type);
- bool change_result(Item_subselect *si, select_result_interceptor *result);
- bool no_tables();
- int scan_table();
+ virtual bool change_result(Item_subselect *si,
+ select_result_interceptor *result);
+ virtual bool no_tables() const;
+ bool scan_table();
bool copy_ref_key();
- bool no_rows() { return empty_result_set; }
- virtual enum_engine_type engine_type() { return UNIQUESUBQUERY_ENGINE; }
+ virtual bool no_rows() const { return empty_result_set; }
+ virtual enum_engine_type engine_type() const { return UNIQUESUBQUERY_ENGINE; }
};
class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
{
+private:
/* FALSE for 'ref', TRUE for 'ref-or-null'. */
bool check_null;
/*
@@ -676,9 +681,9 @@ public:
check_null(chk_null),
having(having_arg)
{}
- int exec();
+ virtual bool exec();
virtual void print (String *str, enum_query_type query_type);
- virtual enum_engine_type engine_type() { return INDEXSUBQUERY_ENGINE; }
+ virtual enum_engine_type engine_type() const { return INDEXSUBQUERY_ENGINE; }
};
/*
@@ -711,21 +716,17 @@ inline bool Item_subselect::is_uncacheab
class subselect_hash_sj_engine: public subselect_uniquesubquery_engine
{
-protected:
+private:
/* TRUE if the subquery was materialized into a temp table. */
bool is_materialized;
/*
The old engine already chosen at parse time and stored in permanent memory.
- Through this member we can re-create and re-prepare materialize_join for
- each execution of a prepared statement. We also reuse the functionality
- of subselect_single_select_engine::[prepare | cols].
+ Through this member we can re-create and re-prepare the join object
+ used to materialize the subquery for each execution of a prepared
+ statement. We also reuse the functionality of
+ subselect_single_select_engine::[prepare | cols].
*/
subselect_single_select_engine *materialize_engine;
- /*
- QEP to execute the subquery and materialize its result into a
- temporary table. Created during the first call to exec().
- */
- JOIN *materialize_join;
/* Temp table context of the outer select's JOIN. */
TMP_TABLE_PARAM *tmp_param;
@@ -734,23 +735,22 @@ public:
subselect_single_select_engine *old_engine)
:subselect_uniquesubquery_engine(thd, NULL, in_predicate, NULL),
is_materialized(FALSE), materialize_engine(old_engine),
- materialize_join(NULL), tmp_param(NULL)
+ tmp_param(NULL)
{}
~subselect_hash_sj_engine();
- bool init_permanent(List<Item> *tmp_columns);
- bool init_runtime();
- void cleanup();
- int prepare()
+ bool setup(List<Item> *tmp_columns);
+ virtual void cleanup();
+ virtual bool prepare()
{
return materialize_engine->prepare();
}
- int exec();
- void print (String *str, enum_query_type query_type);
- uint cols()
+ virtual bool exec();
+ virtual void print (String *str, enum_query_type query_type);
+ virtual uint cols() const
{
return materialize_engine->cols();
}
- virtual enum_engine_type engine_type() { return HASH_SJ_ENGINE; }
+ virtual enum_engine_type engine_type() const { return HASH_SJ_ENGINE; }
};
#endif /* ITEM_SUBSELECT_INCLUDED */
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h 2011-02-16 17:13:30 +0000
+++ b/sql/sql_lex.h 2011-03-01 14:57:53 +0000
@@ -595,7 +595,7 @@ public:
inline bool is_union ();
friend void lex_start(THD *thd);
- friend int subselect_union_engine::exec();
+ friend bool subselect_union_engine::exec();
List<Item> *get_unit_column_types();
};
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc 2011-02-16 09:31:18 +0000
+++ b/sql/sql_select.cc 2011-03-01 14:57:53 +0000
@@ -2641,10 +2641,14 @@ void JOIN::restore_tmp()
}
-int
-JOIN::reinit()
+/**
+ Reset the state of this join object so that it is ready for a
+ new execution.
+*/
+
+void JOIN::reset()
{
- DBUG_ENTER("JOIN::reinit");
+ DBUG_ENTER("JOIN::reset");
unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
select_lex->offset_limit->val_uint() :
@@ -2695,7 +2699,7 @@ JOIN::reinit()
if (!(select_options & SELECT_DESCRIBE))
init_ftfuncs(thd, select_lex, test(order));
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
/**
@@ -3390,14 +3394,12 @@ JOIN::exec()
/**
- Clean up join.
+ Clean up and destroy join object.
- @return
- Return error that hold JOIN.
+ @return false if previous execution was successful, and true otherwise
*/
-int
-JOIN::destroy()
+bool JOIN::destroy()
{
DBUG_ENTER("JOIN::destroy");
select_lex->join= 0;
@@ -3423,6 +3425,11 @@ JOIN::destroy()
cond_equal= 0;
cleanup(1);
+ if (join_tab)
+ {
+ for (JOIN_TAB *tab= join_tab; tab < join_tab+tables; tab++)
+ tab->table= NULL;
+ }
/* Cleanup items referencing temporary table columns */
cleanup_item_list(tmp_all_fields1);
cleanup_item_list(tmp_all_fields3);
@@ -3439,7 +3446,7 @@ JOIN::destroy()
delete_dynamic(&keyuse);
delete procedure;
- DBUG_RETURN(error);
+ DBUG_RETURN(test(error));
}
@@ -3535,8 +3542,8 @@ mysql_select(THD *thd, Item ***rref_poin
subselect execution. So we need to restore them.
*/
Item_subselect *subselect= select_lex->master_unit()->item;
- if (subselect && subselect->is_uncacheable() && join->reinit())
- DBUG_RETURN(TRUE);
+ if (subselect && subselect->is_uncacheable())
+ join->reset();
}
else
{
@@ -3588,6 +3595,8 @@ mysql_select(THD *thd, Item ***rref_poin
select_lex->where= join->conds_history;
select_lex->having= join->having_history;
}
+ if (select_options & SELECT_DESCRIBE)
+ free_join= 0;
err:
if (free_join)
@@ -11357,7 +11366,12 @@ bool error_if_full_join(JOIN *join)
/**
- cleanup JOIN_TAB.
+ Cleanup table of join operation.
+
+ @note
+ Notice that this is not a complete cleanup. In some situations, the
+ object may be reused after a cleanup operation, hence we cannot set
+ the table pointer to NULL in this function.
*/
void JOIN_TAB::cleanup()
@@ -23304,7 +23318,6 @@ bool mysql_explain_union(THD *thd, SELEC
unit->fake_select_lex->options|= SELECT_DESCRIBE;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec();
- res|= unit->cleanup();
}
else
{
=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h 2011-02-08 15:49:51 +0000
+++ b/sql/sql_select.h 2011-03-01 14:57:53 +0000
@@ -1918,9 +1918,9 @@ public:
Item *having, ORDER *proc_param, SELECT_LEX *select,
SELECT_LEX_UNIT *unit);
int optimize();
- int reinit();
+ void reset();
void exec();
- int destroy();
+ bool destroy();
void restore_tmp();
bool alloc_func_list();
bool flatten_subqueries();
=== modified file 'sql/sql_union.cc'
--- a/sql/sql_union.cc 2010-11-11 09:40:06 +0000
+++ b/sql/sql_union.cc 2011-03-01 14:57:53 +0000
@@ -214,16 +214,16 @@ bool st_select_lex_unit::prepare(THD *th
/* fast reinit for EXPLAIN */
for (sl= first_sl; sl; sl= sl->next_select())
{
- sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
- if (!sl->join->procedure &&
- result->prepare(sl->join->fields_list, this))
- {
- DBUG_RETURN(TRUE);
- }
- sl->join->select_options|= SELECT_DESCRIBE;
- sl->join->reinit();
+ sl->join->result= result;
+ select_limit_cnt= HA_POS_ERROR;
+ offset_limit_cnt= 0;
+ if (!sl->join->procedure &&
+ result->prepare(sl->join->fields_list, this))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ sl->join->select_options|= SELECT_DESCRIBE;
+ sl->join->reset();
}
}
DBUG_RETURN(FALSE);
@@ -469,8 +469,8 @@ bool st_select_lex_unit::exec()
DBUG_ENTER("st_select_lex_unit::exec");
if (executed && !uncacheable && !describe)
- DBUG_RETURN(FALSE);
- executed= 1;
+ DBUG_RETURN(false);
+ executed= true;
if (uncacheable || !item || !item->assigned() || describe)
{
@@ -496,19 +496,22 @@ bool st_select_lex_unit::exec()
thd->lex->current_select= sl;
if (optimized)
- saved_error= sl->join->reinit();
+ {
+ saved_error= false;
+ sl->join->reset();
+ }
else
{
set_limit(sl);
- if (sl == global_parameters || describe)
- {
- offset_limit_cnt= 0;
- /*
- We can't use LIMIT at this stage if we are using ORDER BY for the
- whole query
- */
- if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ if (sl == global_parameters || describe)
+ {
+ offset_limit_cnt= 0;
+ /*
+ We can't use LIMIT at this stage if we are using ORDER BY for the
+ whole query
+ */
+ if (sl->order_list.first || describe)
+ select_limit_cnt= HA_POS_ERROR;
}
/*
@@ -520,62 +523,62 @@ bool st_select_lex_unit::exec()
(select_limit_cnt == HA_POS_ERROR || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
- saved_error= sl->join->optimize();
+ saved_error= sl->join->optimize();
}
if (!saved_error)
{
- records_at_start= table->file->stats.records;
- sl->join->exec();
+ records_at_start= table->file->stats.records;
+ sl->join->exec();
if (sl == union_distinct)
- {
- if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
- DBUG_RETURN(TRUE);
- table->no_keyread=1;
- }
- saved_error= sl->join->error;
- offset_limit_cnt= (ha_rows)(sl->offset_limit ?
+ {
+ if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
+ DBUG_RETURN(true);
+ table->no_keyread=1;
+ }
+ saved_error= sl->join->error;
+ offset_limit_cnt= (ha_rows)(sl->offset_limit ?
sl->offset_limit->val_uint() :
0);
- if (!saved_error)
- {
- examined_rows+= thd->examined_row_count;
- if (union_result->flush())
- {
- thd->lex->current_select= lex_select_save;
- DBUG_RETURN(1);
- }
- }
+ if (!saved_error)
+ {
+ examined_rows+= thd->examined_row_count;
+ if (union_result->flush())
+ {
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(true);
+ }
+ }
}
if (saved_error)
{
- thd->lex->current_select= lex_select_save;
- DBUG_RETURN(saved_error);
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(saved_error);
}
/* Needed for the following test and for records_at_start in next loop */
int error= table->file->info(HA_STATUS_VARIABLE);
if(error)
{
table->file->print_error(error, MYF(0));
- DBUG_RETURN(1);
+ DBUG_RETURN(true);
}
if (found_rows_for_union && !sl->braces &&
select_limit_cnt != HA_POS_ERROR)
{
- /*
- This is a union without braces. Remember the number of rows that
- could also have been part of the result set.
- We get this from the difference of between total number of possible
- rows and actual rows added to the temporary table.
- */
- add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
- ((table->file->stats.records - records_at_start)));
+ /*
+ This is a union without braces. Remember the number of rows that
+ could also have been part of the result set.
+ We get this from the difference of between total number of possible
+ rows and actual rows added to the temporary table.
+ */
+ add_rows+= (ulonglong) (thd->limit_found_rows -
+ (ulonglong)(table->file->stats.records - records_at_start));
}
}
}
- optimized= 1;
+ optimized= true;
/* Send result to 'result' */
- saved_error= TRUE;
+ saved_error= true;
{
List<Item_func_match> empty_list;
empty_list.empty();
@@ -587,27 +590,27 @@ bool st_select_lex_unit::exec()
JOIN *join= fake_select_lex->join;
if (!join)
{
- /*
- allocate JOIN for fake select only once (prevent
- mysql_select automatic allocation)
+ /*
+ allocate JOIN for fake select only once (prevent
+ mysql_select automatic allocation)
TODO: The above is nonsense. mysql_select() will not allocate the
join if one already exists. There must be some other reason why we
don't let it allocate the join. Perhaps this is because we need
some special parameter values passed to join constructor?
*/
- if (!(fake_select_lex->join= new JOIN(thd, item_list,
- fake_select_lex->options, result)))
+ if (!(fake_select_lex->join=
+ new JOIN(thd, item_list, fake_select_lex->options, result)))
{
- fake_select_lex->table_list.empty();
- DBUG_RETURN(TRUE);
+ fake_select_lex->table_list.empty();
+ DBUG_RETURN(true);
}
- fake_select_lex->join->no_const_tables= TRUE;
+ fake_select_lex->join->no_const_tables= true;
- /*
- Fake st_select_lex should have item list for correctref_array
- allocation.
- */
- fake_select_lex->item_list= item_list;
+ /*
+ Fake st_select_lex should have item list for correctref_array
+ allocation.
+ */
+ fake_select_lex->item_list= item_list;
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
0, item_list, NULL,
@@ -630,7 +633,7 @@ bool st_select_lex_unit::exec()
subquery execution rather than EXPLAIN line production. In order
to reset them back, we re-do all of the actions (yes it is ugly):
*/
- join->init(thd, item_list, fake_select_lex->options, result);
+ join->init(thd, item_list, fake_select_lex->options, result);
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
0, item_list, NULL,
@@ -643,7 +646,8 @@ bool st_select_lex_unit::exec()
else
{
join->examined_rows= 0;
- saved_error= join->reinit();
+ saved_error= false;
+ join->reset();
join->exec();
}
}
@@ -655,8 +659,8 @@ bool st_select_lex_unit::exec()
thd->examined_row_count+= examined_rows;
}
/*
- Mark for slow query log if any of the union parts didn't use
- indexes efficiently
+ Mark for slow query log if any of the union parts didn't use
+ indexes efficiently
*/
}
}
@@ -665,16 +669,25 @@ bool st_select_lex_unit::exec()
}
+/**
+ Cleanup this query expression object after preparation or one round
+ of execution. After the cleanup, the object can be reused for a
+ new round of execution, but a new optimization will be needed before
+ the execution.
+
+ @return false if previous execution was successful, and true otherwise
+*/
+
bool st_select_lex_unit::cleanup()
{
- int error= 0;
+ bool error= false;
DBUG_ENTER("st_select_lex_unit::cleanup");
if (cleaned)
{
DBUG_RETURN(FALSE);
}
- cleaned= 1;
+ cleaned= true;
if (union_result)
{
@@ -812,6 +825,13 @@ List<Item> *st_select_lex_unit::get_unit
return &sl->item_list;
}
+
+/**
+ Cleanup after preparation or one round of execution.
+
+ @return false if previous execution was successful, and true otherwise
+*/
+
bool st_select_lex::cleanup()
{
bool error= FALSE;
@@ -827,7 +847,7 @@ bool st_select_lex::cleanup()
for (SELECT_LEX_UNIT *lex_unit= first_inner_unit(); lex_unit ;
lex_unit= lex_unit->next_unit())
{
- error= (bool) ((uint) error | (uint) lex_unit->cleanup());
+ error|= lex_unit->cleanup();
}
non_agg_fields.empty();
inner_refs_list.empty();
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (roy.lyseng:3345 to 3346) Bug#11763382 | Roy Lyseng | 1 Mar |