preliminary analysis
select * from t1, t2 where b in
|
( select (select a from t1 order by b) as d from t1 group by (select d) );
|
we crash during update_depend_map_for_order() processing the group by clause for select #2.
order->item[0] is (Item_subselect) (select `d`)
Our depend_map is initialized as order->item[0]->used_tables()
for (; order ; order=order->next)
|
{
|
table_map depend_map;
|
order->item[0]->update_used_tables();
|
order->depend_map=depend_map=order->item[0]->used_tables();
|
in gdb we observe that this is 0b10, which cannot be correct as the Item_field d is an outer reference.
We would expect to see the OUTER_BIT_TABLE_REF bit set in this map, but in the call to update_used_tables() on the prior line, this bit isn't set.
We can see why when we examine how upper_refs for this Item_subselect is processed.
Unsurprisingly we see it contains an Item_ref wrapper around the Item_field d.
In Item_subselect::recalc_used_tables we call
// Now, iterate over fields and collect used_tables() attribute:
|
Field_fixer fixer;
|
fixer.used_tables= 0;
|
fixer.new_parent= new_parent;
|
upper->item->walk(&Item::enumerate_field_refs_processor, 0, &fixer);
|
used_tables_cache |= fixer.used_tables;
|
here we walk through the item tree attached to upper->item (Item_ref d) looking for anything that is an Item_field and collecting a bitmap of all the used_tables.
The problem is that it is an indiscriminate walk, anything that is an Item_field has it's bitmap added.
Item_ref d is a wrapper around Item_subselect (select `test`.`t1`.`a` from `test`.`t1` order by `test`.`t2`.`b`)
and we hit field t2.b, which is NOT a part of this select, it's bitmap refers to select #1.
It's bitmap is assigned to Item_subselect::used_tables_cache and used as a loop counter in update_depend_map_for_order()
which walks off the end of the number of joined tables, accessing uninitialised memory.
We need to make the method visit_field() in our Field_fixer class more discriminatory.
class Field_fixer: public Field_enumerator
|
{
|
public:
|
table_map used_tables; /* Collect used_tables here */
|
st_select_lex *new_parent; /* Select we're in */
|
void visit_field(Item_field *item) override
|
{
|
//for (TABLE_LIST *tbl= new_parent->leaf_tables; tbl; tbl= tbl->next_local)
|
//{
|
// if (tbl->table == field->table)
|
// {
|
used_tables|= item->field->table->map;
|
// return;
|
// }
|
//}
|
//used_tables |= OUTER_REF_TABLE_BIT;
|
}
|
};
|
we note some interesting commented-out lines.
There is MDEV-30756 which looks much like this.