Commit e53345f0 authored by Gleb Shchepa's avatar Gleb Shchepa

Bug #11827369: ASSERTION FAILED: !THD->LEX->CONTEXT_ANALYSIS_ONLY

Some queries with the "SELECT ... FROM DUAL" nested subqueries
failed with an assertion on debug builds.
Non-debug builds were not affected.

There were a few different issues with similar assertion
failures on different queries:

1. The first problem was related to the incomplete propagation
of the "non-constant" item status from underlying subquery
items to the outer item tree: in some cases non-constants were
interpreted as constants and evaluated at the preparation stage
(val_int() calls withing fix_fields() etc).

Thus, the default implementation of Item_ref::const_item() from
the Item parent class didn't take into account the "const_item"
status of the referenced item tree -- it used the insufficient
"used_tables() == 0" check instead. This worked in most cases
since our "non-constant" functions like RAND() and SLEEP() set
the RAND_TABLE_BIT in the used table map, so they aren't
non-constant from Item_ref's "point of view". However, the
"SELECT ... FROM DUAL" subquery may have an empty map of used
tables, but at the same time subqueries are never "constant" at
the context analysis stage (preparation, view creation etc).
So, the non-contantness of such subqueries was missed.

Fix: the Item_ref::const_item() function has been overloaded to
take into account both (*ref)->const_item() status and tricky
Item_ref::used_tables() return values, since the only
(*ref)->const_item() call is not enough there.

2. In some cases instead of the const_item() call we check a
value of the Item::with_subselect field to recognize items
with nested subqueries. However, the Item_ref class didn't
propagate this value from the referenced item tree.

Fix: Item::has_subquery() and Item_ref::has_subquery()
functions have been backported from 5.6. All direct
references to the with_subselect fields of nested items have
been with the has_subquery() function call.

3. The Item_func_regex class didn't propagate with_subselect
as well, since it overloads the Item_func::fix_fields()
function with insufficient fix_fields() implementation.

Fix: the Item_func_regex::fix_fields() function has been
modified to gather "constant" statuses from inner items.

4. The Item_func_isnull::update_used_tables() function has
a special branch for the underlying item where the maybe_null
value is false: in this case it marks the Item_func_isnull
as a "const_item" and sets the cached_value to false.
However, the Item_func_isnull::val_int() was not in sync with
update_used_tables(): it didn't take into account neither
const_item_cache nor cached_value for the case of
"args[0]->maybe_null == false optimization".
As far as such an Item_func_isnull has "const_item() == true",
it's ok to call Item_func_isnull::val_int() etc from outer
items on preparation stage. In this case the server tried to
call Item_func_isnull::args[0]->isnull(), and if the args[0]
item contained a nested not-nullable subquery, it failed
with an assertion.

Fix: take the value of Item_func_isnull::const_item_cache into
account in the val_int() function.

5. The auxiliary Item_is_not_null_test class has a similar
optimization in the update_used_tables() function as the
Item_func_isnull class has, and the same issue in the val_int()
function.
In addition to that the Item_is_not_null_test::update_used_tables()
doesn't update the const_item_cache value, so the "maybe_null"
optimization is useless there. Thus, we missed some optimizations
of cases like these (before and after the fix):
  <  <is_not_null_test>(a),
  ---
  >  <cache>(<is_not_null_test>(a)),
or
  < having (<is_not_null_test>(a) and <is_not_null_test>(a))
  ---
  > having 1
etc.

Fix: update Item_is_not_null_test::const_item_cache in
update_used_tables() and take in into account in val_int().
parent d1f351bc
/* /*
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -428,7 +428,7 @@ Item::Item(THD *thd, Item *item): ...@@ -428,7 +428,7 @@ Item::Item(THD *thd, Item *item):
fixed(item->fixed), fixed(item->fixed),
is_autogenerated_name(item->is_autogenerated_name), is_autogenerated_name(item->is_autogenerated_name),
collation(item->collation), collation(item->collation),
with_subselect(item->with_subselect), with_subselect(item->has_subquery()),
cmp_context(item->cmp_context) cmp_context(item->cmp_context)
{ {
next= thd->free_list; // Put in free list next= thd->free_list; // Put in free list
......
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -1054,6 +1054,11 @@ public: ...@@ -1054,6 +1054,11 @@ public:
{ return Field::GEOM_GEOMETRY; }; { return Field::GEOM_GEOMETRY; };
String *check_well_formed_result(String *str, bool send_error= 0); String *check_well_formed_result(String *str, bool send_error= 0);
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
/**
Checks if this item or any of its decendents contains a subquery.
*/
virtual bool has_subquery() const { return with_subselect; }
}; };
...@@ -2264,6 +2269,10 @@ public: ...@@ -2264,6 +2269,10 @@ public:
Field *get_tmp_table_field() Field *get_tmp_table_field()
{ return result_field ? result_field : (*ref)->get_tmp_table_field(); } { return result_field ? result_field : (*ref)->get_tmp_table_field(); }
Item *get_tmp_table_item(THD *thd); Item *get_tmp_table_item(THD *thd);
bool const_item() const
{
return (*ref)->const_item() && (used_tables() == 0);
}
table_map used_tables() const table_map used_tables() const
{ {
return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables();
...@@ -2332,6 +2341,13 @@ public: ...@@ -2332,6 +2341,13 @@ public:
return (*ref)->get_time(ltime); return (*ref)->get_time(ltime);
} }
/**
Checks if the item tree that ref points to contains a subquery.
*/
virtual bool has_subquery() const
{
return (*ref)->has_subquery();
}
}; };
......
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -4257,7 +4257,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) ...@@ -4257,7 +4257,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
const_item_cache= FALSE; const_item_cache= FALSE;
} }
with_sum_func= with_sum_func || item->with_sum_func; with_sum_func= with_sum_func || item->with_sum_func;
with_subselect|= item->with_subselect; with_subselect|= item->has_subquery();
if (item->maybe_null) if (item->maybe_null)
maybe_null=1; maybe_null=1;
} }
...@@ -4582,7 +4582,7 @@ longlong Item_func_isnull::val_int() ...@@ -4582,7 +4582,7 @@ longlong Item_func_isnull::val_int()
Handle optimization if the argument can't be null Handle optimization if the argument can't be null
This has to be here because of the test in update_used_tables(). This has to be here because of the test in update_used_tables().
*/ */
if (!used_tables_cache && !with_subselect) if (const_item_cache)
return cached_value; return cached_value;
return args[0]->is_null() ? 1: 0; return args[0]->is_null() ? 1: 0;
} }
...@@ -4591,7 +4591,7 @@ longlong Item_is_not_null_test::val_int() ...@@ -4591,7 +4591,7 @@ longlong Item_is_not_null_test::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_is_not_null_test::val_int"); DBUG_ENTER("Item_is_not_null_test::val_int");
if (!used_tables_cache && !with_subselect) if (const_item_cache)
{ {
owner->was_null|= (!cached_value); owner->was_null|= (!cached_value);
DBUG_PRINT("info", ("cached: %ld", (long) cached_value)); DBUG_PRINT("info", ("cached: %ld", (long) cached_value));
...@@ -4612,10 +4612,12 @@ longlong Item_is_not_null_test::val_int() ...@@ -4612,10 +4612,12 @@ longlong Item_is_not_null_test::val_int()
*/ */
void Item_is_not_null_test::update_used_tables() void Item_is_not_null_test::update_used_tables()
{ {
const_item_cache= false;
if (!args[0]->maybe_null) if (!args[0]->maybe_null)
{ {
used_tables_cache= 0; /* is always true */ used_tables_cache= 0; /* is always true */
cached_value= (longlong) 1; cached_value= (longlong) 1;
const_item_cache= true;
} }
else else
{ {
...@@ -4624,6 +4626,7 @@ void Item_is_not_null_test::update_used_tables() ...@@ -4624,6 +4626,7 @@ void Item_is_not_null_test::update_used_tables()
{ {
/* Remember if the value is always NULL or never NULL */ /* Remember if the value is always NULL or never NULL */
cached_value= (longlong) !args[0]->is_null(); cached_value= (longlong) !args[0]->is_null();
const_item_cache= true;
} }
} }
} }
...@@ -4879,6 +4882,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) ...@@ -4879,6 +4882,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1)) args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
return TRUE; /* purecov: inspected */ return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func; with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1; max_length= 1;
decimals= 0; decimals= 0;
......
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -198,7 +198,7 @@ Item_func::fix_fields(THD *thd, Item **ref) ...@@ -198,7 +198,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
used_tables_cache|= item->used_tables(); used_tables_cache|= item->used_tables();
not_null_tables_cache|= item->not_null_tables(); not_null_tables_cache|= item->not_null_tables();
const_item_cache&= item->const_item(); const_item_cache&= item->const_item();
with_subselect|= item->with_subselect; with_subselect|= item->has_subquery();
} }
} }
fix_length_and_dec(); fix_length_and_dec();
......
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -7321,7 +7321,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, ...@@ -7321,7 +7321,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0; // Must do a temp table to sort *simple_order=0; // Must do a temp table to sort
else if (!(order_tables & not_const_tables)) else if (!(order_tables & not_const_tables))
{ {
if (order->item[0]->with_subselect && if (order->item[0]->has_subquery() &&
!(join->select_lex->options & SELECT_DESCRIBE)) !(join->select_lex->options & SELECT_DESCRIBE))
order->item[0]->val_str(&order->item[0]->str_value); order->item[0]->val_str(&order->item[0]->str_value);
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
......
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