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
libmysqld/examples/mysqltest.cc
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 \
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)

View File

@ -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;

View File

@ -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

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;
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));

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
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);

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
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

View File

@ -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;

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: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
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

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 \
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

View File

@ -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];

View File

@ -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; }

View File

@ -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++))
{

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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

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>
{
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) {}
};
};

View File

@ -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; }

View File

@ -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();

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,
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)
{

View File

@ -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);

View File

@ -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;