Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C

There is an optimization of DISTINCT in JOIN::optimize()
which depends on THD::used_tables value. Each SELECT statement
inside SP resets used_tables value(see mysql_select()) and it
leads to wrong result. The fix is to replace THD::used_tables
with LEX::used_tables.
This commit is contained in:
Sergey Glukhov 2011-08-02 11:33:45 +04:00
parent 7841c3f574
commit de3693a1cd
10 changed files with 69 additions and 20 deletions

View file

@ -7053,6 +7053,25 @@ init_connect
SET @@GLOBAL.init_connect= @old_init_connect;
DROP PROCEDURE p2;
DROP PROCEDURE p5;
#
# Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C
#
CREATE TABLE t1 (a INT, b INT, KEY(b));
CREATE TABLE t2 (c INT, d INT, KEY(c));
INSERT INTO t1 VALUES (1,1),(1,1),(1,2);
INSERT INTO t2 VALUES (1,1),(1,2);
CREATE FUNCTION f1() RETURNS INT DETERMINISTIC
BEGIN
DECLARE a int;
-- SQL statement inside
SELECT 1 INTO a;
RETURN a;
END $
SELECT COUNT(DISTINCT d) FROM t1, t2 WHERE a = c AND b = f1();
COUNT(DISTINCT d)
2
DROP FUNCTION f1;
DROP TABLE t1, t2;
# ------------------------------------------------------------------
# -- End of 5.1 tests
# ------------------------------------------------------------------

View file

@ -8350,6 +8350,33 @@ SET @@GLOBAL.init_connect= @old_init_connect;
DROP PROCEDURE p2;
DROP PROCEDURE p5;
--echo #
--echo # Bug#11766594 59736: SELECT DISTINCT.. INCORRECT RESULT WITH DETERMINISTIC FUNCTION IN WHERE C
--echo #
CREATE TABLE t1 (a INT, b INT, KEY(b));
CREATE TABLE t2 (c INT, d INT, KEY(c));
INSERT INTO t1 VALUES (1,1),(1,1),(1,2);
INSERT INTO t2 VALUES (1,1),(1,2);
DELIMITER $;
CREATE FUNCTION f1() RETURNS INT DETERMINISTIC
BEGIN
DECLARE a int;
-- SQL statement inside
SELECT 1 INTO a;
RETURN a;
END $
DELIMITER ;$
SELECT COUNT(DISTINCT d) FROM t1, t2 WHERE a = c AND b = f1();
DROP FUNCTION f1;
DROP TABLE t1, t2;
--echo # ------------------------------------------------------------------
--echo # -- End of 5.1 tests
--echo # ------------------------------------------------------------------

View file

@ -7576,7 +7576,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
sum_func_list)
item->split_sum_func(thd, ref_pointer_array, *sum_func_list);
thd->used_tables|= item->used_tables();
thd->lex->used_tables|= item->used_tables();
thd->lex->current_select->cur_pos_in_select_list++;
}
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
@ -7923,7 +7923,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
views and natural joins this update is performed inside the loop below.
*/
if (table)
thd->used_tables|= table->map;
thd->lex->used_tables|= table->map;
/*
Initialize a generic field iterator for the current table reference.
@ -8008,7 +8008,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
field_table= nj_col->table_ref->table;
if (field_table)
{
thd->used_tables|= field_table->map;
thd->lex->used_tables|= field_table->map;
field_table->covering_keys.intersect(field->part_of_key);
field_table->merge_keys.merge(field->part_of_key);
field_table->used_fields++;
@ -8016,7 +8016,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
}
}
else
thd->used_tables|= item->used_tables();
thd->lex->used_tables|= item->used_tables();
thd->lex->current_select->cur_pos_in_select_list++;
}
/*

View file

@ -649,7 +649,6 @@ THD::THD()
is_slave_error= thread_specific_used= FALSE;
hash_clear(&handler_tables_hash);
tmp_table=0;
used_tables=0;
cuted_fields= sent_row_count= row_count= 0L;
limit_found_rows= 0;
row_count_func= -1;

View file

@ -1734,13 +1734,6 @@ public:
*/
ha_rows examined_row_count;
/*
The set of those tables whose fields are referenced in all subqueries
of the query.
TODO: possibly this it is incorrect to have used tables in THD because
with more than one subquery, it is not clear what does the field mean.
*/
table_map used_tables;
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
/*

View file

@ -631,7 +631,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
lock_type= table_list->lock_type;
thd_proc_info(thd, "init");
thd->used_tables=0;
thd->lex->used_tables=0;
values= its++;
value_count= values->elements;
@ -779,7 +779,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
else
{
if (thd->used_tables) // Column used in values()
if (thd->lex->used_tables) // Column used in values()
restore_record(table,s->default_values); // Get empty record
else
{

View file

@ -360,6 +360,7 @@ void lex_start(THD *thd)
lex->server_options.port= -1;
lex->is_lex_started= TRUE;
lex->used_tables= 0;
DBUG_VOID_RETURN;
}

View file

@ -1836,6 +1836,16 @@ typedef struct st_lex : public Query_tables_list
uint create_select_pos;
bool create_select_in_comment;
/*
The set of those tables whose fields are referenced in all subqueries
of the query.
TODO: possibly this it is incorrect to have used tables in LEX because
with subquery, it is not clear what does the field mean. To fix this
we should aggregate used tables information for selected expressions
into the select_lex.
*/
table_map used_tables;
st_lex();
virtual ~st_lex()

View file

@ -1382,7 +1382,7 @@ static int mysql_test_select(Prepared_statement *stmt,
if (open_normal_and_derived_tables(thd, tables, 0))
goto error;
thd->used_tables= 0; // Updated by setup_fields
thd->lex->used_tables= 0; // Updated by setup_fields
/*
JOIN::prepare calls
@ -1551,7 +1551,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
if (specific_prepare && (*specific_prepare)(thd))
DBUG_RETURN(TRUE);
thd->used_tables= 0; // Updated by setup_fields
thd->lex->used_tables= 0; // Updated by setup_fields
/* Calls JOIN::prepare */
DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));

View file

@ -406,7 +406,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
if (!ref->fixed && ref->fix_fields(thd, 0))
return TRUE;
thd->used_tables|= item->used_tables();
thd->lex->used_tables|= item->used_tables();
}
return false;
}
@ -1632,7 +1632,7 @@ JOIN::optimize()
if (exec_tmp_table1->distinct)
{
table_map used_tables= thd->used_tables;
table_map used_tables= thd->lex->used_tables;
JOIN_TAB *last_join_tab= join_tab+tables-1;
do
{
@ -2526,7 +2526,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE);
thd_proc_info(thd, "init");
thd->used_tables=0; // Updated by setup_fields
thd->lex->used_tables=0; // Updated by setup_fields
err= join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, group, having, proc_param,
select_lex, unit);
@ -16949,7 +16949,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
need_order=0;
extra.append(STRING_WITH_LEN("; Using filesort"));
}
if (distinct & test_all_bits(used_tables,thd->used_tables))
if (distinct & test_all_bits(used_tables, thd->lex->used_tables))
extra.append(STRING_WITH_LEN("; Distinct"));
for (uint part= 0; part < tab->ref.key_parts; part++)