Bug#11765713 58705: OPTIMIZER LET ENGINE DEPEND ON UNINITIALIZED VALUES CREATED BY OPT_SUM_QU
Valgrind warnings were caused by comparing index values to an un-initialized field. mysql-test/r/subselect.result: New test cases. mysql-test/t/subselect.test: New test cases. sql/opt_sum.cc: Add thd to opt_sum_query enabling it to test for errors. If we have a non-nullable index, we cannot use it to match null values, since set_null() will be ignored, and we might compare uninitialized data. sql/sql_select.cc: Add thd to opt_sum_query, enabling it to test for errors. sql/sql_select.h: Add thd to opt_sum_query, enabling it to test for errors.
This commit is contained in:
parent
7634e724e7
commit
dd3d9477b2
@ -4734,3 +4734,21 @@ SELECT * FROM t2 UNION SELECT * FROM t2
|
|||||||
ORDER BY (SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE));
|
ORDER BY (SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abc' IN BOOLEAN MODE));
|
||||||
DROP TABLE t1,t2;
|
DROP TABLE t1,t2;
|
||||||
End of 5.1 tests
|
End of 5.1 tests
|
||||||
|
#
|
||||||
|
# Bug #11765713 58705:
|
||||||
|
# OPTIMIZER LET ENGINE DEPEND ON UNINITIALIZED VALUES
|
||||||
|
# CREATED BY OPT_SUM_QUERY
|
||||||
|
#
|
||||||
|
CREATE TABLE t1(a INT NOT NULL, KEY (a));
|
||||||
|
INSERT INTO t1 VALUES (0), (1);
|
||||||
|
SELECT 1 as foo FROM t1 WHERE a < SOME
|
||||||
|
(SELECT a FROM t1 WHERE a <=>
|
||||||
|
(SELECT a FROM t1)
|
||||||
|
);
|
||||||
|
ERROR 21000: Subquery returns more than 1 row
|
||||||
|
SELECT 1 as foo FROM t1 WHERE a < SOME
|
||||||
|
(SELECT a FROM t1 WHERE a <=>
|
||||||
|
(SELECT a FROM t1 where a is null)
|
||||||
|
);
|
||||||
|
foo
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -3726,3 +3726,25 @@ DROP TABLE t1,t2;
|
|||||||
--enable_result_log
|
--enable_result_log
|
||||||
|
|
||||||
--echo End of 5.1 tests
|
--echo End of 5.1 tests
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Bug #11765713 58705:
|
||||||
|
--echo # OPTIMIZER LET ENGINE DEPEND ON UNINITIALIZED VALUES
|
||||||
|
--echo # CREATED BY OPT_SUM_QUERY
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1(a INT NOT NULL, KEY (a));
|
||||||
|
INSERT INTO t1 VALUES (0), (1);
|
||||||
|
|
||||||
|
--error ER_SUBQUERY_NO_1_ROW
|
||||||
|
SELECT 1 as foo FROM t1 WHERE a < SOME
|
||||||
|
(SELECT a FROM t1 WHERE a <=>
|
||||||
|
(SELECT a FROM t1)
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT 1 as foo FROM t1 WHERE a < SOME
|
||||||
|
(SELECT a FROM t1 WHERE a <=>
|
||||||
|
(SELECT a FROM t1 where a is null)
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -211,6 +211,7 @@ static int get_index_max_value(TABLE *table, TABLE_REF *ref, uint range_fl)
|
|||||||
/**
|
/**
|
||||||
Substitutes constants for some COUNT(), MIN() and MAX() functions.
|
Substitutes constants for some COUNT(), MIN() and MAX() functions.
|
||||||
|
|
||||||
|
@param thd thread handler
|
||||||
@param tables list of leaves of join table tree
|
@param tables list of leaves of join table tree
|
||||||
@param all_fields All fields to be returned
|
@param all_fields All fields to be returned
|
||||||
@param conds WHERE clause
|
@param conds WHERE clause
|
||||||
@ -228,9 +229,12 @@ static int get_index_max_value(TABLE *table, TABLE_REF *ref, uint range_fl)
|
|||||||
HA_ERR_KEY_NOT_FOUND on impossible conditions
|
HA_ERR_KEY_NOT_FOUND on impossible conditions
|
||||||
@retval
|
@retval
|
||||||
HA_ERR_... if a deadlock or a lock wait timeout happens, for example
|
HA_ERR_... if a deadlock or a lock wait timeout happens, for example
|
||||||
|
@retval
|
||||||
|
ER_... e.g. ER_SUBQUERY_NO_1_ROW
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
int opt_sum_query(THD *thd,
|
||||||
|
TABLE_LIST *tables, List<Item> &all_fields, COND *conds)
|
||||||
{
|
{
|
||||||
List_iterator_fast<Item> it(all_fields);
|
List_iterator_fast<Item> it(all_fields);
|
||||||
int const_result= 1;
|
int const_result= 1;
|
||||||
@ -242,6 +246,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
Item *item;
|
Item *item;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
DBUG_ENTER("opt_sum_query");
|
||||||
|
|
||||||
if (conds)
|
if (conds)
|
||||||
where_tables= conds->used_tables();
|
where_tables= conds->used_tables();
|
||||||
|
|
||||||
@ -269,7 +275,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
WHERE t2.field IS NULL;
|
WHERE t2.field IS NULL;
|
||||||
*/
|
*/
|
||||||
if (tl->table->map & where_tables)
|
if (tl->table->map & where_tables)
|
||||||
return 0;
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
used_tables|= tl->table->map;
|
used_tables|= tl->table->map;
|
||||||
@ -297,7 +303,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
{
|
{
|
||||||
tl->table->file->print_error(error, MYF(0));
|
tl->table->file->print_error(error, MYF(0));
|
||||||
tl->table->in_use->fatal_error();
|
tl->table->in_use->fatal_error();
|
||||||
return error;
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
count*= tl->table->file->stats.records;
|
count*= tl->table->file->stats.records;
|
||||||
}
|
}
|
||||||
@ -390,10 +396,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
|
if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
|
||||||
return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE
|
DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); // No rows matching WHERE
|
||||||
/* HA_ERR_LOCK_DEADLOCK or some other error */
|
/* HA_ERR_LOCK_DEADLOCK or some other error */
|
||||||
table->file->print_error(error, MYF(0));
|
table->file->print_error(error, MYF(0));
|
||||||
return(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
removed_tables|= table->map;
|
removed_tables|= table->map;
|
||||||
}
|
}
|
||||||
@ -437,6 +443,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
const_result= 0;
|
const_result= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thd->is_error())
|
||||||
|
DBUG_RETURN(thd->main_da.sql_errno());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If we have a where clause, we can only ignore searching in the
|
If we have a where clause, we can only ignore searching in the
|
||||||
tables if MIN/MAX optimisation replaced all used tables
|
tables if MIN/MAX optimisation replaced all used tables
|
||||||
@ -446,7 +456,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
|
|||||||
*/
|
*/
|
||||||
if (removed_tables && used_tables != removed_tables)
|
if (removed_tables && used_tables != removed_tables)
|
||||||
const_result= 0; // We didn't remove all tables
|
const_result= 0; // We didn't remove all tables
|
||||||
return const_result;
|
DBUG_RETURN(const_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -732,6 +742,12 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
|
|||||||
|
|
||||||
if (is_null || (is_null_safe_eq && args[1]->is_null()))
|
if (is_null || (is_null_safe_eq && args[1]->is_null()))
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
If we have a non-nullable index, we cannot use it,
|
||||||
|
since set_null will be ignored, and we will compare uninitialized data.
|
||||||
|
*/
|
||||||
|
if (!part->field->real_maybe_null())
|
||||||
|
DBUG_RETURN(false);
|
||||||
part->field->set_null();
|
part->field->set_null();
|
||||||
*key_ptr= (uchar) 1;
|
*key_ptr= (uchar) 1;
|
||||||
}
|
}
|
||||||
@ -802,8 +818,9 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
|
|||||||
@param[out] prefix_len Length of prefix for the search range
|
@param[out] prefix_len Length of prefix for the search range
|
||||||
|
|
||||||
@note
|
@note
|
||||||
This function may set table->key_read to 1, which must be reset after
|
This function may set field->table->key_read to true,
|
||||||
index is used! (This can only happen when function returns 1)
|
which must be reset after index is used!
|
||||||
|
(This can only happen when function returns 1)
|
||||||
|
|
||||||
@retval
|
@retval
|
||||||
0 Index can not be used to optimize MIN(field)/MAX(field)
|
0 Index can not be used to optimize MIN(field)/MAX(field)
|
||||||
@ -818,7 +835,9 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
|
|||||||
uint *range_fl, uint *prefix_len)
|
uint *range_fl, uint *prefix_len)
|
||||||
{
|
{
|
||||||
if (!(field->flags & PART_KEY_FLAG))
|
if (!(field->flags & PART_KEY_FLAG))
|
||||||
return 0; // Not key field
|
return false; // Not key field
|
||||||
|
|
||||||
|
DBUG_ENTER("find_key_for_maxmin");
|
||||||
|
|
||||||
TABLE *table= field->table;
|
TABLE *table= field->table;
|
||||||
uint idx= 0;
|
uint idx= 0;
|
||||||
@ -843,7 +862,7 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
|
|||||||
part++, jdx++, key_part_to_use= (key_part_to_use << 1) | 1)
|
part++, jdx++, key_part_to_use= (key_part_to_use << 1) | 1)
|
||||||
{
|
{
|
||||||
if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER))
|
if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER))
|
||||||
return 0;
|
DBUG_RETURN(false);
|
||||||
|
|
||||||
/* Check whether the index component is partial */
|
/* Check whether the index component is partial */
|
||||||
Field *part_field= table->field[part->fieldnr-1];
|
Field *part_field= table->field[part->fieldnr-1];
|
||||||
@ -892,12 +911,12 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
|
|||||||
*/
|
*/
|
||||||
if (field->part_of_key.is_set(idx))
|
if (field->part_of_key.is_set(idx))
|
||||||
table->set_keyread(TRUE);
|
table->set_keyread(TRUE);
|
||||||
return 1;
|
DBUG_RETURN(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
DBUG_RETURN(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -961,7 +961,7 @@ JOIN::optimize()
|
|||||||
If all items were resolved by opt_sum_query, there is no need to
|
If all items were resolved by opt_sum_query, there is no need to
|
||||||
open any tables.
|
open any tables.
|
||||||
*/
|
*/
|
||||||
if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
|
if ((res=opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds)))
|
||||||
{
|
{
|
||||||
if (res == HA_ERR_KEY_NOT_FOUND)
|
if (res == HA_ERR_KEY_NOT_FOUND)
|
||||||
{
|
{
|
||||||
|
@ -612,7 +612,8 @@ Field* create_tmp_field_from_field(THD *thd, Field* org_field,
|
|||||||
|
|
||||||
/* functions from opt_sum.cc */
|
/* functions from opt_sum.cc */
|
||||||
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
|
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
|
||||||
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
|
int opt_sum_query(THD* thd,
|
||||||
|
TABLE_LIST *tables, List<Item> &all_fields, COND *conds);
|
||||||
|
|
||||||
/* from sql_delete.cc, used by opt_range.cc */
|
/* from sql_delete.cc, used by opt_range.cc */
|
||||||
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b);
|
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user