Commit 3468b55a authored by Sergey Glukhov's avatar Sergey Glukhov

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.


mysql-test/r/sp.result:
  test case
mysql-test/t/sp.test:
  test case
sql/sql_base.cc:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_class.cc:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_class.h:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_insert.cc:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_lex.cc:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_lex.h:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_prepare.cc:
  THD::used_tables is replaced with LEX::used_tables
sql/sql_select.cc:
  THD::used_tables is replaced with LEX::used_tables
parent 58cf757f
...@@ -7053,6 +7053,25 @@ init_connect ...@@ -7053,6 +7053,25 @@ init_connect
SET @@GLOBAL.init_connect= @old_init_connect; SET @@GLOBAL.init_connect= @old_init_connect;
DROP PROCEDURE p2; DROP PROCEDURE p2;
DROP PROCEDURE p5; 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 # -- End of 5.1 tests
# ------------------------------------------------------------------ # ------------------------------------------------------------------
...@@ -8350,6 +8350,33 @@ SET @@GLOBAL.init_connect= @old_init_connect; ...@@ -8350,6 +8350,33 @@ SET @@GLOBAL.init_connect= @old_init_connect;
DROP PROCEDURE p2; DROP PROCEDURE p2;
DROP PROCEDURE p5; 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 # ------------------------------------------------------------------
--echo # -- End of 5.1 tests --echo # -- End of 5.1 tests
--echo # ------------------------------------------------------------------ --echo # ------------------------------------------------------------------
...@@ -7576,7 +7576,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, ...@@ -7576,7 +7576,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
sum_func_list) sum_func_list)
item->split_sum_func(thd, ref_pointer_array, *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->cur_pos_in_select_list++;
} }
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup; 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, ...@@ -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. views and natural joins this update is performed inside the loop below.
*/ */
if (table) if (table)
thd->used_tables|= table->map; thd->lex->used_tables|= table->map;
/* /*
Initialize a generic field iterator for the current table reference. 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, ...@@ -8008,7 +8008,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
field_table= nj_col->table_ref->table; field_table= nj_col->table_ref->table;
if (field_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->covering_keys.intersect(field->part_of_key);
field_table->merge_keys.merge(field->part_of_key); field_table->merge_keys.merge(field->part_of_key);
field_table->used_fields++; field_table->used_fields++;
...@@ -8016,7 +8016,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, ...@@ -8016,7 +8016,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
} }
} }
else else
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->cur_pos_in_select_list++;
} }
/* /*
......
...@@ -649,7 +649,6 @@ THD::THD() ...@@ -649,7 +649,6 @@ THD::THD()
is_slave_error= thread_specific_used= FALSE; is_slave_error= thread_specific_used= FALSE;
hash_clear(&handler_tables_hash); hash_clear(&handler_tables_hash);
tmp_table=0; tmp_table=0;
used_tables=0;
cuted_fields= sent_row_count= row_count= 0L; cuted_fields= sent_row_count= row_count= 0L;
limit_found_rows= 0; limit_found_rows= 0;
row_count_func= -1; row_count_func= -1;
......
...@@ -1734,13 +1734,6 @@ public: ...@@ -1734,13 +1734,6 @@ public:
*/ */
ha_rows examined_row_count; 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; USER_CONN *user_connect;
CHARSET_INFO *db_charset; CHARSET_INFO *db_charset;
/* /*
......
...@@ -631,7 +631,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -631,7 +631,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
lock_type= table_list->lock_type; lock_type= table_list->lock_type;
thd_proc_info(thd, "init"); thd_proc_info(thd, "init");
thd->used_tables=0; thd->lex->used_tables=0;
values= its++; values= its++;
value_count= values->elements; value_count= values->elements;
...@@ -779,7 +779,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -779,7 +779,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
} }
else 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 restore_record(table,s->default_values); // Get empty record
else else
{ {
......
...@@ -360,6 +360,7 @@ void lex_start(THD *thd) ...@@ -360,6 +360,7 @@ void lex_start(THD *thd)
lex->server_options.port= -1; lex->server_options.port= -1;
lex->is_lex_started= TRUE; lex->is_lex_started= TRUE;
lex->used_tables= 0;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -1836,6 +1836,16 @@ typedef struct st_lex : public Query_tables_list ...@@ -1836,6 +1836,16 @@ typedef struct st_lex : public Query_tables_list
uint create_select_pos; uint create_select_pos;
bool create_select_in_comment; 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(); st_lex();
virtual ~st_lex() virtual ~st_lex()
......
...@@ -1382,7 +1382,7 @@ static int mysql_test_select(Prepared_statement *stmt, ...@@ -1382,7 +1382,7 @@ static int mysql_test_select(Prepared_statement *stmt,
if (open_normal_and_derived_tables(thd, tables, 0)) if (open_normal_and_derived_tables(thd, tables, 0))
goto error; goto error;
thd->used_tables= 0; // Updated by setup_fields thd->lex->used_tables= 0; // Updated by setup_fields
/* /*
JOIN::prepare calls JOIN::prepare calls
...@@ -1551,7 +1551,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt, ...@@ -1551,7 +1551,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
if (specific_prepare && (*specific_prepare)(thd)) if (specific_prepare && (*specific_prepare)(thd))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
thd->used_tables= 0; // Updated by setup_fields thd->lex->used_tables= 0; // Updated by setup_fields
/* Calls JOIN::prepare */ /* Calls JOIN::prepare */
DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option)); DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
......
...@@ -406,7 +406,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, ...@@ -406,7 +406,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
if (!ref->fixed && ref->fix_fields(thd, 0)) if (!ref->fixed && ref->fix_fields(thd, 0))
return TRUE; return TRUE;
thd->used_tables|= item->used_tables(); thd->lex->used_tables|= item->used_tables();
} }
return false; return false;
} }
...@@ -1632,7 +1632,7 @@ JOIN::optimize() ...@@ -1632,7 +1632,7 @@ JOIN::optimize()
if (exec_tmp_table1->distinct) 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; JOIN_TAB *last_join_tab= join_tab+tables-1;
do do
{ {
...@@ -2526,7 +2526,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array, ...@@ -2526,7 +2526,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
if (!(join= new JOIN(thd, fields, select_options, result))) if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
thd_proc_info(thd, "init"); 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, err= join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, group, having, proc_param, conds, og_num, order, group, having, proc_param,
select_lex, unit); select_lex, unit);
...@@ -16949,7 +16949,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -16949,7 +16949,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
need_order=0; need_order=0;
extra.append(STRING_WITH_LEN("; Using filesort")); 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")); extra.append(STRING_WITH_LEN("; Distinct"));
for (uint part= 0; part < tab->ref.key_parts; part++) for (uint part= 0; part < tab->ref.key_parts; part++)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment