Commit 9e80a789 authored by Gleb Shchepa's avatar Gleb Shchepa

Bug #16311231: MISSING DATA ON SUBQUERY WITH WHERE + XOR

IN IN-CLAUSE USING MYISAM OR MEMORY ENGINE

Backport from 5.6. Original message:

The coincidences caused a data loss:
* The query has IN subqueries nested twice,
* the WHERE clause of the inner subquery refers to the
  outer field, and the whole WHERE clause returns FALSE,
* the inner subquery has a LEFT JOIN that joins a single
  row with a row of NULLs; one of that NULL columns
  represents the select list of the subquery.

Normally, that inner subquery should return empty record set.
However, in our case:
* the Item_is_not_null_test item goes constant, since
  its underlying field is NULL (because of LEFT JOIN ... ON 
  FALSE of const table row with a row of nulls);
* we evaluate Item_is_not_null_test::val_int() as a part
  of fake HAVING expression of the transformed subquery;
* as far as the underlying field is NULL, we optimize
  out the whole fake HAVING expression as FALSE as well
  as a whole subquery with a zero result:
  Impossible HAVING noticed after reading const tables";
* thus, the optimizer ignores the presence of the WHERE
  clause (the WHERE expression is FALSE in our case, so
  the subquery should return empty set);
* however, during the evaluation of the 
  Item_is_not_null_test::val_int() in the optimizer,
  it marked its "owner" with the "was_null" flag -- that
  forced the subquery to return UNKNOWN instead of empty
  set.
That caused a wrong result.


The problem is a regression of the small cleanup in
the fix for the bug11827369 (the Item_is_not_null_test part)
that conflicts with optimizations in the fix for the bug11752543.
Before that regression the Item_is_not_null_test items
never were constants.

The fix is the rollback of Item_is_not_null_test parts
of the bug11827369 fix.
parent 8ad7a67e
......@@ -4591,7 +4591,7 @@ longlong Item_is_not_null_test::val_int()
{
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_is_not_null_test::val_int");
if (const_item_cache)
if (!used_tables_cache && !with_subselect)
{
owner->was_null|= (!cached_value);
DBUG_PRINT("info", ("cached: %ld", (long) cached_value));
......@@ -4612,12 +4612,10 @@ longlong Item_is_not_null_test::val_int()
*/
void Item_is_not_null_test::update_used_tables()
{
const_item_cache= false;
if (!args[0]->maybe_null)
{
used_tables_cache= 0; /* is always true */
cached_value= (longlong) 1;
const_item_cache= true;
}
else
{
......@@ -4626,7 +4624,6 @@ void Item_is_not_null_test::update_used_tables()
{
/* Remember if the value is always NULL or never NULL */
cached_value= (longlong) !args[0]->is_null();
const_item_cache= true;
}
}
}
......
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