Merge maria-5.1 -> maria-5.1-table-elimination
This commit is contained in:
commit
57c199d8d1
@ -1920,3 +1920,4 @@ sql/share/swedish
|
||||
sql/share/ukrainian
|
||||
libmysqld/examples/mysqltest.cc
|
||||
extra/libevent/event-config.h
|
||||
libmysqld/opt_table_elimination.cc
|
||||
|
@ -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 \
|
||||
sql_tablespace.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)
|
||||
nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)
|
||||
|
@ -3,6 +3,9 @@ SET @old_max_allowed_packet= @@global.max_allowed_packet;
|
||||
SET @@global.max_allowed_packet = 2 * 1024 * 1024 + 1024;
|
||||
CREATE TABLE t1(data LONGBLOB);
|
||||
INSERT INTO t1 SELECT REPEAT('1', 2*1024*1024);
|
||||
SELECT COUNT(*) FROM t1;
|
||||
COUNT(*)
|
||||
1
|
||||
SET @old_general_log = @@global.general_log;
|
||||
SET @@global.general_log = 0;
|
||||
SET @@global.general_log = @old_general_log;
|
||||
|
@ -121,8 +121,8 @@ insert into t1 values (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
|
||||
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
|
||||
3 DEPENDENT UNION 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 Impossible HAVING
|
||||
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
|
||||
select * from t1 where 3 in (select (1+1) union select 1);
|
||||
a
|
||||
|
@ -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;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
|
||||
1 SIMPLE t2 const b b 22 const 1 Using index
|
||||
DROP TABLE t1,t2;
|
||||
CREATE TABLE t1(id int PRIMARY KEY, b int, e int);
|
||||
CREATE TABLE t2(i int, a int, INDEX si(i), INDEX ai(a));
|
||||
|
@ -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
|
||||
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
|
||||
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);
|
||||
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
|
||||
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using where; Using temporary; Using filesort
|
||||
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;
|
||||
End of 5.0 tests.
|
||||
CREATE TABLE t1 (a INT, b INT);
|
||||
|
204
mysql-test/r/table_elim.result
Normal file
204
mysql-test/r/table_elim.result
Normal 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;
|
@ -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
|
||||
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL NULL
|
||||
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);
|
||||
a b
|
||||
1 10
|
||||
|
@ -27,7 +27,8 @@ connect (con1, localhost, root,,);
|
||||
|
||||
CREATE TABLE t1(data LONGBLOB);
|
||||
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;
|
||||
--error 0,1
|
||||
remove_file $outfile;
|
||||
|
160
mysql-test/t/table_elim.test
Normal file
160
mysql-test/t/table_elim.test
Normal 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;
|
||||
|
@ -703,3 +703,73 @@
|
||||
fun:malloc
|
||||
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
|
||||
}
|
||||
|
||||
|
320
sql-bench/test-table-elimination.sh
Executable file
320
sql-bench/test-table-elimination.sh
Executable 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);
|
||||
|
@ -73,7 +73,7 @@ ADD_EXECUTABLE(mysqld
|
||||
partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
|
||||
rpl_rli.cc rpl_mi.cc sql_servers.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.h
|
||||
${PROJECT_SOURCE_DIR}/include/mysqld_error.h
|
||||
|
@ -121,7 +121,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
|
||||
event_queue.cc event_db_repository.cc events.cc \
|
||||
sql_plugin.cc sql_binlog.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
|
||||
|
||||
|
11
sql/item.cc
11
sql/item.cc
@ -1915,6 +1915,15 @@ void Item_field::reset_field(Field *f)
|
||||
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
|
||||
{
|
||||
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 */
|
||||
if (mark_item)
|
||||
mark_item->depended_from= last;
|
||||
current->mark_as_dependent(last);
|
||||
current->mark_as_dependent(last, resolved_item);
|
||||
if (thd->lex->describe & DESCRIBE_EXTENDED)
|
||||
{
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
|
21
sql/item.h
21
sql/item.h
@ -731,7 +731,11 @@ public:
|
||||
virtual bool val_bool_result() { return val_bool(); }
|
||||
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; }
|
||||
/*
|
||||
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 is_expensive_processor(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
|
||||
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;
|
||||
|
||||
|
||||
@ -1477,6 +1491,7 @@ public:
|
||||
bool find_item_in_field_list_processor(uchar *arg);
|
||||
bool register_field_in_read_map(uchar *arg);
|
||||
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
|
||||
bool check_column_usage_processor(uchar *arg);
|
||||
void cleanup();
|
||||
bool result_as_longlong()
|
||||
{
|
||||
@ -2203,6 +2218,10 @@ public:
|
||||
if (!depended_from)
|
||||
(*ref)->update_used_tables();
|
||||
}
|
||||
bool const_item() const
|
||||
{
|
||||
return (*ref)->const_item();
|
||||
}
|
||||
table_map not_null_tables() const { return (*ref)->not_null_tables(); }
|
||||
void set_result_field(Field *field) { result_field= field; }
|
||||
bool is_result_field() { return 1; }
|
||||
|
@ -39,7 +39,7 @@ inline Item * and_items(Item* cond, Item *item)
|
||||
Item_subselect::Item_subselect():
|
||||
Item_result_field(), value_assigned(0), thd(0), substitution(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;
|
||||
reset();
|
||||
@ -151,10 +151,14 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
|
||||
|
||||
DBUG_ASSERT(fixed == 0);
|
||||
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))
|
||||
return TRUE;
|
||||
|
||||
|
||||
in_fix_fields++;
|
||||
res= engine->prepare();
|
||||
|
||||
// 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)
|
||||
ret= (*ref)->fix_fields(thd, ref);
|
||||
thd->where= save_where;
|
||||
in_fix_fields--;
|
||||
return ret;
|
||||
}
|
||||
// Is it one field subselect?
|
||||
if (engine->cols() > max_columns)
|
||||
{
|
||||
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
|
||||
in_fix_fields--;
|
||||
return TRUE;
|
||||
}
|
||||
fix_length_and_dec();
|
||||
@ -203,11 +209,30 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
|
||||
fixed= 1;
|
||||
|
||||
err:
|
||||
in_fix_fields--;
|
||||
thd->where= save_where;
|
||||
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,
|
||||
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,
|
||||
argument))
|
||||
return 1;
|
||||
/* TODO: why does this walk WHERE/HAVING but not ON expressions of outer joins? */
|
||||
|
||||
while ((item=li++))
|
||||
{
|
||||
|
@ -52,8 +52,16 @@ protected:
|
||||
bool have_to_be_excluded;
|
||||
/* cache of constant state */
|
||||
bool const_item_cache;
|
||||
|
||||
|
||||
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 */
|
||||
bool engine_changed;
|
||||
/* subquery is transformed */
|
||||
@ -126,6 +134,8 @@ public:
|
||||
virtual void reset_value_registration() {}
|
||||
enum_parsing_place place() { return parsing_place; }
|
||||
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.
|
||||
|
@ -350,7 +350,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
|
||||
sl= sl->master_unit()->outer_select() )
|
||||
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;
|
||||
}
|
||||
|
||||
@ -542,11 +542,6 @@ void Item_sum::update_used_tables ()
|
||||
args[i]->update_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,6 +255,12 @@ protected:
|
||||
*/
|
||||
Item **orig_args, *tmp_orig_args[2];
|
||||
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;
|
||||
|
||||
public:
|
||||
@ -341,6 +347,15 @@ public:
|
||||
virtual const char *func_name() const= 0;
|
||||
virtual Item *result_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; }
|
||||
void update_used_tables ();
|
||||
void cleanup()
|
||||
|
1295
sql/opt_table_elimination.cc
Normal file
1295
sql/opt_table_elimination.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||
{
|
||||
ulonglong map;
|
||||
@ -136,5 +164,10 @@ public:
|
||||
my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
|
||||
char *print(char *buf) const { longlong2str(map,buf,16); return buf; }
|
||||
ulonglong to_ulonglong() const { return map; }
|
||||
class Iterator : public Table_map_iterator
|
||||
{
|
||||
public:
|
||||
Iterator(Bitmap<64> &bmp) : Table_map_iterator(bmp.map) {}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1778,8 +1778,9 @@ void st_select_lex_unit::exclude_tree()
|
||||
'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
|
||||
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;
|
||||
s && s != last;
|
||||
s= s->outer_select())
|
||||
{
|
||||
if (!(s->uncacheable & UNCACHEABLE_DEPENDENT))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
next_to_last= s;
|
||||
}
|
||||
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; }
|
||||
|
@ -743,7 +743,7 @@ public:
|
||||
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 inc_in_sum_expr();
|
||||
|
@ -60,7 +60,6 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
|
||||
table_map table_map, SELECT_LEX *select_lex,
|
||||
st_sargable_param **sargables);
|
||||
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,
|
||||
table_map used_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);
|
||||
static bool check_interleaving_with_nj(JOIN_TAB *next);
|
||||
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,
|
||||
uint first_unused);
|
||||
|
||||
@ -1012,7 +1011,7 @@ JOIN::optimize()
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
reset_nj_counters(join_list);
|
||||
reset_nj_counters(this, join_list);
|
||||
make_outerjoin_info(this);
|
||||
|
||||
/*
|
||||
@ -2381,6 +2380,13 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
|
||||
}
|
||||
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)))
|
||||
DBUG_RETURN(TRUE);
|
||||
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))
|
||||
goto error;
|
||||
|
||||
/* Read tables with 0 or 1 rows (system tables) */
|
||||
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;
|
||||
p_pos < p_end ;
|
||||
p_pos++)
|
||||
{
|
||||
int tmp;
|
||||
s= p_pos->table;
|
||||
s->type=JT_SYSTEM;
|
||||
join->const_table_map|=s->table->map;
|
||||
if ((tmp=join_read_const_table(s, p_pos)))
|
||||
if (! (s->table->map & join->eliminated_tables))
|
||||
{
|
||||
if (tmp > 0)
|
||||
goto error; // Fatal error
|
||||
int tmp;
|
||||
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 */
|
||||
@ -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
|
||||
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
|
||||
@ -2965,6 +2979,26 @@ typedef struct key_field_t {
|
||||
|
||||
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
|
||||
and 'column IS NULL' to one test. This is useful for sub select queries
|
||||
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. */
|
||||
|
||||
static void
|
||||
set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
|
||||
void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
|
||||
{
|
||||
join->positions[idx].table= table;
|
||||
join->positions[idx].key=key;
|
||||
@ -4592,7 +4625,7 @@ choose_plan(JOIN *join, table_map join_tables)
|
||||
DBUG_ENTER("choose_plan");
|
||||
|
||||
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)
|
||||
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;
|
||||
const_tables= 0;
|
||||
const_table_map= 0;
|
||||
eliminated_tables= 0;
|
||||
tmp_table_param.field_count= tmp_table_param.sum_func_count=
|
||||
tmp_table_param.func_count= 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)
|
||||
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;
|
||||
/* Table tab is the last inner table for nested join. */
|
||||
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);
|
||||
used_tables= nested_join->used_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
|
||||
{
|
||||
@ -8733,7 +8769,7 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
|
||||
with anything)
|
||||
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++;
|
||||
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.
|
||||
*/
|
||||
|
||||
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);
|
||||
TABLE_LIST *table;
|
||||
DBUG_ENTER("reset_nj_counters");
|
||||
uint n=0;
|
||||
while ((table= li++))
|
||||
{
|
||||
NESTED_JOIN *nested_join;
|
||||
if ((nested_join= table->nested_join))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (next_emb->nested_join->join_list.elements !=
|
||||
if (next_emb->nested_join->n_tables !=
|
||||
next_emb->nested_join->counter)
|
||||
break;
|
||||
|
||||
@ -8926,9 +8967,23 @@ static void restore_prev_nj_state(JOIN_TAB *last)
|
||||
JOIN *join= last->join;
|
||||
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))
|
||||
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)
|
||||
join->cur_embedding_map|= last_emb->nested_join->nj_map;
|
||||
else
|
||||
@ -16238,6 +16293,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
||||
tmp3.length(0);
|
||||
|
||||
quick_type= -1;
|
||||
|
||||
/* Don't show eliminated tables */
|
||||
if (table->map & join->eliminated_tables)
|
||||
{
|
||||
used_tables|=table->map;
|
||||
continue;
|
||||
}
|
||||
|
||||
item_list.empty();
|
||||
/* id */
|
||||
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->next_unit())
|
||||
{
|
||||
if (mysql_explain_union(thd, unit, result))
|
||||
DBUG_VOID_RETURN;
|
||||
if (!(unit->item && unit->item->eliminated))
|
||||
{
|
||||
if (mysql_explain_union(thd, unit, result))
|
||||
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;
|
||||
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
|
||||
res= unit->exec();
|
||||
res|= unit->cleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -16635,6 +16700,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
|
||||
*/
|
||||
|
||||
static void print_join(THD *thd,
|
||||
table_map eliminated_tables,
|
||||
String *str,
|
||||
List<TABLE_LIST> *tables,
|
||||
enum_query_type query_type)
|
||||
@ -16650,12 +16716,33 @@ static void print_join(THD *thd,
|
||||
*t= ti++;
|
||||
|
||||
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;
|
||||
for (TABLE_LIST **tbl= table + 1; tbl < end; 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)
|
||||
{
|
||||
/* MySQL converts right to left joins */
|
||||
@ -16665,7 +16752,7 @@ static void print_join(THD *thd,
|
||||
str->append(STRING_WITH_LEN(" straight_join "));
|
||||
else
|
||||
str->append(STRING_WITH_LEN(" join "));
|
||||
curr->print(thd, str, query_type);
|
||||
curr->print(thd, eliminated_tables, str, query_type);
|
||||
if (curr->on_expr)
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
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(')');
|
||||
}
|
||||
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 "));
|
||||
/* 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)
|
||||
{
|
||||
|
@ -285,7 +285,15 @@ public:
|
||||
fetching data from a cursor
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -425,6 +433,7 @@ public:
|
||||
table= 0;
|
||||
tables= 0;
|
||||
const_tables= 0;
|
||||
eliminated_tables= 0;
|
||||
join_list= 0;
|
||||
sort_and_group= 0;
|
||||
first_record= 0;
|
||||
@ -530,6 +539,10 @@ public:
|
||||
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
|
||||
select_lex == unit->fake_select_lex));
|
||||
}
|
||||
inline table_map all_tables_map()
|
||||
{
|
||||
return (table_map(1) << tables) - 1;
|
||||
}
|
||||
private:
|
||||
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 safe_index_read(JOIN_TAB *tab);
|
||||
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)
|
||||
{
|
||||
return (thd->variables.optimizer_switch & flag);
|
||||
}
|
||||
|
||||
void eliminate_tables(JOIN *join);
|
||||
|
||||
|
14
sql/table.h
14
sql/table.h
@ -1366,7 +1366,8 @@ struct TABLE_LIST
|
||||
return (derived || view || schema_table || (create && !table->db_stat) ||
|
||||
!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,
|
||||
TABLE_LIST *view);
|
||||
bool set_insert_values(MEM_ROOT *mem_root);
|
||||
@ -1615,7 +1616,11 @@ public:
|
||||
typedef struct st_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 */
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user