mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
Merge
This commit is contained in:
commit
63f78fa887
33 changed files with 4068 additions and 268 deletions
|
@ -30,7 +30,17 @@ extern "C" {
|
|||
|
||||
#define tree_set_pointer(element,ptr) *((uchar **) (element+1))=((uchar*) (ptr))
|
||||
|
||||
/*
|
||||
A tree with its flag set to TREE_ONLY_DUPS behaves differently on inserting
|
||||
an element that is not in the tree:
|
||||
the element is not added at all, but instead tree_insert() returns a special
|
||||
address TREE_ELEMENT_UNIQUE as an indication that the function has not failed
|
||||
due to lack of memory.
|
||||
*/
|
||||
|
||||
#define TREE_ELEMENT_UNIQUE ((TREE_ELEMENT *) 1)
|
||||
#define TREE_NO_DUPS 1
|
||||
#define TREE_ONLY_DUPS 2
|
||||
|
||||
typedef enum { left_root_right, right_root_left } TREE_WALK;
|
||||
typedef uint32 element_count;
|
||||
|
|
|
@ -539,7 +539,7 @@ INSERT INTO t1 SELECT * FROM t1;
|
|||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
SET SESSION sort_buffer_size=1;
|
||||
SET SESSION sort_buffer_size=1024*8;
|
||||
EXPLAIN
|
||||
SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%'
|
||||
ORDER BY a,b;
|
||||
|
|
1042
mysql-test/r/index_intersect.result
Normal file
1042
mysql-test/r/index_intersect.result
Normal file
File diff suppressed because it is too large
Load diff
1041
mysql-test/r/index_intersect_innodb.result
Normal file
1041
mysql-test/r/index_intersect_innodb.result
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,5 @@
|
|||
set @optimizer_switch_save= @@optimizer_switch;
|
||||
set optimizer_switch='index_merge_sort_intersection=off';
|
||||
#---------------- Index merge test 2 -------------------------------------------
|
||||
SET SESSION STORAGE_ENGINE = InnoDB;
|
||||
drop table if exists t1,t2;
|
||||
|
@ -709,3 +711,4 @@ WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
|||
COUNT(*)
|
||||
6145
|
||||
DROP TABLE t1;
|
||||
set optimizer_switch= @optimizer_switch_save;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
set @optimizer_switch_save= @@optimizer_switch;
|
||||
set optimizer_switch='index_merge_sort_intersection=off';
|
||||
#---------------- Index merge test 1 -------------------------------------------
|
||||
SET SESSION STORAGE_ENGINE = MyISAM;
|
||||
drop table if exists t0, t1, t2, t3, t4;
|
||||
|
@ -569,9 +571,7 @@ INSERT INTO t1 SELECT * FROM t1;
|
|||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
SET SESSION sort_buffer_size=1;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '1'
|
||||
SET SESSION sort_buffer_size=1024*8;
|
||||
EXPLAIN
|
||||
SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%'
|
||||
ORDER BY a,b;
|
||||
|
@ -1494,19 +1494,19 @@ DROP TABLE t1,t2;
|
|||
#
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set optimizer_switch='index_merge=off,index_merge_union=off';
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set optimizer_switch='index_merge_union=on';
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set optimizer_switch='default,index_merge_sort_union=off';
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set optimizer_switch=4;
|
||||
ERROR 42000: Variable 'optimizer_switch' can't be set to the value of '4'
|
||||
set optimizer_switch=NULL;
|
||||
|
@ -1533,21 +1533,21 @@ set optimizer_switch=default;
|
|||
set optimizer_switch='index_merge=off,index_merge_union=off,default';
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set optimizer_switch=default;
|
||||
select @@global.optimizer_switch;
|
||||
@@global.optimizer_switch
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
set @@global.optimizer_switch=default;
|
||||
select @@global.optimizer_switch;
|
||||
@@global.optimizer_switch
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
#
|
||||
# Check index_merge's @@optimizer_switch flags
|
||||
#
|
||||
select @@optimizer_switch;
|
||||
@@optimizer_switch
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
create table t0 (a int);
|
||||
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||
create table t1 (a int, b int, c int, filler char(100),
|
||||
|
@ -1624,7 +1624,7 @@ explain select * from t1 where a=10 and b=10;
|
|||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref a,b a 5 const 49 Using where
|
||||
No intersect if it is disabled:
|
||||
set optimizer_switch='default,index_merge_intersection=off';
|
||||
set optimizer_switch='default,index_merge_sort_intersection=off,index_merge_intersection=off';
|
||||
explain select * from t1 where a=10 and b=10;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 ref a,b a 5 const 49 Using where
|
||||
|
@ -1657,5 +1657,6 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||
set optimizer_switch=default;
|
||||
show variables like 'optimizer_switch';
|
||||
Variable_name Value
|
||||
optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on
|
||||
optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off
|
||||
drop table t0, t1;
|
||||
set optimizer_switch= @optimizer_switch_save;
|
||||
|
|
|
@ -1421,9 +1421,9 @@ DROP TABLE t1;
|
|||
#
|
||||
create table t1(a int, b tinytext);
|
||||
insert into t1 values (1,2),(3,2);
|
||||
set session sort_buffer_size= 30000;
|
||||
set session sort_buffer_size= 1000;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '30000'
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '1000'
|
||||
set session max_sort_length= 2180;
|
||||
select * from t1 order by b;
|
||||
ERROR HY001: Out of sort memory; increase server sort buffer size
|
||||
|
|
|
@ -38,6 +38,7 @@ SELECT COUNT(*) FROM CountryLanguage;
|
|||
COUNT(*)
|
||||
984
|
||||
CREATE INDEX Name ON City(Name);
|
||||
set session optimizer_switch='index_merge_sort_intersection=off';
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE (Population >= 100000 OR Name LIKE 'P%' OR Population < 100000);
|
||||
|
@ -1366,3 +1367,4 @@ f1 f2 f3 f4
|
|||
9 0 2 6
|
||||
SET SESSION optimizer_switch=DEFAULT;
|
||||
DROP TABLE t1;
|
||||
set session optimizer_switch='index_merge_sort_intersection=default';
|
||||
|
|
|
@ -39,6 +39,7 @@ SELECT COUNT(*) FROM CountryLanguage;
|
|||
COUNT(*)
|
||||
984
|
||||
CREATE INDEX Name ON City(Name);
|
||||
set session optimizer_switch='index_merge_sort_intersection=off';
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE (Population >= 100000 OR Name LIKE 'P%' OR Population < 100000);
|
||||
|
@ -1367,4 +1368,5 @@ f1 f2 f3 f4
|
|||
9 0 2 6
|
||||
SET SESSION optimizer_switch=DEFAULT;
|
||||
DROP TABLE t1;
|
||||
set session optimizer_switch='index_merge_sort_intersection=default';
|
||||
SET SESSION STORAGE_ENGINE=DEFAULT;
|
||||
|
|
|
@ -3667,8 +3667,6 @@ CREATE TABLE t1 (a int, b int auto_increment, PRIMARY KEY (b));
|
|||
CREATE TABLE t2 (x int auto_increment, y int, z int,
|
||||
PRIMARY KEY (x), FOREIGN KEY (y) REFERENCES t1 (b));
|
||||
SET SESSION sort_buffer_size = 32 * 1024;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '32768'
|
||||
SELECT SQL_NO_CACHE COUNT(*)
|
||||
FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c
|
||||
FROM t1) t;
|
||||
|
@ -4104,8 +4102,6 @@ INSERT INTO `t1` VALUES ('asdf','2007-02-08 01:11:26');
|
|||
INSERT INTO `t2` VALUES ('abcdefghijk');
|
||||
INSERT INTO `t2` VALUES ('asdf');
|
||||
SET session sort_buffer_size=8192;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '8192'
|
||||
SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.a ORDER BY t1.b LIMIT 1) AS d1 FROM t2;
|
||||
d1
|
||||
1
|
||||
|
|
|
@ -971,6 +971,7 @@ tmpdir #
|
|||
select * from information_schema.session_variables where variable_name like 'tmpdir';
|
||||
VARIABLE_NAME VARIABLE_VALUE
|
||||
TMPDIR #
|
||||
set sort_buffer_size=1024*8;
|
||||
select @@ssl_ca, @@ssl_capath, @@ssl_cert, @@ssl_cipher, @@ssl_key;
|
||||
@@ssl_ca @@ssl_capath @@ssl_cert @@ssl_cipher @@ssl_key
|
||||
# # # # #
|
||||
|
|
|
@ -679,8 +679,6 @@ INSERT INTO t1(b,c) SELECT b,c FROM t2;
|
|||
UPDATE t2 SET c='2007-01-03';
|
||||
INSERT INTO t1(b,c) SELECT b,c FROM t2;
|
||||
set @@sort_buffer_size=8192;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '8192'
|
||||
SELECT COUNT(*) FROM t1;
|
||||
COUNT(*)
|
||||
3072
|
||||
|
|
|
@ -679,8 +679,6 @@ INSERT INTO t1(b,c) SELECT b,c FROM t2;
|
|||
UPDATE t2 SET c='2007-01-03';
|
||||
INSERT INTO t1(b,c) SELECT b,c FROM t2;
|
||||
set @@sort_buffer_size=8192;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '8192'
|
||||
SELECT COUNT(*) FROM t1;
|
||||
COUNT(*)
|
||||
3072
|
||||
|
|
|
@ -3667,8 +3667,6 @@ CREATE TABLE t1 (a int, b int auto_increment, PRIMARY KEY (b));
|
|||
CREATE TABLE t2 (x int auto_increment, y int, z int,
|
||||
PRIMARY KEY (x), FOREIGN KEY (y) REFERENCES t1 (b));
|
||||
SET SESSION sort_buffer_size = 32 * 1024;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '32768'
|
||||
SELECT SQL_NO_CACHE COUNT(*)
|
||||
FROM (SELECT a, b, (SELECT x FROM t2 WHERE y=b ORDER BY z DESC LIMIT 1) c
|
||||
FROM t1) t;
|
||||
|
@ -4104,8 +4102,6 @@ INSERT INTO `t1` VALUES ('asdf','2007-02-08 01:11:26');
|
|||
INSERT INTO `t2` VALUES ('abcdefghijk');
|
||||
INSERT INTO `t2` VALUES ('asdf');
|
||||
SET session sort_buffer_size=8192;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect sort_buffer_size value: '8192'
|
||||
SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.a ORDER BY t1.b LIMIT 1) AS d1 FROM t2;
|
||||
d1
|
||||
1
|
||||
|
|
419
mysql-test/t/index_intersect.test
Normal file
419
mysql-test/t/index_intersect.test
Normal file
|
@ -0,0 +1,419 @@
|
|||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1,t2,t3,t4;
|
||||
DROP DATABASE IF EXISTS world;
|
||||
--enable_warnings
|
||||
|
||||
set names utf8;
|
||||
|
||||
CREATE DATABASE world;
|
||||
|
||||
use world;
|
||||
|
||||
--source include/world_schema.inc
|
||||
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--disable_warnings
|
||||
--source include/world.inc
|
||||
--enable_warnings
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
SELECT COUNT(*) FROM Country;
|
||||
SELECT COUNT(*) FROM City;
|
||||
SELECT COUNT(*) FROM CountryLanguage;
|
||||
|
||||
CREATE INDEX Name ON City(Name);
|
||||
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--disable_warnings
|
||||
ANALYZE TABLE City;
|
||||
--enable_warnings
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
|
||||
SET SESSION optimizer_switch='index_merge_sort_intersection=on';
|
||||
|
||||
SELECT COUNT(*) FROM City;
|
||||
|
||||
# The output of the next 6 queries tells us about selectivities
|
||||
# of the conditions utilized in 4 queries following after them
|
||||
|
||||
SELECT COUNT(*) FROM City WHERE Name LIKE 'C%';
|
||||
SELECT COUNT(*) FROM City WHERE Name LIKE 'M%';
|
||||
SELECT COUNT(*) FROM City WHERE Population > 1000000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 1500000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 300000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 5000000;
|
||||
|
||||
# The pattern of the WHERE condition used in the following 4 queries is
|
||||
# range(key1) AND range(key2)
|
||||
# Varying values of the constants in the conjuncts of the condition
|
||||
# we can get either an index intersection retrieval over key1 and key2
|
||||
# or a range index scan for one of these indexes
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'C%' AND Population > 1000000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'M%' AND Population > 1500000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'M%' AND Population > 300000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'M%' AND Population > 5000000;
|
||||
|
||||
|
||||
# The following 8 queries check that
|
||||
# the previous 4 plans are valid and return
|
||||
# the correct results when executed
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name LIKE 'C%' AND Population > 1000000;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'C%' AND Population > 1000000;
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name LIKE 'M%' AND Population > 1500000;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'M%' AND Population > 1500000;
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name LIKE 'M%' AND Population > 300000;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'M%' AND Population > 300000;
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name LIKE 'M%' AND Population > 5000000;
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name LIKE 'M%' AND Population > 5000000;
|
||||
|
||||
|
||||
# The output of the next 6 queries tells us about selectivities
|
||||
# of the conditions utilized in 3 queries following after them
|
||||
|
||||
SELECT COUNT(*) FROM City WHERE Name BETWEEN 'M' AND 'N';
|
||||
SELECT COUNT(*) FROM City WHERE Name BETWEEN 'G' AND 'J';
|
||||
SELECT COUNT(*) FROM City WHERE Population > 1000000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 500000;
|
||||
SELECT COUNT(*) FROM City WHERE Country LIKE 'C%';
|
||||
SELECT COUNT(*) FROM City WHERE Country LIKE 'B%';
|
||||
|
||||
|
||||
# The pattern of the WHERE condition used in the following 3 queries is
|
||||
# range(key1) AND range(key2) AND range(key3)
|
||||
# Varying values of the constants in the conjuncts of the condition
|
||||
# we can get index intersection over different pairs of keys:
|
||||
# over(key1,key2), over(key1,key3) and over(key2,key3)
|
||||
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'M' AND 'N' AND Population > 1000000 AND Country LIKE 'C%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 1000000 AND Country LIKE 'B%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
|
||||
|
||||
|
||||
# The following 6 queries check that
|
||||
# the previous 3 plans are valid and return
|
||||
# the correct results when executed
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name BETWEEN 'M' AND 'N' AND Population > 1000000 AND Country LIKE 'C%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'M' AND 'N' AND Population > 1000000 AND Country LIKE 'C%';
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 1000000 AND Country LIKE 'B%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 1000000 AND Country LIKE 'B%';
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
|
||||
|
||||
|
||||
# The output of the next 12 queries tells us about selectivities
|
||||
# of the conditions utilized in 5 queries following after them
|
||||
|
||||
SELECT COUNT(*) FROM City WHERE ID BETWEEN 501 AND 1000;
|
||||
SELECT COUNT(*) FROM City WHERE ID BETWEEN 1 AND 500;
|
||||
SELECT COUNT(*) FROM City WHERE ID BETWEEN 2001 AND 2500;
|
||||
SELECT COUNT(*) FROM City WHERE ID BETWEEN 3701 AND 4000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 700000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 1000000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 300000;
|
||||
SELECT COUNT(*) FROM City WHERE Population > 600000;
|
||||
SELECT COUNT(*) FROM City WHERE Country LIKE 'C%';
|
||||
SELECT COUNT(*) FROM City WHERE Country LIKE 'A%';
|
||||
SELECT COUNT(*) FROM City WHERE Country LIKE 'L%';
|
||||
SELECT COUNT(*) FROM City WHERE Country BETWEEN 'S' AND 'Z';
|
||||
|
||||
|
||||
# The pattern of the WHERE condition used in the following 5 queries is
|
||||
# range(key1) AND range(key2) AND range(key3)
|
||||
# with key1 happens to be a primary key (it matters only for InnoDB)
|
||||
# Varying values of the constants in the conjuncts of the condition
|
||||
# we can get index intersection either over all three keys, or over
|
||||
# different pairs, or a range scan over one of these keys.
|
||||
# Bear in mind that the condition (Country LIKE 'A%') is actually
|
||||
# equivalent to the condition (Country BETWEEN 'A' AND 'B') for the
|
||||
# tested instance the table City.
|
||||
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 1 AND 500 AND Population > 1000000 AND Country LIKE 'A%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'L%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3701 AND 4000 AND Population > 1000000
|
||||
AND Country BETWEEN 'S' AND 'Z';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000
|
||||
AND Country BETWEEN 'S' AND 'Z' ;
|
||||
|
||||
|
||||
# The following 10 queries check that
|
||||
# the previous 5 plans are valid and return
|
||||
# the correct results when executed
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 501 AND 1000 AND Population > 700000 AND Country LIKE 'C%';
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE ID BETWEEN 1 AND 500 AND Population > 1000000 AND Country LIKE 'A%';
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 1 AND 500 AND Population > 1000000 AND Country LIKE 'A%';
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'L%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 2001 AND 2500 AND Population > 300000 AND Country LIKE 'L%';
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE ID BETWEEN 3701 AND 4000 AND Population > 700000
|
||||
AND Country BETWEEN 'S' AND 'Z';
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3701 AND 4000 AND Population > 700000
|
||||
AND Country BETWEEN 'S' AND 'Z';
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000
|
||||
AND Country BETWEEN 'S' AND 'Z' ;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000
|
||||
AND Country BETWEEN 'S' AND 'Z' ;
|
||||
|
||||
|
||||
SET SESSION sort_buffer_size = 2048;
|
||||
|
||||
|
||||
# The following EXPLAIN command demonstrate that the execution plans
|
||||
# may be different if sort_buffer_size is set to a small value
|
||||
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'C%' AND Population > 1000000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'M%' AND Population > 1500000;
|
||||
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 1000000 AND Country LIKE 'B%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
|
||||
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 1 AND 500 AND Population > 1000000 AND Country LIKE 'A%';
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000
|
||||
AND Country BETWEEN 'S' AND 'Z';
|
||||
|
||||
|
||||
#Yet the query themselves return the correct results in this case as well
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'C%' AND Population > 1000000;
|
||||
|
||||
SELECT * FROM City WHERE
|
||||
Name LIKE 'M%' AND Population > 1500000;
|
||||
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 700000 AND Country LIKE 'B%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Name BETWEEN 'G' AND 'J' AND Population > 500000 AND Country LIKE 'C%';
|
||||
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 1 AND 500 AND Population > 1000000 AND Country LIKE 'A%';
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE ID BETWEEN 3001 AND 4000 AND Population > 600000
|
||||
AND Country BETWEEN 'S' AND 'Z';
|
||||
|
||||
|
||||
SET SESSION sort_buffer_size = default;
|
||||
|
||||
# Instead of the index on the column Country create two compound indexes
|
||||
# including this column as the first component
|
||||
|
||||
DROP INDEX Country ON City;
|
||||
|
||||
CREATE INDEX CountryID ON City(Country,ID);
|
||||
CREATE INDEX CountryName ON City(Country,Name);
|
||||
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
--disable_warnings
|
||||
ANALYZE TABLE City;
|
||||
--enable_warnings
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
# Check that the first component of a compound index can be used for
|
||||
# index intersection, even in the cases when we have a ref access
|
||||
# for this component
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Country LIKE 'M%' AND Population > 1000000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Country='CHN' AND Population > 1500000;
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM City
|
||||
WHERE Country='CHN' AND Population > 1500000 AND Name LIKE 'C%';
|
||||
|
||||
|
||||
# Check that the previous 3 plans return the right results when executed
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Country LIKE 'M%' AND Population > 1000000;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE Country LIKE 'M%' AND Population > 1000000;
|
||||
|
||||
--sorted_result
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Country='CHN' AND Population > 1500000;
|
||||
--sorted_result
|
||||
SELECT * FROM City
|
||||
WHERE Country='CHN' AND Population > 1500000;
|
||||
|
||||
|
||||
SELECT * FROM City USE INDEX ()
|
||||
WHERE Country='CHN' AND Population > 1500000 AND Name LIKE 'C%';
|
||||
|
||||
SELECT * FROM City
|
||||
WHERE Country='CHN' AND Population > 1500000 AND Name LIKE 'C%';
|
||||
|
||||
|
||||
DROP DATABASE world;
|
||||
|
||||
use test;
|
||||
|
||||
#
|
||||
# Bug #684086: crash with EXPLAIN in InnoDB for index intersection
|
||||
# of two indexes one of which is primary
|
||||
#
|
||||
|
||||
CREATE TABLE t1 (
|
||||
f1 int,
|
||||
f4 varchar(32),
|
||||
f5 int,
|
||||
PRIMARY KEY (f1),
|
||||
KEY (f4)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 VALUES
|
||||
(5,'H',1), (9,'g',0), (527,'i',0), (528,'y',1), (529,'S',6),
|
||||
(530,'m',7), (531,'b',2), (532,'N',1), (533,'V',NULL), (534,'l',1),
|
||||
(535,'M',0), (536,'w',1), (537,'j',5), (538,'l',0), (539,'n',2),
|
||||
(540,'m',2), (541,'r',2), (542,'l',2), (543,'h',3),(544,'o',0),
|
||||
(956,'h',0), (957,'g',0), (958,'W',5), (959,'s',3), (960,'w',0),
|
||||
(961,'q',0), (962,'e',NULL), (963,'u',7), (964,'q',1), (965,'N',NULL),
|
||||
(966,'e',0), (967,'t',3), (968,'e',6), (969,'f',NULL), (970,'j',0),
|
||||
(971,'s',3), (972,'I',0), (973,'h',4), (974,'g',1), (975,'s',0),
|
||||
(976,'r',3), (977,'x',1), (978,'v',8), (979,'j',NULL), (980,'z',7),
|
||||
(981,'t',9), (982,'j',5), (983,'u',NULL), (984,'g',6), (985,'w',1),
|
||||
(986,'h',1), (987,'v',0), (988,'v',0), (989,'c',2), (990,'b',7),
|
||||
(991,'z',0), (992,'M',1), (993,'u',2), (994,'r',2), (995,'b',4),
|
||||
(996,'A',2), (997,'u',0), (998,'a',0), (999,'j',2), (1,'I',2);
|
||||
|
||||
EXPLAIN
|
||||
SELECT * FROM t1
|
||||
WHERE (f1 < 535 OR f1 > 985) AND ( f4='r' OR f4 LIKE 'a%' ) ;
|
||||
|
||||
SELECT * FROM t1
|
||||
WHERE (f1 < 535 OR f1 > 985) AND ( f4='r' OR f4 LIKE 'a%' ) ;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
SET SESSION optimizer_switch='index_merge_sort_intersection=on';
|
7
mysql-test/t/index_intersect_innodb.test
Normal file
7
mysql-test/t/index_intersect_innodb.test
Normal file
|
@ -0,0 +1,7 @@
|
|||
--source include/have_innodb.inc
|
||||
|
||||
SET SESSION STORAGE_ENGINE='InnoDB';
|
||||
|
||||
--source t/index_intersect.test
|
||||
|
||||
SET SESSION STORAGE_ENGINE=DEFAULT;
|
|
@ -18,6 +18,11 @@ let $engine_type= InnoDB;
|
|||
# InnoDB does not support Merge tables (affects include/index_merge1.inc)
|
||||
let $merge_table_support= 0;
|
||||
|
||||
set @optimizer_switch_save= @@optimizer_switch;
|
||||
|
||||
set optimizer_switch='index_merge_sort_intersection=off';
|
||||
|
||||
|
||||
# The first two tests are disabled because of non deterministic explain output.
|
||||
# If include/index_merge1.inc can be enabled for InnoDB and all other
|
||||
# storage engines, please remove the subtest for Bug#21277 from
|
||||
|
@ -84,3 +89,5 @@ SELECT COUNT(*) FROM
|
|||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
set optimizer_switch= @optimizer_switch_save;
|
||||
|
|
|
@ -14,6 +14,10 @@ let $engine_type= MyISAM;
|
|||
# MyISAM supports Merge tables
|
||||
let $merge_table_support= 1;
|
||||
|
||||
set @optimizer_switch_save= @@optimizer_switch;
|
||||
|
||||
set optimizer_switch='index_merge_sort_intersection=off';
|
||||
|
||||
--source include/index_merge1.inc
|
||||
--source include/index_merge_ror.inc
|
||||
--source include/index_merge2.inc
|
||||
|
@ -164,7 +168,7 @@ set optimizer_switch='default,index_merge=off';
|
|||
explain select * from t1 where a=10 and b=10;
|
||||
|
||||
--echo No intersect if it is disabled:
|
||||
set optimizer_switch='default,index_merge_intersection=off';
|
||||
set optimizer_switch='default,index_merge_sort_intersection=off,index_merge_intersection=off';
|
||||
explain select * from t1 where a=10 and b=10;
|
||||
|
||||
--echo Do intersect when union was disabled
|
||||
|
@ -195,3 +199,5 @@ show variables like 'optimizer_switch';
|
|||
|
||||
drop table t0, t1;
|
||||
|
||||
set optimizer_switch= @optimizer_switch_save;
|
||||
|
||||
|
|
|
@ -843,7 +843,7 @@ DROP TABLE t1;
|
|||
--echo #
|
||||
create table t1(a int, b tinytext);
|
||||
insert into t1 values (1,2),(3,2);
|
||||
set session sort_buffer_size= 30000;
|
||||
set session sort_buffer_size= 1000;
|
||||
set session max_sort_length= 2180;
|
||||
--error 1038
|
||||
select * from t1 order by b;
|
||||
|
|
|
@ -33,6 +33,8 @@ ANALYZE TABLE City;
|
|||
--enable_result_log
|
||||
--enable_query_log
|
||||
|
||||
set session optimizer_switch='index_merge_sort_intersection=off';
|
||||
|
||||
# The following 4 queries are added for code coverage
|
||||
|
||||
#the exptected # of rows differ on 32-bit and 64-bit platforms for innodb
|
||||
|
@ -1007,3 +1009,6 @@ SELECT * FROM t1 FORCE KEY (PRIMARY,f3,f4)
|
|||
SET SESSION optimizer_switch=DEFAULT;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
#the following command must be the last one in the file
|
||||
set session optimizer_switch='index_merge_sort_intersection=default';
|
||||
|
|
|
@ -736,6 +736,7 @@ select * from information_schema.session_variables where variable_name like 'tmp
|
|||
# Bug #19606: make ssl settings available via SHOW VARIABLES and @@variables
|
||||
#
|
||||
# Don't actually output, since it depends on the system
|
||||
set sort_buffer_size=1024*8;
|
||||
--replace_column 1 # 2 # 3 # 4 # 5 #
|
||||
select @@ssl_ca, @@ssl_capath, @@ssl_cert, @@ssl_cipher, @@ssl_key;
|
||||
--replace_column 2 #
|
||||
|
|
|
@ -221,6 +221,8 @@ TREE_ELEMENT *tree_insert(TREE *tree, void *key, uint key_size,
|
|||
}
|
||||
if (element == &tree->null_element)
|
||||
{
|
||||
if (tree->flag & TREE_ONLY_DUPS)
|
||||
return((TREE_ELEMENT *) 1);
|
||||
uint alloc_size=sizeof(TREE_ELEMENT)+key_size+tree->size_of_element;
|
||||
tree->allocated+=alloc_size;
|
||||
|
||||
|
|
150
sql/filesort.cc
150
sql/filesort.cc
|
@ -50,10 +50,6 @@ static int write_keys(SORTPARAM *param,uchar * *sort_keys,
|
|||
uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
|
||||
static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
|
||||
static void register_used_fields(SORTPARAM *param);
|
||||
static int merge_index(SORTPARAM *param,uchar *sort_buffer,
|
||||
BUFFPEK *buffpek,
|
||||
uint maxbuffer,IO_CACHE *tempfile,
|
||||
IO_CACHE *outfile);
|
||||
static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
|
||||
FILESORT_INFO *table_sort);
|
||||
static uint suffix_length(ulong string_length);
|
||||
|
@ -145,8 +141,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
|
|||
/* filesort cannot handle zero-length records. */
|
||||
DBUG_ASSERT(param.sort_length);
|
||||
param.ref_length= table->file->ref_length;
|
||||
param.addon_field= 0;
|
||||
param.addon_length= 0;
|
||||
if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
|
||||
!table->fulltext_searched && !sort_positions)
|
||||
{
|
||||
|
@ -1200,8 +1194,11 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
QUEUE queue;
|
||||
qsort2_cmp cmp;
|
||||
void *first_cmp_arg;
|
||||
volatile THD::killed_state *killed= ¤t_thd->killed;
|
||||
element_count dupl_count;
|
||||
uchar *src;
|
||||
THD::killed_state not_killable;
|
||||
uchar *unique_buff= param->unique_buff;
|
||||
volatile THD::killed_state *killed= ¤t_thd->killed;
|
||||
DBUG_ENTER("merge_buffers");
|
||||
|
||||
status_var_increment(current_thd->status_var.filesort_merge_passes);
|
||||
|
@ -1216,7 +1213,13 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
rec_length= param->rec_length;
|
||||
res_length= param->res_length;
|
||||
sort_length= param->sort_length;
|
||||
offset= rec_length-res_length;
|
||||
uint dupl_count_ofs= rec_length-sizeof(element_count);
|
||||
uint min_dupl_count= param->min_dupl_count;
|
||||
bool check_dupl_count= flag && min_dupl_count;
|
||||
offset= (rec_length-
|
||||
(flag && min_dupl_count ? sizeof(dupl_count) : 0)-res_length);
|
||||
uint wr_len= flag ? res_length : rec_length;
|
||||
uint wr_offset= flag ? offset : 0;
|
||||
maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
|
||||
to_start_filepos= my_b_tell(to_file);
|
||||
strpos= sort_buffer;
|
||||
|
@ -1225,7 +1228,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
/* The following will fire if there is not enough space in sort_buffer */
|
||||
DBUG_ASSERT(maxcount!=0);
|
||||
|
||||
if (param->unique_buff)
|
||||
if (unique_buff)
|
||||
{
|
||||
cmp= param->compare;
|
||||
first_cmp_arg= (void *) ¶m->cmp_context;
|
||||
|
@ -1250,28 +1253,29 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
queue_insert(&queue, (uchar*) buffpek);
|
||||
}
|
||||
|
||||
if (param->unique_buff)
|
||||
if (unique_buff)
|
||||
{
|
||||
/*
|
||||
Called by Unique::get()
|
||||
Copy the first argument to param->unique_buff for unique removal.
|
||||
Copy the first argument to unique_buff for unique removal.
|
||||
Store it also in 'to_file'.
|
||||
|
||||
This is safe as we know that there is always more than one element
|
||||
in each block to merge (This is guaranteed by the Unique:: algorithm
|
||||
*/
|
||||
buffpek= (BUFFPEK*) queue_top(&queue);
|
||||
memcpy(param->unique_buff, buffpek->key, rec_length);
|
||||
if (my_b_write(to_file, (uchar*) buffpek->key, rec_length))
|
||||
{
|
||||
error=1; goto err; /* purecov: inspected */
|
||||
}
|
||||
memcpy(unique_buff, buffpek->key, rec_length);
|
||||
if (min_dupl_count)
|
||||
memcpy(&dupl_count, unique_buff+dupl_count_ofs,
|
||||
sizeof(dupl_count));
|
||||
buffpek->key+= rec_length;
|
||||
buffpek->mem_count--;
|
||||
if (!--max_rows)
|
||||
if (! --buffpek->mem_count)
|
||||
{
|
||||
error= 0; /* purecov: inspected */
|
||||
goto end; /* purecov: inspected */
|
||||
if (!(error= (int) read_to_buffer(from_file, buffpek,
|
||||
rec_length)))
|
||||
{
|
||||
VOID(queue_remove(&queue,0));
|
||||
reuse_freed_buff(&queue, buffpek, rec_length);
|
||||
}
|
||||
else if (error == -1)
|
||||
goto err; /* purecov: inspected */
|
||||
}
|
||||
queue_replaced(&queue); // Top element has been used
|
||||
}
|
||||
|
@ -1287,26 +1291,49 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
for (;;)
|
||||
{
|
||||
buffpek= (BUFFPEK*) queue_top(&queue);
|
||||
src= buffpek->key;
|
||||
if (cmp) // Remove duplicates
|
||||
{
|
||||
if (!(*cmp)(first_cmp_arg, &(param->unique_buff),
|
||||
if (!(*cmp)(first_cmp_arg, &unique_buff,
|
||||
(uchar**) &buffpek->key))
|
||||
goto skip_duplicate;
|
||||
memcpy(param->unique_buff, (uchar*) buffpek->key, rec_length);
|
||||
{
|
||||
if (min_dupl_count)
|
||||
{
|
||||
element_count cnt;
|
||||
memcpy(&cnt, (uchar *) buffpek->key+dupl_count_ofs, sizeof(cnt));
|
||||
dupl_count+= cnt;
|
||||
}
|
||||
goto skip_duplicate;
|
||||
}
|
||||
if (min_dupl_count)
|
||||
{
|
||||
memcpy(unique_buff+dupl_count_ofs, &dupl_count,
|
||||
sizeof(dupl_count));
|
||||
}
|
||||
src= unique_buff;
|
||||
}
|
||||
if (flag == 0)
|
||||
|
||||
/*
|
||||
Do not write into the output file if this is the final merge called
|
||||
for a Unique object used for intersection and dupl_count is less
|
||||
than min_dupl_count.
|
||||
If the Unique object is used to intersect N sets of unique elements
|
||||
then for any element:
|
||||
dupl_count >= N <=> the element is occurred in each of these N sets.
|
||||
*/
|
||||
if (!check_dupl_count || dupl_count >= min_dupl_count)
|
||||
{
|
||||
if (my_b_write(to_file,(uchar*) buffpek->key, rec_length))
|
||||
if (my_b_write(to_file, src+wr_offset, wr_len))
|
||||
{
|
||||
error=1; goto err; /* purecov: inspected */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_b_write(to_file, (uchar*) buffpek->key+offset, res_length))
|
||||
{
|
||||
error=1; goto err; /* purecov: inspected */
|
||||
}
|
||||
if (cmp)
|
||||
{
|
||||
memcpy(unique_buff, (uchar*) buffpek->key, rec_length);
|
||||
if (min_dupl_count)
|
||||
memcpy(&dupl_count, unique_buff+dupl_count_ofs,
|
||||
sizeof(dupl_count));
|
||||
}
|
||||
if (!--max_rows)
|
||||
{
|
||||
|
@ -1318,7 +1345,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
buffpek->key+= rec_length;
|
||||
if (! --buffpek->mem_count)
|
||||
{
|
||||
if (!(error= (int) read_to_buffer(from_file,buffpek,
|
||||
if (!(error= (int) read_to_buffer(from_file, buffpek,
|
||||
rec_length)))
|
||||
{
|
||||
VOID(queue_remove(&queue,0));
|
||||
|
@ -1341,11 +1368,35 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
*/
|
||||
if (cmp)
|
||||
{
|
||||
if (!(*cmp)(first_cmp_arg, &(param->unique_buff), (uchar**) &buffpek->key))
|
||||
if (!(*cmp)(first_cmp_arg, &unique_buff, (uchar**) &buffpek->key))
|
||||
{
|
||||
buffpek->key+= rec_length; // Remove duplicate
|
||||
if (min_dupl_count)
|
||||
{
|
||||
element_count cnt;
|
||||
memcpy(&cnt, (uchar *) buffpek->key+dupl_count_ofs, sizeof(cnt));
|
||||
dupl_count+= cnt;
|
||||
}
|
||||
buffpek->key+= rec_length;
|
||||
--buffpek->mem_count;
|
||||
}
|
||||
|
||||
if (min_dupl_count)
|
||||
memcpy(unique_buff+dupl_count_ofs, &dupl_count,
|
||||
sizeof(dupl_count));
|
||||
|
||||
if (!check_dupl_count || dupl_count >= min_dupl_count)
|
||||
{
|
||||
src= unique_buff;
|
||||
if (my_b_write(to_file, src+wr_offset, wr_len))
|
||||
{
|
||||
error=1; goto err; /* purecov: inspected */
|
||||
}
|
||||
if (!--max_rows)
|
||||
{
|
||||
error= 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
|
@ -1358,7 +1409,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
max_rows-= buffpek->mem_count;
|
||||
if (flag == 0)
|
||||
{
|
||||
if (my_b_write(to_file,(uchar*) buffpek->key,
|
||||
if (my_b_write(to_file, (uchar*) buffpek->key,
|
||||
(rec_length*buffpek->mem_count)))
|
||||
{
|
||||
error= 1; goto err; /* purecov: inspected */
|
||||
|
@ -1367,19 +1418,25 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
|
|||
else
|
||||
{
|
||||
register uchar *end;
|
||||
strpos= buffpek->key+offset;
|
||||
for (end= strpos+buffpek->mem_count*rec_length ;
|
||||
strpos != end ;
|
||||
strpos+= rec_length)
|
||||
{
|
||||
if (my_b_write(to_file, strpos, res_length))
|
||||
src= buffpek->key+offset;
|
||||
for (end= src+buffpek->mem_count*rec_length ;
|
||||
src != end ;
|
||||
src+= rec_length)
|
||||
{
|
||||
if (check_dupl_count)
|
||||
{
|
||||
memcpy((uchar *) &dupl_count, src+dupl_count_ofs, sizeof(dupl_count));
|
||||
if (dupl_count < min_dupl_count)
|
||||
continue;
|
||||
}
|
||||
if (my_b_write(to_file, src, wr_len))
|
||||
{
|
||||
error=1; goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((error=(int) read_to_buffer(from_file,buffpek, rec_length))
|
||||
while ((error=(int) read_to_buffer(from_file, buffpek, rec_length))
|
||||
!= -1 && error != 0);
|
||||
|
||||
end:
|
||||
|
@ -1393,7 +1450,7 @@ err:
|
|||
|
||||
/* Do a merge to output-file (save only positions) */
|
||||
|
||||
static int merge_index(SORTPARAM *param, uchar *sort_buffer,
|
||||
int merge_index(SORTPARAM *param, uchar *sort_buffer,
|
||||
BUFFPEK *buffpek, uint maxbuffer,
|
||||
IO_CACHE *tempfile, IO_CACHE *outfile)
|
||||
{
|
||||
|
@ -1699,3 +1756,4 @@ void change_double_for_sort(double nr,uchar *to)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -340,6 +340,9 @@ protected:
|
|||
*/
|
||||
#define TIME_FOR_COMPARE_ROWID (TIME_FOR_COMPARE*100)
|
||||
|
||||
/* cost1 is better that cost2 only if cost1 + COST_EPS < cost2 */
|
||||
#define COST_EPS 0.001
|
||||
|
||||
/*
|
||||
For sequential disk seeks the cost formula is:
|
||||
DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip
|
||||
|
@ -542,12 +545,13 @@ protected:
|
|||
#define OPTIMIZER_SWITCH_INDEX_MERGE_UNION 2
|
||||
#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION 4
|
||||
#define OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT 8
|
||||
#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT 16
|
||||
|
||||
#ifdef DBUG_OFF
|
||||
# define OPTIMIZER_SWITCH_LAST 16
|
||||
#else
|
||||
# define OPTIMIZER_SWITCH_TABLE_ELIMINATION 16
|
||||
# define OPTIMIZER_SWITCH_LAST 32
|
||||
#else
|
||||
# define OPTIMIZER_SWITCH_TABLE_ELIMINATION 32
|
||||
# define OPTIMIZER_SWITCH_LAST 64
|
||||
#endif
|
||||
|
||||
#ifdef DBUG_OFF
|
||||
|
@ -2233,6 +2237,8 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
|
|||
ha_rows max_rows, bool sort_positions,
|
||||
ha_rows *examined_rows);
|
||||
void filesort_free_buffers(TABLE *table, bool full);
|
||||
double get_merge_many_buffs_cost(uint *buffer, uint last_n_elems,
|
||||
int elem_size);
|
||||
void change_double_for_sort(double nr,uchar *to);
|
||||
double my_double_round(double value, longlong dec, bool dec_unsigned,
|
||||
bool truncate);
|
||||
|
|
|
@ -338,7 +338,7 @@ TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"",
|
|||
static const char *optimizer_switch_names[]=
|
||||
{
|
||||
"index_merge","index_merge_union","index_merge_sort_union",
|
||||
"index_merge_intersection",
|
||||
"index_merge_intersection","index_merge_sort_intersection",
|
||||
#ifndef DBUG_OFF
|
||||
"table_elimination",
|
||||
#endif
|
||||
|
@ -352,6 +352,7 @@ static const unsigned int optimizer_switch_names_len[]=
|
|||
sizeof("index_merge_union") - 1,
|
||||
sizeof("index_merge_sort_union") - 1,
|
||||
sizeof("index_merge_intersection") - 1,
|
||||
sizeof("index_merge_sort_intersection") - 1,
|
||||
#ifndef DBUG_OFF
|
||||
sizeof("table_elimination") - 1,
|
||||
#endif
|
||||
|
@ -431,7 +432,8 @@ static const char *sql_mode_str= "OFF";
|
|||
/* Text representation for OPTIMIZER_SWITCH_DEFAULT */
|
||||
static const char *optimizer_switch_str="index_merge=on,index_merge_union=on,"
|
||||
"index_merge_sort_union=on,"
|
||||
"index_merge_intersection=on"
|
||||
"index_merge_intersection=on,"
|
||||
"index_merge_sort_intersection=off"
|
||||
#ifndef DBUG_OFF
|
||||
",table_elimination=on";
|
||||
#else
|
||||
|
@ -7297,7 +7299,8 @@ thread is in the relay logs.",
|
|||
0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
|
||||
{"optimizer_switch", OPT_OPTIMIZER_SWITCH,
|
||||
"optimizer_switch=option=val[,option=val...], where option={index_merge, "
|
||||
"index_merge_union, index_merge_sort_union, index_merge_intersection"
|
||||
"index_merge_union, index_merge_sort_union, index_merge_intersection, "
|
||||
"index_merge_sort_intersection"
|
||||
#ifndef DBUG_OFF
|
||||
", table_elimination"
|
||||
#endif
|
||||
|
|
1297
sql/opt_range.cc
1297
sql/opt_range.cc
File diff suppressed because it is too large
Load diff
127
sql/opt_range.h
127
sql/opt_range.h
|
@ -274,12 +274,13 @@ public:
|
|||
|
||||
enum {
|
||||
QS_TYPE_RANGE = 0,
|
||||
QS_TYPE_INDEX_MERGE = 1,
|
||||
QS_TYPE_RANGE_DESC = 2,
|
||||
QS_TYPE_FULLTEXT = 3,
|
||||
QS_TYPE_ROR_INTERSECT = 4,
|
||||
QS_TYPE_ROR_UNION = 5,
|
||||
QS_TYPE_GROUP_MIN_MAX = 6
|
||||
QS_TYPE_INDEX_INTERSECT = 1,
|
||||
QS_TYPE_INDEX_MERGE = 2,
|
||||
QS_TYPE_RANGE_DESC = 3,
|
||||
QS_TYPE_FULLTEXT = 4,
|
||||
QS_TYPE_ROR_INTERSECT = 5,
|
||||
QS_TYPE_ROR_UNION = 6,
|
||||
QS_TYPE_GROUP_MIN_MAX = 7
|
||||
};
|
||||
|
||||
/* Get type of this quick select - one of the QS_TYPE_* values */
|
||||
|
@ -305,6 +306,10 @@ public:
|
|||
Save ROWID of last retrieved row in file->ref. This used in ROR-merging.
|
||||
*/
|
||||
virtual void save_last_pos(){};
|
||||
|
||||
void add_key_and_length(String *key_names,
|
||||
String *used_lengths,
|
||||
bool *first);
|
||||
|
||||
/*
|
||||
Append comma-separated list of keys this quick select uses to key_names;
|
||||
|
@ -314,13 +319,16 @@ public:
|
|||
virtual void add_keys_and_lengths(String *key_names,
|
||||
String *used_lengths)=0;
|
||||
|
||||
void add_key_name(String *str, bool *first);
|
||||
|
||||
/*
|
||||
Append text representation of quick select structure (what and how is
|
||||
merged) to str. The result is added to "Extra" field in EXPLAIN output.
|
||||
This function is implemented only by quick selects that merge other quick
|
||||
selects output and/or can produce output suitable for merging.
|
||||
*/
|
||||
virtual void add_info_string(String *str) {};
|
||||
virtual void add_info_string(String *str) {}
|
||||
|
||||
/*
|
||||
Return 1 if any index used by this quick select
|
||||
uses field which is marked in passed bitmap.
|
||||
|
@ -393,8 +401,19 @@ protected:
|
|||
friend QUICK_RANGE_SELECT *get_quick_select(PARAM*,uint idx,
|
||||
SEL_ARG *key_tree,
|
||||
MEM_ROOT *alloc);
|
||||
friend
|
||||
int read_keys_and_merge_scans(THD *thd, TABLE *head,
|
||||
List<QUICK_RANGE_SELECT> quick_selects,
|
||||
QUICK_RANGE_SELECT *pk_quick_select,
|
||||
READ_RECORD *read_record,
|
||||
bool intersection,
|
||||
key_map *filtered_scans,
|
||||
Unique **unique_ptr);
|
||||
|
||||
friend class QUICK_SELECT_DESC;
|
||||
friend class QUICK_INDEX_SORT_SELECT;
|
||||
friend class QUICK_INDEX_MERGE_SELECT;
|
||||
friend class QUICK_INDEX_INTERSECT_SELECT;
|
||||
friend class QUICK_ROR_INTERSECT_SELECT;
|
||||
friend class QUICK_GROUP_MIN_MAX_SELECT;
|
||||
|
||||
|
@ -448,37 +467,43 @@ public:
|
|||
|
||||
|
||||
/*
|
||||
QUICK_INDEX_MERGE_SELECT - index_merge access method quick select.
|
||||
QUICK_INDEX_SORT_SELECT is the base class for the common functionality of:
|
||||
- QUICK_INDEX_MERGE_SELECT, access based on multi-index merge/union
|
||||
- QUICK_INDEX_INTERSECT_SELECT, access based on multi-index intersection
|
||||
|
||||
|
||||
QUICK_INDEX_MERGE_SELECT uses
|
||||
QUICK_INDEX_SORT_SELECT uses
|
||||
* QUICK_RANGE_SELECTs to get rows
|
||||
* Unique class to remove duplicate rows
|
||||
* Unique class
|
||||
- to remove duplicate rows for QUICK_INDEX_MERGE_SELECT
|
||||
- to intersect rows for QUICK_INDEX_INTERSECT_SELECT
|
||||
|
||||
INDEX MERGE OPTIMIZER
|
||||
Current implementation doesn't detect all cases where index_merge could
|
||||
Current implementation doesn't detect all cases where index merge could
|
||||
be used, in particular:
|
||||
|
||||
* index_merge+'using index' is not supported
|
||||
|
||||
* If WHERE part contains complex nested AND and OR conditions, some ways
|
||||
to retrieve rows using index_merge will not be considered. The choice
|
||||
to retrieve rows using index merge will not be considered. The choice
|
||||
of read plan may depend on the order of conjuncts/disjuncts in WHERE
|
||||
part of the query, see comments near imerge_list_or_list and
|
||||
SEL_IMERGE::or_sel_tree_with_checks functions for details.
|
||||
|
||||
* There is no "index_merge_ref" method (but index_merge on non-first
|
||||
* There is no "index_merge_ref" method (but index merge on non-first
|
||||
table in join is possible with 'range checked for each record').
|
||||
|
||||
See comments around SEL_IMERGE class and test_quick_select for more
|
||||
details.
|
||||
|
||||
ROW RETRIEVAL ALGORITHM
|
||||
|
||||
index_merge uses Unique class for duplicates removal. index_merge takes
|
||||
advantage of Clustered Primary Key (CPK) if the table has one.
|
||||
The index_merge algorithm consists of two phases:
|
||||
index merge/intersection uses Unique class for duplicates removal.
|
||||
index merge/intersection takes advantage of Clustered Primary Key (CPK)
|
||||
if the table has one.
|
||||
The index merge/intersection algorithm consists of two phases:
|
||||
|
||||
Phase 1
|
||||
(implemented by a QUICK_INDEX_MERGE_SELECT::read_keys_and_merge call):
|
||||
|
||||
Phase 1 (implemented in QUICK_INDEX_MERGE_SELECT::prepare_unique):
|
||||
prepare()
|
||||
{
|
||||
activate 'index only';
|
||||
|
@ -492,32 +517,31 @@ public:
|
|||
deactivate 'index only';
|
||||
}
|
||||
|
||||
Phase 2 (implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next
|
||||
calls):
|
||||
Phase 2
|
||||
(implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next calls):
|
||||
|
||||
fetch()
|
||||
{
|
||||
retrieve all rows from row pointers stored in Unique;
|
||||
retrieve all rows from row pointers stored in Unique
|
||||
(merging/intersecting them);
|
||||
free Unique;
|
||||
retrieve all rows for CPK scan;
|
||||
if (! intersection)
|
||||
retrieve all rows for CPK scan;
|
||||
}
|
||||
*/
|
||||
|
||||
class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
|
||||
class QUICK_INDEX_SORT_SELECT : public QUICK_SELECT_I
|
||||
{
|
||||
protected:
|
||||
Unique *unique;
|
||||
public:
|
||||
QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
|
||||
~QUICK_INDEX_MERGE_SELECT();
|
||||
QUICK_INDEX_SORT_SELECT(THD *thd, TABLE *table);
|
||||
~QUICK_INDEX_SORT_SELECT();
|
||||
|
||||
int init();
|
||||
int reset(void);
|
||||
int get_next();
|
||||
bool reverse_sorted() { return false; }
|
||||
bool unique_key_range() { return false; }
|
||||
int get_type() { return QS_TYPE_INDEX_MERGE; }
|
||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||
void add_info_string(String *str);
|
||||
bool is_keys_used(const MY_BITMAP *fields);
|
||||
#ifndef DBUG_OFF
|
||||
void dbug_dump(int indent, bool verbose);
|
||||
|
@ -525,24 +549,57 @@ public:
|
|||
|
||||
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
|
||||
|
||||
/* range quick selects this index_merge read consists of */
|
||||
/* range quick selects this index merge/intersect consists of */
|
||||
List<QUICK_RANGE_SELECT> quick_selects;
|
||||
|
||||
/* quick select that uses clustered primary key (NULL if none) */
|
||||
QUICK_RANGE_SELECT* pk_quick_select;
|
||||
|
||||
/* true if this select is currently doing a clustered PK scan */
|
||||
bool doing_pk_scan;
|
||||
|
||||
MEM_ROOT alloc;
|
||||
THD *thd;
|
||||
int read_keys_and_merge();
|
||||
virtual int read_keys_and_merge()= 0;
|
||||
|
||||
/* used to get rows collected in Unique */
|
||||
READ_RECORD read_record;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class QUICK_INDEX_MERGE_SELECT : public QUICK_INDEX_SORT_SELECT
|
||||
{
|
||||
private:
|
||||
/* true if this select is currently doing a clustered PK scan */
|
||||
bool doing_pk_scan;
|
||||
protected:
|
||||
int read_keys_and_merge();
|
||||
|
||||
public:
|
||||
QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table)
|
||||
:QUICK_INDEX_SORT_SELECT(thd, table) {}
|
||||
|
||||
int get_next();
|
||||
int get_type() { return QS_TYPE_INDEX_MERGE; }
|
||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||
void add_info_string(String *str);
|
||||
};
|
||||
|
||||
class QUICK_INDEX_INTERSECT_SELECT : public QUICK_INDEX_SORT_SELECT
|
||||
{
|
||||
protected:
|
||||
int read_keys_and_merge();
|
||||
|
||||
public:
|
||||
QUICK_INDEX_INTERSECT_SELECT(THD *thd, TABLE *table)
|
||||
:QUICK_INDEX_SORT_SELECT(thd, table) {}
|
||||
|
||||
key_map filtered_scans;
|
||||
int get_next();
|
||||
int get_type() { return QS_TYPE_INDEX_INTERSECT; }
|
||||
void add_keys_and_lengths(String *key_names, String *used_lengths);
|
||||
void add_info_string(String *str);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Rowid-Ordered Retrieval (ROR) index intersection quick select.
|
||||
This quick select produces intersection of row sequences returned
|
||||
|
|
|
@ -2977,6 +2977,7 @@ class user_var_entry
|
|||
DTCollation collation;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Unique -- class for unique (removing of duplicates).
|
||||
Puts all values to the TREE. If the tree becomes too big,
|
||||
|
@ -2993,27 +2994,43 @@ class Unique :public Sql_alloc
|
|||
IO_CACHE file;
|
||||
TREE tree;
|
||||
uchar *record_pointers;
|
||||
ulong filtered_out_elems;
|
||||
bool flush();
|
||||
uint size;
|
||||
uint full_size;
|
||||
uint min_dupl_count; /* always 0 for unions, > 0 for intersections */
|
||||
|
||||
public:
|
||||
ulong elements;
|
||||
Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
|
||||
uint size_arg, ulonglong max_in_memory_size_arg);
|
||||
uint size_arg, ulonglong max_in_memory_size_arg,
|
||||
uint min_dupl_count_arg= 0);
|
||||
~Unique();
|
||||
ulong elements_in_tree() { return tree.elements_in_tree; }
|
||||
inline bool unique_add(void *ptr)
|
||||
{
|
||||
DBUG_ENTER("unique_add");
|
||||
DBUG_PRINT("info", ("tree %u - %lu", tree.elements_in_tree, max_elements));
|
||||
if (tree.elements_in_tree > max_elements && flush())
|
||||
if (!(tree.flag & TREE_ONLY_DUPS) &&
|
||||
tree.elements_in_tree >= max_elements && flush())
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(!tree_insert(&tree, ptr, 0, tree.custom_arg));
|
||||
}
|
||||
|
||||
bool is_in_memory() { return (my_b_tell(&file) == 0); }
|
||||
void close_for_expansion() { tree.flag= TREE_ONLY_DUPS; }
|
||||
|
||||
bool get(TABLE *table);
|
||||
|
||||
/* Cost of searching for an element in the tree */
|
||||
inline static double get_search_cost(uint tree_elems, uint compare_factor)
|
||||
{
|
||||
return log((double) tree_elems) / (compare_factor * M_LN2);
|
||||
}
|
||||
|
||||
static double get_use_cost(uint *buffer, uint nkeys, uint key_size,
|
||||
ulonglong max_in_memory_size);
|
||||
ulonglong max_in_memory_size, uint compare_factor,
|
||||
bool intersect_fl, bool *in_memory);
|
||||
inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size,
|
||||
ulonglong max_in_memory_size)
|
||||
{
|
||||
|
@ -3030,6 +3047,11 @@ public:
|
|||
|
||||
friend int unique_write_to_file(uchar* key, element_count count, Unique *unique);
|
||||
friend int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique);
|
||||
|
||||
friend int unique_write_to_file_with_count(uchar* key, element_count count,
|
||||
Unique *unique);
|
||||
friend int unique_intersect_write_to_ptrs(uchar* key, element_count count,
|
||||
Unique *unique);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -2688,6 +2688,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
|
|||
goto error;
|
||||
}
|
||||
table->quick_keys.clear_all();
|
||||
table->intersect_keys.clear_all();
|
||||
table->reginfo.join_tab=s;
|
||||
table->reginfo.not_exists_optimize=0;
|
||||
bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->s->keys);
|
||||
|
@ -6373,8 +6374,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
|||
used_tables|=current_map;
|
||||
|
||||
if (tab->type == JT_REF && tab->quick &&
|
||||
(uint) tab->ref.key == tab->quick->index &&
|
||||
tab->ref.key_length < tab->quick->max_used_key_length)
|
||||
(((uint) tab->ref.key == tab->quick->index &&
|
||||
tab->ref.key_length < tab->quick->max_used_key_length) ||
|
||||
tab->table->intersect_keys.is_set(tab->ref.key)))
|
||||
{
|
||||
/* Range uses longer key; Use this instead of ref on key */
|
||||
tab->type=JT_ALL;
|
||||
|
@ -10186,6 +10188,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
|||
table->quick_keys.init();
|
||||
table->covering_keys.init();
|
||||
table->merge_keys.init();
|
||||
table->intersect_keys.init();
|
||||
table->keys_in_use_for_query.init();
|
||||
|
||||
table->s= share;
|
||||
|
@ -13678,7 +13681,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
|||
by clustered PK values.
|
||||
*/
|
||||
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT)
|
||||
DBUG_RETURN(0);
|
||||
|
@ -14084,6 +14088,7 @@ check_reverse_order:
|
|||
QUICK_SELECT_DESC *tmp;
|
||||
int quick_type= select->quick->get_type();
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
|
@ -14256,6 +14261,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
|||
select->cleanup(); // filesort did select
|
||||
tab->select= 0;
|
||||
table->quick_keys.clear_all(); // as far as we cleanup select->quick
|
||||
table->intersect_keys.clear_all();
|
||||
table->sort.io_cache= tablesort_result_cache;
|
||||
}
|
||||
tab->select_cond=0;
|
||||
|
@ -16879,6 +16885,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||
{
|
||||
quick_type= tab->select->quick->get_type();
|
||||
if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
|
||||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
|
||||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
|
||||
(quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
|
||||
tab->type = JT_INDEX_MERGE;
|
||||
|
@ -17092,6 +17099,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
|
|||
{
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
|
||||
{
|
||||
extra.append(STRING_WITH_LEN("; Using "));
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef struct st_sort_param {
|
|||
uint addon_length; /* Length of added packed fields */
|
||||
uint res_length; /* Length of records in final sorted file/buffer */
|
||||
uint keys; /* Max keys / buffer */
|
||||
element_count min_dupl_count;
|
||||
ha_rows max_rows,examined_rows;
|
||||
TABLE *sort_form; /* For quicker make_sortkey */
|
||||
SORT_FIELD *local_sortorder;
|
||||
|
@ -80,4 +81,9 @@ int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
|
|||
IO_CACHE *to_file, uchar *sort_buffer,
|
||||
BUFFPEK *lastbuff,BUFFPEK *Fb,
|
||||
BUFFPEK *Tb,int flag);
|
||||
int merge_index(SORTPARAM *param, uchar *sort_buffer,
|
||||
BUFFPEK *buffpek, uint maxbuffer,
|
||||
IO_CACHE *tempfile, IO_CACHE *outfile);
|
||||
|
||||
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
|
||||
|
||||
|
|
|
@ -683,7 +683,7 @@ struct st_table {
|
|||
needed by the query without reading the row.
|
||||
*/
|
||||
key_map covering_keys;
|
||||
key_map quick_keys, merge_keys;
|
||||
key_map quick_keys, merge_keys,intersect_keys;
|
||||
/*
|
||||
A set of keys that can be used in the query that references this
|
||||
table.
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include "mysql_priv.h"
|
||||
#include "sql_sort.h"
|
||||
|
||||
|
||||
int unique_write_to_file(uchar* key, element_count count, Unique *unique)
|
||||
{
|
||||
/*
|
||||
|
@ -45,6 +44,12 @@ int unique_write_to_file(uchar* key, element_count count, Unique *unique)
|
|||
return my_b_write(&unique->file, key, unique->size) ? 1 : 0;
|
||||
}
|
||||
|
||||
int unique_write_to_file_with_count(uchar* key, element_count count, Unique *unique)
|
||||
{
|
||||
return my_b_write(&unique->file, key, unique->size) ||
|
||||
my_b_write(&unique->file, &count, sizeof(element_count)) ? 1 : 0;
|
||||
}
|
||||
|
||||
int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique)
|
||||
{
|
||||
memcpy(unique->record_pointers, key, unique->size);
|
||||
|
@ -52,10 +57,28 @@ int unique_write_to_ptrs(uchar* key, element_count count, Unique *unique)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int unique_intersect_write_to_ptrs(uchar* key, element_count count, Unique *unique)
|
||||
{
|
||||
if (count >= unique->min_dupl_count)
|
||||
{
|
||||
memcpy(unique->record_pointers, key, unique->size);
|
||||
unique->record_pointers+=unique->size;
|
||||
}
|
||||
else
|
||||
unique->filtered_out_elems++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
|
||||
uint size_arg, ulonglong max_in_memory_size_arg)
|
||||
uint size_arg, ulonglong max_in_memory_size_arg,
|
||||
uint min_dupl_count_arg)
|
||||
:max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0)
|
||||
{
|
||||
min_dupl_count= min_dupl_count_arg;
|
||||
full_size= size;
|
||||
if (min_dupl_count_arg)
|
||||
full_size+= sizeof(element_count);
|
||||
my_b_clear(&file);
|
||||
init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func, 0,
|
||||
NULL, comp_func_fixed_arg);
|
||||
|
@ -123,7 +146,8 @@ inline double log2_n_fact(double x)
|
|||
*/
|
||||
|
||||
static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
|
||||
uint *first, uint *last)
|
||||
uint *first, uint *last,
|
||||
uint compare_factor)
|
||||
{
|
||||
uint total_buf_elems= 0;
|
||||
for (uint *pbuf= first; pbuf <= last; pbuf++)
|
||||
|
@ -134,7 +158,7 @@ static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
|
|||
|
||||
/* Using log2(n)=log(n)/log(2) formula */
|
||||
return 2*((double)total_buf_elems*elem_size) / IO_SIZE +
|
||||
total_buf_elems*log((double) n_buffers) / (TIME_FOR_COMPARE_ROWID * M_LN2);
|
||||
total_buf_elems*log((double) n_buffers) / (compare_factor * M_LN2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,7 +191,8 @@ static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
|
|||
|
||||
static double get_merge_many_buffs_cost(uint *buffer,
|
||||
uint maxbuffer, uint max_n_elems,
|
||||
uint last_n_elems, int elem_size)
|
||||
uint last_n_elems, int elem_size,
|
||||
uint compare_factor)
|
||||
{
|
||||
register int i;
|
||||
double total_cost= 0.0;
|
||||
|
@ -194,19 +219,22 @@ static double get_merge_many_buffs_cost(uint *buffer,
|
|||
{
|
||||
total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
|
||||
buff_elems + i,
|
||||
buff_elems + i + MERGEBUFF-1);
|
||||
buff_elems + i + MERGEBUFF-1,
|
||||
compare_factor);
|
||||
lastbuff++;
|
||||
}
|
||||
total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
|
||||
buff_elems + i,
|
||||
buff_elems + maxbuffer);
|
||||
buff_elems + maxbuffer,
|
||||
compare_factor);
|
||||
maxbuffer= lastbuff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Simulate final merge_buff call. */
|
||||
total_cost += get_merge_buffers_cost(buff_elems, elem_size,
|
||||
buff_elems, buff_elems + maxbuffer);
|
||||
buff_elems, buff_elems + maxbuffer,
|
||||
compare_factor);
|
||||
return total_cost;
|
||||
}
|
||||
|
||||
|
@ -221,7 +249,11 @@ static double get_merge_many_buffs_cost(uint *buffer,
|
|||
to get # bytes needed.
|
||||
nkeys #of elements in Unique
|
||||
key_size size of each elements in bytes
|
||||
max_in_memory_size amount of memory Unique will be allowed to use
|
||||
max_in_memory_size amount of memory Unique will be allowed to use
|
||||
compare_factor used to calculate cost of one comparison
|
||||
write_fl if the result must be saved written to disk
|
||||
in_memory_elems OUT estimate of the number of elements in memory
|
||||
if disk is not used
|
||||
|
||||
RETURN
|
||||
Cost in disk seeks.
|
||||
|
@ -259,7 +291,9 @@ static double get_merge_many_buffs_cost(uint *buffer,
|
|||
*/
|
||||
|
||||
double Unique::get_use_cost(uint *buffer, uint nkeys, uint key_size,
|
||||
ulonglong max_in_memory_size)
|
||||
ulonglong max_in_memory_size,
|
||||
uint compare_factor,
|
||||
bool intersect_fl, bool *in_memory)
|
||||
{
|
||||
ulong max_elements_in_tree;
|
||||
ulong last_tree_elems;
|
||||
|
@ -276,12 +310,15 @@ double Unique::get_use_cost(uint *buffer, uint nkeys, uint key_size,
|
|||
result= 2*log2_n_fact(last_tree_elems + 1.0);
|
||||
if (n_full_trees)
|
||||
result+= n_full_trees * log2_n_fact(max_elements_in_tree + 1.0);
|
||||
result /= TIME_FOR_COMPARE_ROWID;
|
||||
result /= compare_factor;
|
||||
|
||||
DBUG_PRINT("info",("unique trees sizes: %u=%u*%lu + %lu", nkeys,
|
||||
n_full_trees, n_full_trees?max_elements_in_tree:0,
|
||||
last_tree_elems));
|
||||
|
||||
if (in_memory)
|
||||
*in_memory= !n_full_trees;
|
||||
|
||||
if (!n_full_trees)
|
||||
return result;
|
||||
|
||||
|
@ -295,12 +332,12 @@ double Unique::get_use_cost(uint *buffer, uint nkeys, uint key_size,
|
|||
result += DISK_SEEK_BASE_COST * ceil(((double) key_size)*last_tree_elems / IO_SIZE);
|
||||
|
||||
/* Cost of merge */
|
||||
if (intersect_fl)
|
||||
key_size+= sizeof(element_count);
|
||||
double merge_cost= get_merge_many_buffs_cost(buffer, n_full_trees,
|
||||
max_elements_in_tree,
|
||||
last_tree_elems, key_size);
|
||||
if (merge_cost < 0.0)
|
||||
return merge_cost;
|
||||
|
||||
last_tree_elems, key_size,
|
||||
compare_factor);
|
||||
result += merge_cost;
|
||||
/*
|
||||
Add cost of reading the resulting sequence, assuming there were no
|
||||
|
@ -327,7 +364,10 @@ bool Unique::flush()
|
|||
file_ptr.count=tree.elements_in_tree;
|
||||
file_ptr.file_pos=my_b_tell(&file);
|
||||
|
||||
if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
|
||||
tree_walk_action action= min_dupl_count ?
|
||||
(tree_walk_action) unique_write_to_file_with_count :
|
||||
(tree_walk_action) unique_write_to_file;
|
||||
if (tree_walk(&tree, action,
|
||||
(void*) this, left_root_right) ||
|
||||
insert_dynamic(&file_ptrs, (uchar*) &file_ptr))
|
||||
return 1;
|
||||
|
@ -357,6 +397,7 @@ Unique::reset()
|
|||
reinit_io_cache(&file, WRITE_CACHE, 0L, 0, 1);
|
||||
}
|
||||
elements= 0;
|
||||
tree.flag= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -576,15 +617,19 @@ bool Unique::get(TABLE *table)
|
|||
{
|
||||
SORTPARAM sort_param;
|
||||
table->sort.found_records=elements+tree.elements_in_tree;
|
||||
|
||||
if (my_b_tell(&file) == 0)
|
||||
{
|
||||
/* Whole tree is in memory; Don't use disk if you don't need to */
|
||||
if ((record_pointers=table->sort.record_pointers= (uchar*)
|
||||
my_malloc(size * tree.elements_in_tree, MYF(0))))
|
||||
{
|
||||
(void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs,
|
||||
tree_walk_action action= min_dupl_count ?
|
||||
(tree_walk_action) unique_intersect_write_to_ptrs :
|
||||
(tree_walk_action) unique_write_to_ptrs;
|
||||
filtered_out_elems= 0;
|
||||
(void) tree_walk(&tree, action,
|
||||
this, left_root_right);
|
||||
table->sort.found_records-= filtered_out_elems;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -614,7 +659,9 @@ bool Unique::get(TABLE *table)
|
|||
sort_param.max_rows= elements;
|
||||
sort_param.sort_form=table;
|
||||
sort_param.rec_length= sort_param.sort_length= sort_param.ref_length=
|
||||
size;
|
||||
full_size;
|
||||
sort_param.min_dupl_count= min_dupl_count;
|
||||
sort_param.res_length= 0;
|
||||
sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length);
|
||||
sort_param.not_killable=1;
|
||||
|
||||
|
@ -635,8 +682,9 @@ bool Unique::get(TABLE *table)
|
|||
if (flush_io_cache(&file) ||
|
||||
reinit_io_cache(&file,READ_CACHE,0L,0,0))
|
||||
goto err;
|
||||
if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr,
|
||||
file_ptr, file_ptr+maxbuffer,0))
|
||||
sort_param.res_length= sort_param.rec_length-
|
||||
(min_dupl_count ? sizeof(min_dupl_count) : 0);
|
||||
if (merge_index(&sort_param, sort_buffer, file_ptr, maxbuffer, &file, outfile))
|
||||
goto err;
|
||||
error=0;
|
||||
err:
|
||||
|
@ -651,3 +699,5 @@ err:
|
|||
outfile->end_of_file=save_pos;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
|
||||
|
||||
#define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD)
|
||||
#define MIN_SORT_MEMORY (32*1024-MALLOC_OVERHEAD)
|
||||
#define MIN_SORT_MEMORY (1024-MALLOC_OVERHEAD)
|
||||
|
||||
/* Memory allocated when parsing a statement / saving a statement */
|
||||
#define MEM_ROOT_BLOCK_SIZE 8192
|
||||
|
|
Loading…
Add table
Reference in a new issue