Skip to content

Commit d6cb725

Browse files
committed
MDEV-30711: Crash in add_keyuses_for_splitting() when joining with a derived table
Split-Materialized optimization in add_keyuses_for_splitting() pushes "derived_split_table.field=right_expr" down into the derived_split_table. This requires that right_expr's attributes are updated. Typically it has references to tables in the parent select, those become references to outside in the child select. This was done with the right_expr->walk(&Item::set_fields_as_dependent_processor, ...) call. The function was implemented for Item_field objects. However it was not implemented for Item_direct_view_ref objects. If an Item_direct_view_ref object didn't have an Item_field inside (e.g. view column referred to a constant) this would mean that used_tables() attribute would not be updated and right_expr will end up with wrong used_tables(), which could eventually cause a crash as it looked like a reference to non-existant table. Fixed by adding Item_direct_view_ref::set_fields_as_dependent_processor() with the same logic as in Item_field.
1 parent 5f51a3a commit d6cb725

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

mysql-test/main/derived_split_innodb.result

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,4 +935,37 @@ cnt
935935
6
936936
DROP TABLE t1;
937937
# End of 10.4 tests
938+
#
939+
# MDEV-30711: Crash in add_keyuses_for_splitting() when joining with a derived table
940+
#
941+
create table t1 (a int);
942+
insert into t1 values (1),(2);
943+
create table t2 (a int, index(a));
944+
insert into t2 values (1),(3);
945+
create view v1 as
946+
select
947+
nullif(tbl2.COL1,123) as COL10
948+
from
949+
t1 left join
950+
(select 1 as COL1, a from t2) tbl2 on t1.a=tbl2.a;
951+
create table t10 (grp_id int, a int, index(grp_id));
952+
insert into t10 select A.seq, B.seq from seq_1_to_100 A, seq_1_to_100 B;
953+
analyze table t10;
954+
Table Op Msg_type Msg_text
955+
test.t10 analyze status Engine-independent statistics collected
956+
test.t10 analyze status Table is already up to date
957+
explain
958+
select * from
959+
v1,
960+
(select grp_id, count(*) from t10 group by grp_id) T
961+
where
962+
T.grp_id=v1.COL10;
963+
id select_type table type possible_keys key key_len ref rows Extra
964+
1 PRIMARY t1 ALL NULL NULL NULL NULL 2
965+
1 PRIMARY t2 ref a a 5 test.t1.a 2 Using where; Using index
966+
1 PRIMARY <derived2> ref key0 key0 5 func 10 Using where
967+
2 DERIVED t10 index grp_id grp_id 5 NULL 10000 Using index; Using temporary; Using filesort
968+
drop table t1,t2, t10;
969+
drop view v1;
970+
# End of 10.11 tests
938971
SET GLOBAL innodb_stats_persistent=@save_innodb_stats_persistent;

mysql-test/main/derived_split_innodb.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,4 +549,34 @@ DROP TABLE t1;
549549

550550
--echo # End of 10.4 tests
551551

552+
--echo #
553+
--echo # MDEV-30711: Crash in add_keyuses_for_splitting() when joining with a derived table
554+
--echo #
555+
create table t1 (a int);
556+
insert into t1 values (1),(2);
557+
create table t2 (a int, index(a));
558+
insert into t2 values (1),(3);
559+
560+
create view v1 as
561+
select
562+
nullif(tbl2.COL1,123) as COL10
563+
from
564+
t1 left join
565+
(select 1 as COL1, a from t2) tbl2 on t1.a=tbl2.a;
566+
567+
create table t10 (grp_id int, a int, index(grp_id));
568+
insert into t10 select A.seq, B.seq from seq_1_to_100 A, seq_1_to_100 B;
569+
analyze table t10;
570+
571+
explain
572+
select * from
573+
v1,
574+
(select grp_id, count(*) from t10 group by grp_id) T
575+
where
576+
T.grp_id=v1.COL10;
577+
578+
drop table t1,t2, t10;
579+
drop view v1;
580+
581+
--echo # End of 10.11 tests
552582
SET GLOBAL innodb_stats_persistent=@save_innodb_stats_persistent;

sql/item.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6504,6 +6504,19 @@ class Item_direct_view_ref :public Item_direct_ref
65046504
{ return get_item_copy<Item_direct_view_ref>(thd, this); }
65056505
Item *field_transformer_for_having_pushdown(THD *, uchar *) override
65066506
{ return this; }
6507+
/*
6508+
Do the same thing as Item_field: if we were referring to a local view,
6509+
now we refer to somewhere outside of our SELECT.
6510+
*/
6511+
bool set_fields_as_dependent_processor(void *arg) override
6512+
{
6513+
if (!(used_tables() & OUTER_REF_TABLE_BIT))
6514+
{
6515+
depended_from= (st_select_lex *) arg;
6516+
item_equal= NULL;
6517+
}
6518+
return 0;
6519+
}
65076520
};
65086521

65096522

sql/opt_split.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,11 @@ void TABLE::add_splitting_info_for_key_field(KEY_FIELD *key_field)
610610
right_item->walk(&Item::set_fields_as_dependent_processor,
611611
false, join->select_lex);
612612
right_item->update_used_tables();
613+
/*
614+
We've just pushed right_item down into the child select. It may only
615+
have references to outside.
616+
*/
617+
DBUG_ASSERT(!(right_item->used_tables() & ~PSEUDO_TABLE_BITS));
613618
eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item);
614619
}
615620
if (!eq_item)

0 commit comments

Comments
 (0)