Merge maria-5.1 -> maria-5.1-table-elimination

This commit is contained in:
Sergey Petrunya 2009-08-14 01:12:12 +04:00
commit 57c199d8d1
27 changed files with 2338 additions and 57 deletions

View File

@ -1920,3 +1920,4 @@ sql/share/swedish
sql/share/ukrainian sql/share/ukrainian
libmysqld/examples/mysqltest.cc libmysqld/examples/mysqltest.cc
extra/libevent/event-config.h extra/libevent/event-config.h
libmysqld/opt_table_elimination.cc

View File

@ -76,7 +76,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \ rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
sql_tablespace.cc \ sql_tablespace.cc \
rpl_injector.cc my_user.c partition_info.cc \ rpl_injector.cc my_user.c partition_info.cc \
sql_servers.cc event_parse_data.cc sql_servers.cc event_parse_data.cc opt_table_elimination.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) libmysqld_int_a_SOURCES= $(libmysqld_sources)
nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources) nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)

View File

@ -3,6 +3,9 @@ SET @old_max_allowed_packet= @@global.max_allowed_packet;
SET @@global.max_allowed_packet = 2 * 1024 * 1024 + 1024; SET @@global.max_allowed_packet = 2 * 1024 * 1024 + 1024;
CREATE TABLE t1(data LONGBLOB); CREATE TABLE t1(data LONGBLOB);
INSERT INTO t1 SELECT REPEAT('1', 2*1024*1024); INSERT INTO t1 SELECT REPEAT('1', 2*1024*1024);
SELECT COUNT(*) FROM t1;
COUNT(*)
1
SET @old_general_log = @@global.general_log; SET @old_general_log = @@global.general_log;
SET @@global.general_log = 0; SET @@global.general_log = 0;
SET @@global.general_log = @old_general_log; SET @@global.general_log = @old_general_log;

View File

@ -121,8 +121,8 @@ insert into t1 values (1);
explain select * from t1 where 3 in (select (1+1) union select 1); explain select * from t1 where 3 in (select (1+1) union select 1);
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used 2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used 3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL Impossible HAVING
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
select * from t1 where 3 in (select (1+1) union select 1); select * from t1 where 3 in (select (1+1) union select 1);
a a

View File

@ -3585,7 +3585,6 @@ INSERT INTO t2 VALUES (1,'a'),(2,'b'),(3,'c');
EXPLAIN SELECT t1.a FROM t1 LEFT JOIN t2 ON t2.b=t1.b WHERE t1.a=3; EXPLAIN SELECT t1.a FROM t1 LEFT JOIN t2 ON t2.b=t1.b WHERE t1.a=3;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
1 SIMPLE t2 const b b 22 const 1 Using index
DROP TABLE t1,t2; DROP TABLE t1,t2;
CREATE TABLE t1(id int PRIMARY KEY, b int, e int); CREATE TABLE t1(id int PRIMARY KEY, b int, e int);
CREATE TABLE t2(i int, a int, INDEX si(i), INDEX ai(a)); CREATE TABLE t2(i int, a int, INDEX si(i), INDEX ai(a));

View File

@ -4353,13 +4353,13 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
Warnings: Warnings:
Note 1003 select 1 AS `1` from `test`.`t1` where <in_optimizer>(1,<exists>(select 1 AS `1` from `test`.`t1` group by `test`.`t1`.`a` having (<cache>(1) = <ref_null_helper>(1)))) Note 1003 select 1 AS `1` from `test`.`t1` where <in_optimizer>(1,<exists>(select 1 AS `1` from `test`.`t1` group by `test`.`t1`.`a` having 1))
EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE 1 IN (SELECT 1 FROM t1 WHERE a > 3 GROUP BY a); EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE 1 IN (SELECT 1 FROM t1 WHERE a > 3 GROUP BY a);
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using where; Using temporary; Using filesort 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using where; Using temporary; Using filesort
Warnings: Warnings:
Note 1003 select 1 AS `1` from `test`.`t1` where <in_optimizer>(1,<exists>(select 1 AS `1` from `test`.`t1` where (`test`.`t1`.`a` > 3) group by `test`.`t1`.`a` having (<cache>(1) = <ref_null_helper>(1)))) Note 1003 select 1 AS `1` from `test`.`t1` where <in_optimizer>(1,<exists>(select 1 AS `1` from `test`.`t1` where (`test`.`t1`.`a` > 3) group by `test`.`t1`.`a` having 1))
DROP TABLE t1; DROP TABLE t1;
End of 5.0 tests. End of 5.0 tests.
CREATE TABLE t1 (a INT, b INT); CREATE TABLE t1 (a INT, b INT);

View File

@ -0,0 +1,204 @@
drop table if exists t0, t1, t2, t3;
drop view if exists v1, v2;
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t0 as select * from t1;
create table t2 (a int primary key, b int)
as select a, a as b from t1 where a in (1,2);
create table t3 (a int primary key, b int)
as select a, a as b from t1 where a in (1,3);
# This will be eliminated:
explain select t1.a from t1 left join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
explain extended select t1.a from t1 left join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where 1
select t1.a from t1 left join t2 on t2.a=t1.a;
a
0
1
2
3
# This will not be eliminated as t2.b is in in select list:
explain select * from t1 left join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1
# This will not be eliminated as t2.b is in in order list:
explain select t1.a from t1 left join t2 on t2.a=t1.a order by t2.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1
# This will not be eliminated as t2.b is in group list:
explain select t1.a from t1 left join t2 on t2.a=t1.a group by t2.b;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1
# This will not be eliminated as t2.b is in the WHERE
explain select t1.a from t1 left join t2 on t2.a=t1.a where t2.b < 3 or t2.b is null;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using where
# Elimination of multiple tables:
explain select t1.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
# Elimination of multiple tables (2):
explain select t1.a from t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
# Elimination when done within an outer join nest:
explain extended
select t0.*
from
t0 left join (t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and
t3.a=t1.a) on t0.a=t1.a;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 4 100.00
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00
Warnings:
Note 1003 select `test`.`t0`.`a` AS `a` from `test`.`t0` left join (`test`.`t1`) on((`test`.`t0`.`a` = `test`.`t1`.`a`)) where 1
# Elimination with aggregate functions
explain select count(*) from t1 left join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
explain select count(1) from t1 left join t2 on t2.a=t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
explain select count(1) from t1 left join t2 on t2.a=t1.a group by t1.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
This must not use elimination:
explain select count(1) from t1 left join t2 on t2.a=t1.a group by t2.a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index
drop table t0, t1, t2, t3;
create table t0 ( id integer, primary key (id));
create table t1 (
id integer,
attr1 integer,
primary key (id),
key (attr1)
);
create table t2 (
id integer,
attr2 integer,
fromdate date,
primary key (id, fromdate),
key (attr2,fromdate)
);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
insert into t0 select A.id + 10*B.id from t0 A, t0 B where B.id > 0;
insert into t1 select id, id from t0;
insert into t2 select id, id, date_add('2009-06-22', interval id day) from t0;
insert into t2 select id, id+1, date_add('2008-06-22', interval id day) from t0;
create view v1 as
select
F.id, A1.attr1, A2.attr2
from
t0 F
left join t1 A1 on A1.id=F.id
left join t2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
t2 where id=A2.id);
create view v2 as
select
F.id, A1.attr1, A2.attr2
from
t0 F
left join t1 A1 on A1.id=F.id
left join t2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
t2 where id=F.id);
This should use one table:
explain select id from v1 where id=2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY F const PRIMARY PRIMARY 4 const 1 Using index
This should use one table:
explain extended select id from v1 where id in (1,2,3,4);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY F range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.A2.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` where (`F`.`id` in (1,2,3,4))
This should use facts and A1 tables:
explain extended select id from v1 where attr1 between 12 and 14;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY A1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using where
1 PRIMARY F eq_ref PRIMARY PRIMARY 4 test.A1.id 1 100.00 Using index
Warnings:
Note 1276 Field or reference 'test.A2.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t1` `A1` where ((`F`.`id` = `A1`.`id`) and (`A1`.`attr1` between 12 and 14))
This should use facts, A2 and its subquery:
explain extended select id from v1 where attr2 between 12 and 14;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY A2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using where
1 PRIMARY F eq_ref PRIMARY PRIMARY 4 test.A2.id 1 100.00 Using index
3 DEPENDENT SUBQUERY t2 ref PRIMARY PRIMARY 4 test.A2.id 2 100.00 Using index
Warnings:
Note 1276 Field or reference 'test.A2.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t2` `A2` where ((`F`.`id` = `A2`.`id`) and (`A2`.`attr2` between 12 and 14) and (`A2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `A2`.`id`))))
This should use one table:
explain select id from v2 where id=2;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY F const PRIMARY PRIMARY 4 const 1 Using index
This should use one table:
explain extended select id from v2 where id in (1,2,3,4);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY F range PRIMARY PRIMARY 4 NULL 4 100.00 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.F.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` where (`F`.`id` in (1,2,3,4))
This should use facts and A1 tables:
explain extended select id from v2 where attr1 between 12 and 14;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY A1 range PRIMARY,attr1 attr1 5 NULL 2 100.00 Using where
1 PRIMARY F eq_ref PRIMARY PRIMARY 4 test.A1.id 1 100.00 Using index
Warnings:
Note 1276 Field or reference 'test.F.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t1` `A1` where ((`F`.`id` = `A1`.`id`) and (`A1`.`attr1` between 12 and 14))
This should use facts, A2 and its subquery:
explain extended select id from v2 where attr2 between 12 and 14;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY A2 range PRIMARY,attr2 attr2 5 NULL 5 100.00 Using where
1 PRIMARY F eq_ref PRIMARY PRIMARY 4 test.A2.id 1 100.00 Using where; Using index
3 DEPENDENT SUBQUERY t2 ref PRIMARY PRIMARY 4 test.F.id 2 100.00 Using index
Warnings:
Note 1276 Field or reference 'test.F.id' of SELECT #3 was resolved in SELECT #1
Note 1003 select `F`.`id` AS `id` from `test`.`t0` `F` join `test`.`t2` `A2` where ((`F`.`id` = `A2`.`id`) and (`A2`.`attr2` between 12 and 14) and (`A2`.`fromdate` = (select max(`test`.`t2`.`fromdate`) AS `MAX(fromdate)` from `test`.`t2` where (`test`.`t2`.`id` = `F`.`id`))))
drop view v1, v2;
drop table t0, t1, t2;
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t2 (pk1 int, pk2 int, pk3 int, col int, primary key(pk1, pk2, pk3));
insert into t2 select a,a,a,a from t1;
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk2=t2.pk1+1 and
t2.pk3=t2.pk2+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
This must use both:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+t2.col;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1
This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk2=t1.a and
t2.pk1=t2.pk2+1 and
t2.pk3=t2.pk1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4
drop table t1, t2;

View File

@ -522,7 +522,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
2 UNION t2 const PRIMARY PRIMARY 4 const 1 100.00 2 UNION t2 const PRIMARY PRIMARY 4 const 1 100.00
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
Warnings: Warnings:
Note 1003 (select '1' AS `a`,'1' AS `b` from `test`.`t1` where ('1' = 1)) union (select '1' AS `a`,'10' AS `b` from `test`.`t2` where ('1' = 1)) Note 1003 (select '1' AS `a`,'1' AS `b` from `test`.`t1` where 1) union (select '1' AS `a`,'10' AS `b` from `test`.`t2` where 1)
(select * from t1 where a=5) union (select * from t2 where a=1); (select * from t1 where a=5) union (select * from t2 where a=1);
a b a b
1 10 1 10

View File

@ -27,7 +27,8 @@ connect (con1, localhost, root,,);
CREATE TABLE t1(data LONGBLOB); CREATE TABLE t1(data LONGBLOB);
INSERT INTO t1 SELECT REPEAT('1', 2*1024*1024); INSERT INTO t1 SELECT REPEAT('1', 2*1024*1024);
# The following is to remove the race between end of insert and start of MYSQL_DUMP:
SELECT COUNT(*) FROM t1;
let $outfile= $MYSQLTEST_VARDIR/tmp/bug41486.sql; let $outfile= $MYSQLTEST_VARDIR/tmp/bug41486.sql;
--error 0,1 --error 0,1
remove_file $outfile; remove_file $outfile;

View File

@ -0,0 +1,160 @@
#
# Table elimination (MWL#17) tests
#
--disable_warnings
drop table if exists t0, t1, t2, t3;
drop view if exists v1, v2;
--enable_warnings
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t0 as select * from t1;
create table t2 (a int primary key, b int)
as select a, a as b from t1 where a in (1,2);
create table t3 (a int primary key, b int)
as select a, a as b from t1 where a in (1,3);
--echo # This will be eliminated:
explain select t1.a from t1 left join t2 on t2.a=t1.a;
explain extended select t1.a from t1 left join t2 on t2.a=t1.a;
select t1.a from t1 left join t2 on t2.a=t1.a;
--echo # This will not be eliminated as t2.b is in in select list:
explain select * from t1 left join t2 on t2.a=t1.a;
--echo # This will not be eliminated as t2.b is in in order list:
explain select t1.a from t1 left join t2 on t2.a=t1.a order by t2.b;
--echo # This will not be eliminated as t2.b is in group list:
explain select t1.a from t1 left join t2 on t2.a=t1.a group by t2.b;
--echo # This will not be eliminated as t2.b is in the WHERE
explain select t1.a from t1 left join t2 on t2.a=t1.a where t2.b < 3 or t2.b is null;
--echo # Elimination of multiple tables:
explain select t1.a from t1 left join (t2 join t3) on t2.a=t1.a and t3.a=t1.a;
--echo # Elimination of multiple tables (2):
explain select t1.a from t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and t3.a=t1.a;
--echo # Elimination when done within an outer join nest:
explain extended
select t0.*
from
t0 left join (t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and
t3.a=t1.a) on t0.a=t1.a;
--echo # Elimination with aggregate functions
explain select count(*) from t1 left join t2 on t2.a=t1.a;
explain select count(1) from t1 left join t2 on t2.a=t1.a;
explain select count(1) from t1 left join t2 on t2.a=t1.a group by t1.a;
--echo This must not use elimination:
explain select count(1) from t1 left join t2 on t2.a=t1.a group by t2.a;
drop table t0, t1, t2, t3;
# This will stand for elim_facts
create table t0 ( id integer, primary key (id));
# Attribute1, non-versioned
create table t1 (
id integer,
attr1 integer,
primary key (id),
key (attr1)
);
# Attribute2, time-versioned
create table t2 (
id integer,
attr2 integer,
fromdate date,
primary key (id, fromdate),
key (attr2,fromdate)
);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
insert into t0 select A.id + 10*B.id from t0 A, t0 B where B.id > 0;
insert into t1 select id, id from t0;
insert into t2 select id, id, date_add('2009-06-22', interval id day) from t0;
insert into t2 select id, id+1, date_add('2008-06-22', interval id day) from t0;
create view v1 as
select
F.id, A1.attr1, A2.attr2
from
t0 F
left join t1 A1 on A1.id=F.id
left join t2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
t2 where id=A2.id);
create view v2 as
select
F.id, A1.attr1, A2.attr2
from
t0 F
left join t1 A1 on A1.id=F.id
left join t2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
t2 where id=F.id);
--echo This should use one table:
explain select id from v1 where id=2;
--echo This should use one table:
explain extended select id from v1 where id in (1,2,3,4);
--echo This should use facts and A1 tables:
explain extended select id from v1 where attr1 between 12 and 14;
--echo This should use facts, A2 and its subquery:
explain extended select id from v1 where attr2 between 12 and 14;
# Repeat for v2:
--echo This should use one table:
explain select id from v2 where id=2;
--echo This should use one table:
explain extended select id from v2 where id in (1,2,3,4);
--echo This should use facts and A1 tables:
explain extended select id from v2 where attr1 between 12 and 14;
--echo This should use facts, A2 and its subquery:
explain extended select id from v2 where attr2 between 12 and 14;
drop view v1, v2;
drop table t0, t1, t2;
#
# Tests for the code that uses t.keypartX=func(t.keypartY) equalities to
# make table elimination inferences
#
create table t1 (a int);
insert into t1 values (0),(1),(2),(3);
create table t2 (pk1 int, pk2 int, pk3 int, col int, primary key(pk1, pk2, pk3));
insert into t2 select a,a,a,a from t1;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk2=t2.pk1+1 and
t2.pk3=t2.pk2+1;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+1;
--echo This must use both:
explain select t1.* from t1 left join t2 on t2.pk1=t1.a and
t2.pk3=t2.pk1+1 and
t2.pk2=t2.pk3+t2.col;
--echo This must use only t1:
explain select t1.* from t1 left join t2 on t2.pk2=t1.a and
t2.pk1=t2.pk2+1 and
t2.pk3=t2.pk1;
drop table t1, t2;

View File

@ -703,3 +703,73 @@
fun:malloc fun:malloc
fun:inet_ntoa fun:inet_ntoa
} }
#
# Some problem inside glibc on Ubuntu 9.04, x86 (but not amd64):
#
# ==5985== 19 bytes in 1 blocks are still reachable in loss record 1 of 6
# ==5985== at 0x7AF3FDE: malloc (vg_replace_malloc.c:207)
# ... 11,12, or 13 functions w/o symbols ...
# ==5985== by 0x8717185: nptl_pthread_exit_hack_handler (my_thr_init.c:55)
#
# Since valgrind 3.3.0 doesn't support '...' multi-function pattern, using
# multiple suppressions:
#
{
Mem loss inside nptl_pthread_exit_hack_handler
Memcheck:Leak
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:nptl_pthread_exit_hack_handler
}
{
Mem loss inside nptl_pthread_exit_hack_handler
Memcheck:Leak
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:nptl_pthread_exit_hack_handler
}
{
Mem loss inside nptl_pthread_exit_hack_handler
Memcheck:Leak
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:*
fun:nptl_pthread_exit_hack_handler
}

View File

@ -0,0 +1,320 @@
#!@PERL@
# Test of table elimination feature
use Cwd;
use DBI;
use Getopt::Long;
use Benchmark;
$opt_loop_count=100000;
$opt_medium_loop_count=10000;
$opt_small_loop_count=100;
$pwd = cwd(); $pwd = "." if ($pwd eq '');
require "$pwd/bench-init.pl" || die "Can't read Configuration file: $!\n";
if ($opt_small_test)
{
$opt_loop_count/=10;
$opt_medium_loop_count/=10;
$opt_small_loop_count/=10;
}
print "Testing table elimination feature\n";
print "The test table has $opt_loop_count rows.\n\n";
# A query to get the recent versions of all attributes:
$select_current_full_facts="
select
F.id, A1.attr1, A2.attr2
from
elim_facts F
left join elim_attr1 A1 on A1.id=F.id
left join elim_attr2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
elim_attr2 where id=A2.id);
";
$select_current_full_facts="
select
F.id, A1.attr1, A2.attr2
from
elim_facts F
left join elim_attr1 A1 on A1.id=F.id
left join elim_attr2 A2 on A2.id=F.id and
A2.fromdate=(select MAX(fromdate) from
elim_attr2 where id=F.id);
";
# TODO: same as above but for some given date also?
# TODO:
####
#### Connect and start timeing
####
$dbh = $server->connect();
$start_time=new Benchmark;
####
#### Create needed tables
####
goto select_test if ($opt_skip_create);
print "Creating tables\n";
$dbh->do("drop table elim_facts" . $server->{'drop_attr'});
$dbh->do("drop table elim_attr1" . $server->{'drop_attr'});
$dbh->do("drop table elim_attr2" . $server->{'drop_attr'});
# The facts table
do_many($dbh,$server->create("elim_facts",
["id integer"],
["primary key (id)"]));
# Attribute1, non-versioned
do_many($dbh,$server->create("elim_attr1",
["id integer",
"attr1 integer"],
["primary key (id)",
"key (attr1)"]));
# Attribute2, time-versioned
do_many($dbh,$server->create("elim_attr2",
["id integer",
"attr2 integer",
"fromdate date"],
["primary key (id, fromdate)",
"key (attr2,fromdate)"]));
#NOTE: ignoring: if ($limits->{'views'})
$dbh->do("drop view elim_current_facts");
$dbh->do("create view elim_current_facts as $select_current_full_facts");
if ($opt_lock_tables)
{
do_query($dbh,"LOCK TABLES elim_facts, elim_attr1, elim_attr2 WRITE");
}
if ($opt_fast && defined($server->{vacuum}))
{
$server->vacuum(1,\$dbh);
}
####
#### Fill the facts table
####
$n_facts= $opt_loop_count;
if ($opt_fast && $server->{transactions})
{
$dbh->{AutoCommit} = 0;
}
print "Inserting $n_facts rows into facts table\n";
$loop_time=new Benchmark;
$query="insert into elim_facts values (";
for ($id=0; $id < $n_facts ; $id++)
{
do_query($dbh,"$query $id)");
}
if ($opt_fast && $server->{transactions})
{
$dbh->commit;
$dbh->{AutoCommit} = 1;
}
$end_time=new Benchmark;
print "Time to insert ($n_facts): " .
timestr(timediff($end_time, $loop_time),"all") . "\n\n";
####
#### Fill attr1 table
####
if ($opt_fast && $server->{transactions})
{
$dbh->{AutoCommit} = 0;
}
print "Inserting $n_facts rows into attr1 table\n";
$loop_time=new Benchmark;
$query="insert into elim_attr1 values (";
for ($id=0; $id < $n_facts ; $id++)
{
$attr1= ceil(rand($n_facts));
do_query($dbh,"$query $id, $attr1)");
}
if ($opt_fast && $server->{transactions})
{
$dbh->commit;
$dbh->{AutoCommit} = 1;
}
$end_time=new Benchmark;
print "Time to insert ($n_facts): " .
timestr(timediff($end_time, $loop_time),"all") . "\n\n";
####
#### Fill attr2 table
####
if ($opt_fast && $server->{transactions})
{
$dbh->{AutoCommit} = 0;
}
print "Inserting $n_facts rows into attr2 table\n";
$loop_time=new Benchmark;
for ($id=0; $id < $n_facts ; $id++)
{
# Two values for each $id - current one and obsolete one.
$attr1= ceil(rand($n_facts));
$query="insert into elim_attr2 values ($id, $attr1, now())";
do_query($dbh,$query);
$query="insert into elim_attr2 values ($id, $attr1, '2009-01-01')";
do_query($dbh,$query);
}
if ($opt_fast && $server->{transactions})
{
$dbh->commit;
$dbh->{AutoCommit} = 1;
}
$end_time=new Benchmark;
print "Time to insert ($n_facts): " .
timestr(timediff($end_time, $loop_time),"all") . "\n\n";
####
#### Finalize the database population
####
if ($opt_lock_tables)
{
do_query($dbh,"UNLOCK TABLES");
}
if ($opt_fast && defined($server->{vacuum}))
{
$server->vacuum(0,\$dbh,["elim_facts", "elim_attr1", "elim_attr2"]);
}
if ($opt_lock_tables)
{
do_query($dbh,"LOCK TABLES elim_facts, elim_attr1, elim_attr2 WRITE");
}
####
#### Do some selects on the table
####
select_test:
#
# The selects will be:
# - N pk-lookups with all attributes
# - pk-attribute-based lookup
# - latest-attribute value based lookup.
###
### Bare facts select:
###
print "testing bare facts facts table\n";
$loop_time=new Benchmark;
$rows=0;
for ($i=0 ; $i < $opt_medium_loop_count ; $i++)
{
$val= ceil(rand($n_facts));
$rows+=fetch_all_rows($dbh,"select * from elim_facts where id=$val");
}
$count=$i;
$end_time=new Benchmark;
print "time for select_bare_facts ($count:$rows): " .
timestr(timediff($end_time, $loop_time),"all") . "\n";
###
### Full facts select, no elimination:
###
print "testing full facts facts table\n";
$loop_time=new Benchmark;
$rows=0;
for ($i=0 ; $i < $opt_medium_loop_count ; $i++)
{
$val= rand($n_facts);
$rows+=fetch_all_rows($dbh,"select * from elim_current_facts where id=$val");
}
$count=$i;
$end_time=new Benchmark;
print "time for select_two_attributes ($count:$rows): " .
timestr(timediff($end_time, $loop_time),"all") . "\n";
###
### Now with elimination: select only only one fact
###
print "testing selection of one attribute\n";
$loop_time=new Benchmark;
$rows=0;
for ($i=0 ; $i < $opt_medium_loop_count ; $i++)
{
$val= rand($n_facts);
$rows+=fetch_all_rows($dbh,"select id, attr1 from elim_current_facts where id=$val");
}
$count=$i;
$end_time=new Benchmark;
print "time for select_one_attribute ($count:$rows): " .
timestr(timediff($end_time, $loop_time),"all") . "\n";
###
### Now with elimination: select only only one fact
###
print "testing selection of one attribute\n";
$loop_time=new Benchmark;
$rows=0;
for ($i=0 ; $i < $opt_medium_loop_count ; $i++)
{
$val= rand($n_facts);
$rows+=fetch_all_rows($dbh,"select id, attr2 from elim_current_facts where id=$val");
}
$count=$i;
$end_time=new Benchmark;
print "time for select_one_attribute ($count:$rows): " .
timestr(timediff($end_time, $loop_time),"all") . "\n";
###
### TODO...
###
;
####
#### End of benchmark
####
if ($opt_lock_tables)
{
do_query($dbh,"UNLOCK TABLES");
}
if (!$opt_skip_delete)
{
do_query($dbh,"drop table elim_facts, elim_attr1, elim_attr2" . $server->{'drop_attr'});
}
if ($opt_fast && defined($server->{vacuum}))
{
$server->vacuum(0,\$dbh);
}
$dbh->disconnect; # close connection
end_benchmark($start_time);

View File

@ -73,7 +73,7 @@ ADD_EXECUTABLE(mysqld
partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
rpl_rli.cc rpl_mi.cc sql_servers.cc rpl_rli.cc rpl_mi.cc sql_servers.cc
sql_connect.cc scheduler.cc sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc sql_profile.cc event_parse_data.cc opt_table_elimination.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
${PROJECT_SOURCE_DIR}/include/mysqld_error.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h

View File

@ -121,7 +121,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
event_queue.cc event_db_repository.cc events.cc \ event_queue.cc event_db_repository.cc events.cc \
sql_plugin.cc sql_binlog.cc \ sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \
sql_servers.cc event_parse_data.cc sql_servers.cc event_parse_data.cc \
opt_table_elimination.cc
nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c

View File

@ -1915,6 +1915,15 @@ void Item_field::reset_field(Field *f)
name= (char*) f->field_name; name= (char*) f->field_name;
} }
bool Item_field::check_column_usage_processor(uchar *arg)
{
Field_enumerator *fe= (Field_enumerator*)arg;
fe->see_field(field);
return FALSE;
}
const char *Item_ident::full_name() const const char *Item_ident::full_name() const
{ {
char *tmp; char *tmp;
@ -3380,7 +3389,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
/* store pointer on SELECT_LEX from which item is dependent */ /* store pointer on SELECT_LEX from which item is dependent */
if (mark_item) if (mark_item)
mark_item->depended_from= last; mark_item->depended_from= last;
current->mark_as_dependent(last); current->mark_as_dependent(last, resolved_item);
if (thd->lex->describe & DESCRIBE_EXTENDED) if (thd->lex->describe & DESCRIBE_EXTENDED)
{ {
char warn_buff[MYSQL_ERRMSG_SIZE]; char warn_buff[MYSQL_ERRMSG_SIZE];

View File

@ -731,7 +731,11 @@ public:
virtual bool val_bool_result() { return val_bool(); } virtual bool val_bool_result() { return val_bool(); }
virtual bool is_null_result() { return is_null(); } virtual bool is_null_result() { return is_null(); }
/* bit map of tables used by item */ /*
Bitmap of tables used by item
(note: if you need to check dependencies on individual columns, check out
check_column_usage_processor)
*/
virtual table_map used_tables() const { return (table_map) 0L; } virtual table_map used_tables() const { return (table_map) 0L; }
/* /*
Return table map of tables that can't be NULL tables (tables that are Return table map of tables that can't be NULL tables (tables that are
@ -888,6 +892,8 @@ public:
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; } virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
virtual bool is_expensive_processor(uchar *arg) { return 0; } virtual bool is_expensive_processor(uchar *arg) { return 0; }
virtual bool register_field_in_read_map(uchar *arg) { return 0; } virtual bool register_field_in_read_map(uchar *arg) { return 0; }
virtual bool check_column_usage_processor(uchar *arg) { return 0; }
virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; }
/* /*
Check if a partition function is allowed Check if a partition function is allowed
SYNOPSIS SYNOPSIS
@ -1012,6 +1018,14 @@ public:
}; };
/* Data for Item::check_column_usage_processor */
class Field_enumerator
{
public:
virtual void see_field(Field *field)= 0;
virtual ~Field_enumerator() {}; /* Shut up compiler warning */
};
class sp_head; class sp_head;
@ -1477,6 +1491,7 @@ public:
bool find_item_in_field_list_processor(uchar *arg); bool find_item_in_field_list_processor(uchar *arg);
bool register_field_in_read_map(uchar *arg); bool register_field_in_read_map(uchar *arg);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_column_usage_processor(uchar *arg);
void cleanup(); void cleanup();
bool result_as_longlong() bool result_as_longlong()
{ {
@ -2203,6 +2218,10 @@ public:
if (!depended_from) if (!depended_from)
(*ref)->update_used_tables(); (*ref)->update_used_tables();
} }
bool const_item() const
{
return (*ref)->const_item();
}
table_map not_null_tables() const { return (*ref)->not_null_tables(); } table_map not_null_tables() const { return (*ref)->not_null_tables(); }
void set_result_field(Field *field) { result_field= field; } void set_result_field(Field *field) { result_field= field; }
bool is_result_field() { return 1; } bool is_result_field() { return 1; }

View File

@ -39,7 +39,7 @@ inline Item * and_items(Item* cond, Item *item)
Item_subselect::Item_subselect(): Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), thd(0), substitution(0), Item_result_field(), value_assigned(0), thd(0), substitution(0),
engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0),
const_item_cache(1), engine_changed(0), changed(0), is_correlated(FALSE) const_item_cache(1), in_fix_fields(0), engine_changed(0), changed(0), is_correlated(FALSE)
{ {
with_subselect= 1; with_subselect= 1;
reset(); reset();
@ -151,10 +151,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param)); engine->set_thd((thd= thd_param));
if (!in_fix_fields)
refers_to.empty();
eliminated= FALSE;
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
return TRUE; return TRUE;
in_fix_fields++;
res= engine->prepare(); res= engine->prepare();
// all transformation is done (used by prepared statements) // all transformation is done (used by prepared statements)
@ -181,12 +185,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
if (!(*ref)->fixed) if (!(*ref)->fixed)
ret= (*ref)->fix_fields(thd, ref); ret= (*ref)->fix_fields(thd, ref);
thd->where= save_where; thd->where= save_where;
in_fix_fields--;
return ret; return ret;
} }
// Is it one field subselect? // Is it one field subselect?
if (engine->cols() > max_columns) if (engine->cols() > max_columns)
{ {
my_error(ER_OPERAND_COLUMNS, MYF(0), 1); my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
in_fix_fields--;
return TRUE; return TRUE;
} }
fix_length_and_dec(); fix_length_and_dec();
@ -203,11 +209,30 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
fixed= 1; fixed= 1;
err: err:
in_fix_fields--;
thd->where= save_where; thd->where= save_where;
return res; return res;
} }
bool Item_subselect::check_column_usage_processor(uchar *arg)
{
List_iterator<Item> it(refers_to);
Item *item;
while ((item= it++))
{
if (item->walk(&Item::check_column_usage_processor,FALSE, arg))
return TRUE;
}
return FALSE;
}
bool Item_subselect::mark_as_eliminated_processor(uchar *arg)
{
eliminated= TRUE;
return FALSE;
}
bool Item_subselect::walk(Item_processor processor, bool walk_subquery, bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
uchar *argument) uchar *argument)
{ {
@ -225,6 +250,7 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
if (lex->having && (lex->having)->walk(processor, walk_subquery, if (lex->having && (lex->having)->walk(processor, walk_subquery,
argument)) argument))
return 1; return 1;
/* TODO: why does this walk WHERE/HAVING but not ON expressions of outer joins? */
while ((item=li++)) while ((item=li++))
{ {

View File

@ -52,8 +52,16 @@ protected:
bool have_to_be_excluded; bool have_to_be_excluded;
/* cache of constant state */ /* cache of constant state */
bool const_item_cache; bool const_item_cache;
public: public:
/*
References from inside the subquery to the select that this predicate is
in. References to parent selects not included.
*/
List<Item> refers_to;
int in_fix_fields;
bool eliminated;
/* changed engine indicator */ /* changed engine indicator */
bool engine_changed; bool engine_changed;
/* subquery is transformed */ /* subquery is transformed */
@ -126,6 +134,8 @@ public:
virtual void reset_value_registration() {} virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; } enum_parsing_place place() { return parsing_place; }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
bool mark_as_eliminated_processor(uchar *arg);
bool check_column_usage_processor(uchar *arg);
/** /**
Get the SELECT_LEX structure associated with this Item. Get the SELECT_LEX structure associated with this Item.

View File

@ -350,7 +350,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl= sl->master_unit()->outer_select() ) sl= sl->master_unit()->outer_select() )
sl->master_unit()->item->with_sum_func= 1; sl->master_unit()->item->with_sum_func= 1;
} }
thd->lex->current_select->mark_as_dependent(aggr_sel); thd->lex->current_select->mark_as_dependent(aggr_sel, NULL);
return FALSE; return FALSE;
} }
@ -542,11 +542,6 @@ void Item_sum::update_used_tables ()
args[i]->update_used_tables(); args[i]->update_used_tables();
used_tables_cache|= args[i]->used_tables(); used_tables_cache|= args[i]->used_tables();
} }
used_tables_cache&= PSEUDO_TABLE_BITS;
/* the aggregate function is aggregated into its local context */
used_tables_cache |= (1 << aggr_sel->join->tables) - 1;
} }
} }

View File

@ -255,6 +255,12 @@ protected:
*/ */
Item **orig_args, *tmp_orig_args[2]; Item **orig_args, *tmp_orig_args[2];
table_map used_tables_cache; table_map used_tables_cache;
/*
TRUE <=> We've managed to calculate the value of this Item in
opt_sum_query(), hence it can be considered constant at all subsequent
steps.
*/
bool forced_const; bool forced_const;
public: public:
@ -341,6 +347,15 @@ public:
virtual const char *func_name() const= 0; virtual const char *func_name() const= 0;
virtual Item *result_item(Field *field) virtual Item *result_item(Field *field)
{ return new Item_field(field); } { return new Item_field(field); }
/*
Return bitmap of tables that are needed to evaluate the item.
The implementation takes into account the used strategy: items resolved
at optimization phase will report 0.
Items that depend on the number of join output records, but not columns
of any particular table (like COUNT(*)) will report 0 from used_tables(),
but will still return false from const_item().
*/
table_map used_tables() const { return used_tables_cache; } table_map used_tables() const { return used_tables_cache; }
void update_used_tables (); void update_used_tables ();
void cleanup() void cleanup()

1295
sql/opt_table_elimination.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -93,6 +93,34 @@ public:
} }
}; };
/* An iterator to quickly walk over bits in unlonglong bitmap. */
class Table_map_iterator
{
ulonglong bmp;
uint no;
public:
Table_map_iterator(ulonglong t) : bmp(t), no(0) {}
int next_bit()
{
static const char last_bit[16]= {32, 0, 1, 0,
2, 0, 1, 0,
3, 0, 1, 0,
2, 0, 1, 0};
uint bit;
while ((bit= last_bit[bmp & 0xF]) == 32)
{
no += 4;
bmp= bmp >> 4;
if (!bmp)
return BITMAP_END;
}
bmp &= ~(1LL << bit);
return no + bit;
}
int operator++(int) { return next_bit(); }
enum { BITMAP_END= 64 };
};
template <> class Bitmap<64> template <> class Bitmap<64>
{ {
ulonglong map; ulonglong map;
@ -136,5 +164,10 @@ public:
my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; } my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
char *print(char *buf) const { longlong2str(map,buf,16); return buf; } char *print(char *buf) const { longlong2str(map,buf,16); return buf; }
ulonglong to_ulonglong() const { return map; } ulonglong to_ulonglong() const { return map; }
class Iterator : public Table_map_iterator
{
public:
Iterator(Bitmap<64> &bmp) : Table_map_iterator(bmp.map) {}
};
}; };

View File

@ -1778,8 +1778,9 @@ void st_select_lex_unit::exclude_tree()
'last' should be reachable from this st_select_lex_node 'last' should be reachable from this st_select_lex_node
*/ */
void st_select_lex::mark_as_dependent(st_select_lex *last) void st_select_lex::mark_as_dependent(st_select_lex *last, Item *dependency)
{ {
SELECT_LEX *next_to_last;
/* /*
Mark all selects from resolved to 1 before select where was Mark all selects from resolved to 1 before select where was
found table as depended (of select where was found table) found table as depended (of select where was found table)
@ -1787,6 +1788,7 @@ void st_select_lex::mark_as_dependent(st_select_lex *last)
for (SELECT_LEX *s= this; for (SELECT_LEX *s= this;
s && s != last; s && s != last;
s= s->outer_select()) s= s->outer_select())
{
if (!(s->uncacheable & UNCACHEABLE_DEPENDENT)) if (!(s->uncacheable & UNCACHEABLE_DEPENDENT))
{ {
// Select is dependent of outer select // Select is dependent of outer select
@ -1802,8 +1804,12 @@ void st_select_lex::mark_as_dependent(st_select_lex *last)
sl->uncacheable|= UNCACHEABLE_UNITED; sl->uncacheable|= UNCACHEABLE_UNITED;
} }
} }
next_to_last= s;
}
is_correlated= TRUE; is_correlated= TRUE;
this->master_unit()->item->is_correlated= TRUE; this->master_unit()->item->is_correlated= TRUE;
if (dependency)
next_to_last->master_unit()->item->refers_to.push_back(dependency);
} }
bool st_select_lex_node::set_braces(bool value) { return 1; } bool st_select_lex_node::set_braces(bool value) { return 1; }

View File

@ -743,7 +743,7 @@ public:
return master_unit()->return_after_parsing(); return master_unit()->return_after_parsing();
} }
void mark_as_dependent(st_select_lex *last); void mark_as_dependent(st_select_lex *last, Item *dependency);
bool set_braces(bool value); bool set_braces(bool value);
bool inc_in_sum_expr(); bool inc_in_sum_expr();

View File

@ -60,7 +60,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
table_map table_map, SELECT_LEX *select_lex, table_map table_map, SELECT_LEX *select_lex,
st_sargable_param **sargables); st_sargable_param **sargables);
static int sort_keyuse(KEYUSE *a,KEYUSE *b); static int sort_keyuse(KEYUSE *a,KEYUSE *b);
static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key);
static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
table_map used_tables); table_map used_tables);
static bool choose_plan(JOIN *join,table_map join_tables); static bool choose_plan(JOIN *join,table_map join_tables);
@ -115,7 +114,7 @@ static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
COND *conds, bool top); COND *conds, bool top);
static bool check_interleaving_with_nj(JOIN_TAB *next); static bool check_interleaving_with_nj(JOIN_TAB *next);
static void restore_prev_nj_state(JOIN_TAB *last); static void restore_prev_nj_state(JOIN_TAB *last);
static void reset_nj_counters(List<TABLE_LIST> *join_list); static uint reset_nj_counters(JOIN *join, List<TABLE_LIST> *join_list);
static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list, static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
uint first_unused); uint first_unused);
@ -1012,7 +1011,7 @@ JOIN::optimize()
DBUG_RETURN(1); DBUG_RETURN(1);
} }
reset_nj_counters(join_list); reset_nj_counters(this, join_list);
make_outerjoin_info(this); make_outerjoin_info(this);
/* /*
@ -2381,6 +2380,13 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
} }
else else
{ {
/*
When in EXPLAIN, delay deleting the joins so that they are still
available when we're producing EXPLAIN EXTENDED warning text.
*/
if (select_options & SELECT_DESCRIBE)
free_join= 0;
if (!(join= new JOIN(thd, fields, select_options, result))) if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
thd_proc_info(thd, "init"); thd_proc_info(thd, "init");
@ -2646,24 +2652,31 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
~outer_join, join->select_lex, &sargables)) ~outer_join, join->select_lex, &sargables))
goto error; goto error;
/* Read tables with 0 or 1 rows (system tables) */
join->const_table_map= 0; join->const_table_map= 0;
join->const_tables= const_count;
eliminate_tables(join);
const_count= join->const_tables;
found_const_table_map= join->const_table_map;
/* Read tables with 0 or 1 rows (system tables) */
for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count; for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count;
p_pos < p_end ; p_pos < p_end ;
p_pos++) p_pos++)
{ {
int tmp;
s= p_pos->table; s= p_pos->table;
s->type=JT_SYSTEM; if (! (s->table->map & join->eliminated_tables))
join->const_table_map|=s->table->map;
if ((tmp=join_read_const_table(s, p_pos)))
{ {
if (tmp > 0) int tmp;
goto error; // Fatal error s->type=JT_SYSTEM;
join->const_table_map|=s->table->map;
if ((tmp=join_read_const_table(s, p_pos)))
{
if (tmp > 0)
goto error; // Fatal error
}
else
found_const_table_map|= s->table->map;
} }
else
found_const_table_map|= s->table->map;
} }
/* loop until no more const tables are found */ /* loop until no more const tables are found */
@ -2688,7 +2701,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
substitution of a const table the key value happens to be null substitution of a const table the key value happens to be null
then we can state that there are no matches for this equi-join. then we can state that there are no matches for this equi-join.
*/ */
if ((keyuse= s->keyuse) && *s->on_expr_ref && !s->embedding_map) if ((keyuse= s->keyuse) && *s->on_expr_ref && !s->embedding_map &&
!(table->map & join->eliminated_tables))
{ {
/* /*
When performing an outer join operation if there are no matching rows When performing an outer join operation if there are no matching rows
@ -2965,6 +2979,26 @@ typedef struct key_field_t {
This is called for OR between different levels. This is called for OR between different levels.
That is, the function operates on an array of KEY_FIELD elements which has
two parts:
$LEFT_PART $RIGHT_PART
+-----------------------+-----------------------+
start new_fields end
$LEFT_PART and $RIGHT_PART are arrays that have KEY_FIELD elements for two
parts of the OR condition. Our task is to produce an array of KEY_FIELD
elements that would correspond to "$LEFT_PART OR $RIGHT_PART".
The rules for combining elements are as follows:
(keyfieldA1 AND keyfieldA2 AND ...) OR (keyfieldB1 AND keyfieldB2 AND ...)=
AND_ij (keyfieldA_i OR keyfieldB_j)
We discard all (keyfieldA_i OR keyfieldB_j) that refer to different
fields. For those referring to the same field, the logic is as follows:
t.keycol=
To be able to do 'ref_or_null' we merge a comparison of a column To be able to do 'ref_or_null' we merge a comparison of a column
and 'column IS NULL' to one test. This is useful for sub select queries and 'column IS NULL' to one test. This is useful for sub select queries
that are internally transformed to something like:. that are internally transformed to something like:.
@ -3990,8 +4024,7 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
/** Save const tables first as used tables. */ /** Save const tables first as used tables. */
static void void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
{ {
join->positions[idx].table= table; join->positions[idx].table= table;
join->positions[idx].key=key; join->positions[idx].key=key;
@ -4592,7 +4625,7 @@ choose_plan(JOIN *join, table_map join_tables)
DBUG_ENTER("choose_plan"); DBUG_ENTER("choose_plan");
join->cur_embedding_map= 0; join->cur_embedding_map= 0;
reset_nj_counters(join->join_list); reset_nj_counters(join, join->join_list);
/* /*
if (SELECT_STRAIGHT_JOIN option is set) if (SELECT_STRAIGHT_JOIN option is set)
reorder tables so dependent tables come after tables they depend reorder tables so dependent tables come after tables they depend
@ -5757,6 +5790,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table)
tables= 1; tables= 1;
const_tables= 0; const_tables= 0;
const_table_map= 0; const_table_map= 0;
eliminated_tables= 0;
tmp_table_param.field_count= tmp_table_param.sum_func_count= tmp_table_param.field_count= tmp_table_param.sum_func_count=
tmp_table_param.func_count= 0; tmp_table_param.func_count= 0;
tmp_table_param.copy_field= tmp_table_param.copy_field_end=0; tmp_table_param.copy_field= tmp_table_param.copy_field_end=0;
@ -6021,7 +6055,7 @@ make_outerjoin_info(JOIN *join)
} }
if (!tab->first_inner) if (!tab->first_inner)
tab->first_inner= nested_join->first_nested; tab->first_inner= nested_join->first_nested;
if (++nested_join->counter < nested_join->join_list.elements) if (++nested_join->counter < nested_join->n_tables)
break; break;
/* Table tab is the last inner table for nested join. */ /* Table tab is the last inner table for nested join. */
nested_join->first_nested->last_inner= tab; nested_join->first_nested->last_inner= tab;
@ -8575,6 +8609,8 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
conds= simplify_joins(join, &nested_join->join_list, conds, top); conds= simplify_joins(join, &nested_join->join_list, conds, top);
used_tables= nested_join->used_tables; used_tables= nested_join->used_tables;
not_null_tables= nested_join->not_null_tables; not_null_tables= nested_join->not_null_tables;
/* The following two might become unequal after table elimination: */
nested_join->n_tables= nested_join->join_list.elements;
} }
else else
{ {
@ -8733,7 +8769,7 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
with anything) with anything)
2. we could run out bits in nested_join_map otherwise. 2. we could run out bits in nested_join_map otherwise.
*/ */
if (nested_join->join_list.elements != 1) if (nested_join->n_tables != 1)
{ {
nested_join->nj_map= (nested_join_map) 1 << first_unused++; nested_join->nj_map= (nested_join_map) 1 << first_unused++;
first_unused= build_bitmap_for_nested_joins(&nested_join->join_list, first_unused= build_bitmap_for_nested_joins(&nested_join->join_list,
@ -8755,21 +8791,26 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
tables which will be ignored. tables which will be ignored.
*/ */
static void reset_nj_counters(List<TABLE_LIST> *join_list) static uint reset_nj_counters(JOIN *join, List<TABLE_LIST> *join_list)
{ {
List_iterator<TABLE_LIST> li(*join_list); List_iterator<TABLE_LIST> li(*join_list);
TABLE_LIST *table; TABLE_LIST *table;
DBUG_ENTER("reset_nj_counters"); DBUG_ENTER("reset_nj_counters");
uint n=0;
while ((table= li++)) while ((table= li++))
{ {
NESTED_JOIN *nested_join; NESTED_JOIN *nested_join;
if ((nested_join= table->nested_join)) if ((nested_join= table->nested_join))
{ {
nested_join->counter= 0; nested_join->counter= 0;
reset_nj_counters(&nested_join->join_list); //nested_join->n_tables= my_count_bits(nested_join->used_tables &
// ~join->eliminated_tables);
nested_join->n_tables= reset_nj_counters(join, &nested_join->join_list);
} }
if (table->table && (table->table->map & ~join->eliminated_tables))
n++;
} }
DBUG_VOID_RETURN; DBUG_RETURN(n);
} }
@ -8894,7 +8935,7 @@ static bool check_interleaving_with_nj(JOIN_TAB *next_tab)
join->cur_embedding_map |= next_emb->nested_join->nj_map; join->cur_embedding_map |= next_emb->nested_join->nj_map;
} }
if (next_emb->nested_join->join_list.elements != if (next_emb->nested_join->n_tables !=
next_emb->nested_join->counter) next_emb->nested_join->counter)
break; break;
@ -8926,9 +8967,23 @@ static void restore_prev_nj_state(JOIN_TAB *last)
JOIN *join= last->join; JOIN *join= last->join;
while (last_emb) while (last_emb)
{ {
/*
psergey-elim: (nevermind)
new_prefix= cur_prefix & ~last;
if (!(new_prefix & cur_table_map)) // removed last inner table
{
join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
}
else (current)
{
// Won't hurt doing it all the time:
join->cur_embedding_map |= ...;
}
else
*/
if (!(--last_emb->nested_join->counter)) if (!(--last_emb->nested_join->counter))
join->cur_embedding_map&= ~last_emb->nested_join->nj_map; join->cur_embedding_map&= ~last_emb->nested_join->nj_map;
else if (last_emb->nested_join->join_list.elements-1 == else if (last_emb->nested_join->n_tables-1 ==
last_emb->nested_join->counter) last_emb->nested_join->counter)
join->cur_embedding_map|= last_emb->nested_join->nj_map; join->cur_embedding_map|= last_emb->nested_join->nj_map;
else else
@ -16238,6 +16293,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
tmp3.length(0); tmp3.length(0);
quick_type= -1; quick_type= -1;
/* Don't show eliminated tables */
if (table->map & join->eliminated_tables)
{
used_tables|=table->map;
continue;
}
item_list.empty(); item_list.empty();
/* id */ /* id */
item_list.push_back(new Item_uint((uint32) item_list.push_back(new Item_uint((uint32)
@ -16560,8 +16623,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
unit; unit;
unit= unit->next_unit()) unit= unit->next_unit())
{ {
if (mysql_explain_union(thd, unit, result)) if (!(unit->item && unit->item->eliminated))
DBUG_VOID_RETURN; {
if (mysql_explain_union(thd, unit, result))
DBUG_VOID_RETURN;
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -16602,7 +16668,6 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
unit->fake_select_lex->options|= SELECT_DESCRIBE; unit->fake_select_lex->options|= SELECT_DESCRIBE;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec(); res= unit->exec();
res|= unit->cleanup();
} }
else else
{ {
@ -16635,6 +16700,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
*/ */
static void print_join(THD *thd, static void print_join(THD *thd,
table_map eliminated_tables,
String *str, String *str,
List<TABLE_LIST> *tables, List<TABLE_LIST> *tables,
enum_query_type query_type) enum_query_type query_type)
@ -16650,12 +16716,33 @@ static void print_join(THD *thd,
*t= ti++; *t= ti++;
DBUG_ASSERT(tables->elements >= 1); DBUG_ASSERT(tables->elements >= 1);
(*table)->print(thd, str, query_type); /*
Assert that the first table in the list isn't eliminated. This comes from
the fact that the first table can't be inner table of an outer join.
*/
DBUG_ASSERT(!eliminated_tables ||
!(((*table)->table && ((*table)->table->map & eliminated_tables)) ||
((*table)->nested_join && !((*table)->nested_join->used_tables &
~eliminated_tables))));
(*table)->print(thd, eliminated_tables, str, query_type);
TABLE_LIST **end= table + tables->elements; TABLE_LIST **end= table + tables->elements;
for (TABLE_LIST **tbl= table + 1; tbl < end; tbl++) for (TABLE_LIST **tbl= table + 1; tbl < end; tbl++)
{ {
TABLE_LIST *curr= *tbl; TABLE_LIST *curr= *tbl;
/*
The "eliminated_tables &&" check guards againist the case of
printing the query for CREATE VIEW. We do that without having run
JOIN::optimize() and so will have nested_join->used_tables==0.
*/
if (eliminated_tables &&
((curr->table && (curr->table->map & eliminated_tables)) ||
(curr->nested_join && !(curr->nested_join->used_tables &
~eliminated_tables))))
{
continue;
}
if (curr->outer_join) if (curr->outer_join)
{ {
/* MySQL converts right to left joins */ /* MySQL converts right to left joins */
@ -16665,7 +16752,7 @@ static void print_join(THD *thd,
str->append(STRING_WITH_LEN(" straight_join ")); str->append(STRING_WITH_LEN(" straight_join "));
else else
str->append(STRING_WITH_LEN(" join ")); str->append(STRING_WITH_LEN(" join "));
curr->print(thd, str, query_type); curr->print(thd, eliminated_tables, str, query_type);
if (curr->on_expr) if (curr->on_expr)
{ {
str->append(STRING_WITH_LEN(" on(")); str->append(STRING_WITH_LEN(" on("));
@ -16719,12 +16806,13 @@ Index_hint::print(THD *thd, String *str)
@param str string where table should be printed @param str string where table should be printed
*/ */
void TABLE_LIST::print(THD *thd, String *str, enum_query_type query_type) void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
enum_query_type query_type)
{ {
if (nested_join) if (nested_join)
{ {
str->append('('); str->append('(');
print_join(thd, str, &nested_join->join_list, query_type); print_join(thd, eliminated_tables, str, &nested_join->join_list, query_type);
str->append(')'); str->append(')');
} }
else else
@ -16866,7 +16954,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
{ {
str->append(STRING_WITH_LEN(" from ")); str->append(STRING_WITH_LEN(" from "));
/* go through join tree */ /* go through join tree */
print_join(thd, str, &top_join_list, query_type); print_join(thd, join? join->eliminated_tables: 0, str, &top_join_list, query_type);
} }
else if (where) else if (where)
{ {

View File

@ -285,7 +285,15 @@ public:
fetching data from a cursor fetching data from a cursor
*/ */
bool resume_nested_loop; bool resume_nested_loop;
table_map const_table_map,found_const_table_map; table_map const_table_map;
/*
Constant tables for which we have found a row (as opposed to those for
which we didn't).
*/
table_map found_const_table_map;
/* Tables removed by table elimination. Set to 0 before the elimination. */
table_map eliminated_tables;
/* /*
Bitmap of all inner tables from outer joins Bitmap of all inner tables from outer joins
*/ */
@ -425,6 +433,7 @@ public:
table= 0; table= 0;
tables= 0; tables= 0;
const_tables= 0; const_tables= 0;
eliminated_tables= 0;
join_list= 0; join_list= 0;
sort_and_group= 0; sort_and_group= 0;
first_record= 0; first_record= 0;
@ -530,6 +539,10 @@ public:
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 || return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
select_lex == unit->fake_select_lex)); select_lex == unit->fake_select_lex));
} }
inline table_map all_tables_map()
{
return (table_map(1) << tables) - 1;
}
private: private:
bool make_simple_join(JOIN *join, TABLE *tmp_table); bool make_simple_join(JOIN *join, TABLE *tmp_table);
}; };
@ -730,9 +743,12 @@ bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error); int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab); int safe_index_read(JOIN_TAB *tab);
COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value); COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key);
inline bool optimizer_flag(THD *thd, uint flag) inline bool optimizer_flag(THD *thd, uint flag)
{ {
return (thd->variables.optimizer_switch & flag); return (thd->variables.optimizer_switch & flag);
} }
void eliminate_tables(JOIN *join);

View File

@ -1366,7 +1366,8 @@ struct TABLE_LIST
return (derived || view || schema_table || (create && !table->db_stat) || return (derived || view || schema_table || (create && !table->db_stat) ||
!table); !table);
} }
void print(THD *thd, String *str, enum_query_type query_type); void print(THD *thd, table_map eliminated_tables, String *str,
enum_query_type query_type);
bool check_single_table(TABLE_LIST **table, table_map map, bool check_single_table(TABLE_LIST **table, table_map map,
TABLE_LIST *view); TABLE_LIST *view);
bool set_insert_values(MEM_ROOT *mem_root); bool set_insert_values(MEM_ROOT *mem_root);
@ -1615,7 +1616,11 @@ public:
typedef struct st_nested_join typedef struct st_nested_join
{ {
List<TABLE_LIST> join_list; /* list of elements in the nested join */ List<TABLE_LIST> join_list; /* list of elements in the nested join */
table_map used_tables; /* bitmap of tables in the nested join */ /*
Bitmap of tables within this nested join (including those embedded within
its children), including tables removed by table elimination.
*/
table_map used_tables;
table_map not_null_tables; /* tables that rejects nulls */ table_map not_null_tables; /* tables that rejects nulls */
struct st_join_table *first_nested;/* the first nested table in the plan */ struct st_join_table *first_nested;/* the first nested table in the plan */
/* /*
@ -1626,6 +1631,11 @@ typedef struct st_nested_join
Before each use the counters are zeroed by reset_nj_counters. Before each use the counters are zeroed by reset_nj_counters.
*/ */
uint counter; uint counter;
/*
Number of elements in join_list that were not (or contain table(s) that
weren't) removed by table elimination.
*/
uint n_tables;
nested_join_map nj_map; /* Bit used to identify this nested join*/ nested_join_map nj_map; /* Bit used to identify this nested join*/
} NESTED_JOIN; } NESTED_JOIN;