Skip to content

Commit 2993c29

Browse files
committed
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 replaced 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().
2 parents 5656b9d + e53345f commit 2993c29

5 files changed

Lines changed: 31 additions & 11 deletions

File tree

sql/item.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -428,7 +428,7 @@ Item::Item(THD *thd, Item *item):
428428
fixed(item->fixed),
429429
is_autogenerated_name(item->is_autogenerated_name),
430430
collation(item->collation),
431-
with_subselect(item->with_subselect),
431+
with_subselect(item->has_subquery()),
432432
cmp_context(item->cmp_context)
433433
{
434434
next= thd->free_list; // Put in free list

sql/item.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -1054,6 +1054,11 @@ class Item {
10541054
{ return Field::GEOM_GEOMETRY; };
10551055
String *check_well_formed_result(String *str, bool send_error= 0);
10561056
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
1057+
1058+
/**
1059+
Checks if this item or any of its decendents contains a subquery.
1060+
*/
1061+
virtual bool has_subquery() const { return with_subselect; }
10571062
};
10581063

10591064

@@ -2264,6 +2269,10 @@ class Item_ref :public Item_ident
22642269
Field *get_tmp_table_field()
22652270
{ return result_field ? result_field : (*ref)->get_tmp_table_field(); }
22662271
Item *get_tmp_table_item(THD *thd);
2272+
bool const_item() const
2273+
{
2274+
return (*ref)->const_item() && (used_tables() == 0);
2275+
}
22672276
table_map used_tables() const
22682277
{
22692278
return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables();
@@ -2332,6 +2341,13 @@ class Item_ref :public Item_ident
23322341
return (*ref)->get_time(ltime);
23332342
}
23342343

2344+
/**
2345+
Checks if the item tree that ref points to contains a subquery.
2346+
*/
2347+
virtual bool has_subquery() const
2348+
{
2349+
return (*ref)->has_subquery();
2350+
}
23352351
};
23362352

23372353

sql/item_cmpfunc.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
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)
42574257
const_item_cache= FALSE;
42584258
}
42594259
with_sum_func= with_sum_func || item->with_sum_func;
4260-
with_subselect|= item->with_subselect;
4260+
with_subselect|= item->has_subquery();
42614261
if (item->maybe_null)
42624262
maybe_null=1;
42634263
}
@@ -4582,7 +4582,7 @@ longlong Item_func_isnull::val_int()
45824582
Handle optimization if the argument can't be null
45834583
This has to be here because of the test in update_used_tables().
45844584
*/
4585-
if (!used_tables_cache && !with_subselect)
4585+
if (const_item_cache)
45864586
return cached_value;
45874587
return args[0]->is_null() ? 1: 0;
45884588
}
@@ -4591,7 +4591,7 @@ longlong Item_is_not_null_test::val_int()
45914591
{
45924592
DBUG_ASSERT(fixed == 1);
45934593
DBUG_ENTER("Item_is_not_null_test::val_int");
4594-
if (!used_tables_cache && !with_subselect)
4594+
if (const_item_cache)
45954595
{
45964596
owner->was_null|= (!cached_value);
45974597
DBUG_PRINT("info", ("cached: %ld", (long) cached_value));
@@ -4612,10 +4612,12 @@ longlong Item_is_not_null_test::val_int()
46124612
*/
46134613
void Item_is_not_null_test::update_used_tables()
46144614
{
4615+
const_item_cache= false;
46154616
if (!args[0]->maybe_null)
46164617
{
46174618
used_tables_cache= 0; /* is always true */
46184619
cached_value= (longlong) 1;
4620+
const_item_cache= true;
46194621
}
46204622
else
46214623
{
@@ -4624,6 +4626,7 @@ void Item_is_not_null_test::update_used_tables()
46244626
{
46254627
/* Remember if the value is always NULL or never NULL */
46264628
cached_value= (longlong) !args[0]->is_null();
4629+
const_item_cache= true;
46274630
}
46284631
}
46294632
}
@@ -4879,6 +4882,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
48794882
args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
48804883
return TRUE; /* purecov: inspected */
48814884
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
4885+
with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
48824886
max_length= 1;
48834887
decimals= 0;
48844888

sql/item_func.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
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)
198198
used_tables_cache|= item->used_tables();
199199
not_null_tables_cache|= item->not_null_tables();
200200
const_item_cache&= item->const_item();
201-
with_subselect|= item->with_subselect;
201+
with_subselect|= item->has_subquery();
202202
}
203203
}
204204
fix_length_and_dec();

sql/sql_select.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
22

33
This program is free software; you can redistribute it and/or modify
44
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,
73217321
*simple_order=0; // Must do a temp table to sort
73227322
else if (!(order_tables & not_const_tables))
73237323
{
7324-
if (order->item[0]->with_subselect &&
7324+
if (order->item[0]->has_subquery() &&
73257325
!(join->select_lex->options & SELECT_DESCRIBE))
73267326
order->item[0]->val_str(&order->item[0]->str_value);
73277327
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));

0 commit comments

Comments
 (0)