Commit a6b88f14 authored by Igor Babaev's avatar Igor Babaev

MDEV-415: Back-port of the WL task #1393 from the mysql-5.6 code line.

The task adds a more efficient handling of the queries with 
ORDER BY order LIMIT n, such that n is small enough and
no indexes are used for order.
parent 5a86a612
......@@ -712,6 +712,7 @@ extern int flush_write_cache(RECORD_CACHE *info);
extern void handle_recived_signals(void);
extern sig_handler my_set_alarm_variable(int signo);
extern my_bool radixsort_is_appliccable(uint n_items, size_t size_of_element);
extern void my_string_ptr_sort(uchar *base,uint items,size_t size);
extern void radixsort_for_str_ptr(uchar* base[], uint number_of_elements,
size_t size_of_element,uchar *buffer[]);
......
......@@ -44,6 +44,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql-common/client_plugin.c ../sql-common/mysql_async.c
../sql/password.c ../sql/discover.cc ../sql/derror.cc
../sql/field.cc ../sql/field_conv.cc
../sql/filesort_utils.cc
../sql/filesort.cc ../sql/gstream.cc ../sql/slave.cc
../sql/signal_handler.cc
../sql/handler.cc ../sql/hash_filo.cc ../sql/hostname.cc
......
......@@ -4,7 +4,7 @@ SET @old_debug= @@session.debug;
#
CREATE TABLE t1(f0 int auto_increment primary key, f1 int);
INSERT INTO t1(f1) VALUES (0),(1),(2),(3),(4),(5);
SET session debug_dbug= '+d,make_sort_keys_alloc_fail';
SET session debug_dbug= '+d,alloc_sort_buffer_fail';
CALL mtr.add_suppression("Out of sort memory");
SELECT * FROM t1 ORDER BY f1 ASC, f0;
ERROR HY001: Out of sort memory, consider increasing server sort buffer size
......
......@@ -2149,3 +2149,48 @@ f1 MIN(f2) MAX(f2)
4 00:25:00 00:25:00
DROP TABLE t1;
#End of test#49771
#
# Bug #58782
# Missing rows with SELECT .. WHERE .. IN subquery
# with full GROUP BY and no aggr
#
CREATE TABLE t1 (
pk INT NOT NULL,
col_int_nokey INT,
PRIMARY KEY (pk)
);
INSERT INTO t1 VALUES (10,7);
INSERT INTO t1 VALUES (11,1);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (13,3);
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2;
field1 field2
10 7
11 1
12 5
13 3
CREATE TABLE where_subselect
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2
;
SELECT *
FROM where_subselect
WHERE (field1, field2) IN (
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2
);
field1 field2
10 7
11 1
12 5
13 3
DROP TABLE t1;
DROP TABLE where_subselect;
# End of Bug #58782
This diff is collapsed.
CREATE TABLE t1(
f0 int auto_increment PRIMARY KEY,
f1 int,
f2 varchar(200)
);
INSERT INTO t1(f1, f2) VALUES
(0,"0"),(1,"1"),(2,"2"),(3,"3"),(4,"4"),(5,"5"),
(6,"6"),(7,"7"),(8,"8"),(9,"9"),(10,"10"),
(11,"11"),(12,"12"),(13,"13"),(14,"14"),(15,"15"),
(16,"16"),(17,"17"),(18,"18"),(19,"19"),(20,"20"),
(21,"21"),(22,"22"),(23,"23"),(24,"24"),(25,"25"),
(26,"26"),(27,"27"),(28,"28"),(29,"29"),(30,"30"),
(31,"31"),(32,"32"),(33,"33"),(34,"34"),(35,"35"),
(36,"36"),(37,"37"),(38,"38"),(39,"39"),(40,"40"),
(41,"41"),(42,"42"),(43,"43"),(44,"44"),(45,"45"),
(46,"46"),(47,"47"),(48,"48"),(49,"49"),(50,"50"),
(51,"51"),(52,"52"),(53,"53"),(54,"54"),(55,"55"),
(56,"56"),(57,"57"),(58,"58"),(59,"59"),(60,"60"),
(61,"61"),(62,"62"),(63,"63"),(64,"64"),(65,"65"),
(66,"66"),(67,"67"),(68,"68"),(69,"69"),(70,"70"),
(71,"71"),(72,"72"),(73,"73"),(74,"74"),(75,"75"),
(76,"76"),(77,"77"),(78,"78"),(79,"79"),(80,"80"),
(81,"81"),(82,"82"),(83,"83"),(84,"84"),(85,"85"),
(86,"86"),(87,"87"),(88,"88"),(89,"89"),(90,"90"),
(91,"91"),(92,"92"),(93,"93"),(94,"94"),(95,"95"),
(96,"96"),(97,"97"),(98,"98"),(99,"99");
CREATE TEMPORARY TABLE tmp (f1 int, f2 varchar(20));
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
set sort_buffer_size= 32768;
FLUSH STATUS;
SHOW SESSION STATUS LIKE 'Sort%';
Variable_name Value
Sort_merge_passes 0
Sort_range 0
Sort_rows 0
Sort_scan 0
SELECT * FROM t1 ORDER BY f2 LIMIT 100;
f0 f1 f2
1 0 0
101 0 0
201 0 0
301 0 0
401 0 0
501 0 0
601 0 0
701 0 0
801 0 0
901 0 0
1001 0 0
1101 0 0
1201 0 0
1301 0 0
1401 0 0
1501 0 0
1601 0 0
1701 0 0
1801 0 0
1901 0 0
2001 0 0
2101 0 0
2201 0 0
2301 0 0
2401 0 0
2501 0 0
2601 0 0
2701 0 0
2801 0 0
2901 0 0
3001 0 0
3101 0 0
3201 0 0
3301 0 0
3401 0 0
3501 0 0
3601 0 0
3701 0 0
3801 0 0
3901 0 0
4001 0 0
4101 0 0
4201 0 0
4301 0 0
4401 0 0
4501 0 0
4601 0 0
4701 0 0
4801 0 0
4901 0 0
5001 0 0
5101 0 0
5201 0 0
5301 0 0
5401 0 0
5501 0 0
5601 0 0
5701 0 0
5801 0 0
5901 0 0
6001 0 0
6101 0 0
6201 0 0
6301 0 0
6401 0 0
6501 0 0
6601 0 0
6701 0 0
6801 0 0
6901 0 0
7001 0 0
7101 0 0
7201 0 0
7301 0 0
7401 0 0
7501 0 0
7601 0 0
7701 0 0
7801 0 0
7901 0 0
8001 0 0
8101 0 0
8201 0 0
8301 0 0
8401 0 0
8501 0 0
8601 0 0
8701 0 0
8801 0 0
8901 0 0
9001 0 0
9101 0 0
9201 0 0
9301 0 0
9401 0 0
9501 0 0
9601 0 0
9701 0 0
9801 0 0
9901 0 0
SHOW SESSION STATUS LIKE 'Sort%';
Variable_name Value
Sort_merge_passes 0
Sort_range 0
Sort_rows 100
Sort_scan 1
DROP TABLE t1, tmp;
......@@ -248,6 +248,47 @@ NULL
drop procedure p1;
drop tables t1,t2,t3;
#
# Bug #58756
# Crash in heap_rrnd on query with HAVING ... IN (subquery) + LIMIT
#
CREATE TABLE t1 (
col_time_key time DEFAULT NULL,
col_datetime_key datetime DEFAULT NULL,
col_varchar_nokey varchar(1) DEFAULT NULL,
KEY col_time_key (col_time_key),
KEY col_datetime_key (col_datetime_key)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES ('17:53:30','2005-11-10 12:40:29','h');
INSERT INTO t1 VALUES ('11:35:49','2009-04-25 00:00:00','b');
INSERT INTO t1 VALUES (NULL,'2002-11-27 00:00:00','s');
INSERT INTO t1 VALUES ('06:01:40','2004-01-26 20:32:32','e');
INSERT INTO t1 VALUES ('05:45:11','2007-10-26 11:41:40','j');
INSERT INTO t1 VALUES ('00:00:00','2005-10-07 00:00:00','e');
INSERT INTO t1 VALUES ('00:00:00','2000-07-15 05:00:34','f');
INSERT INTO t1 VALUES ('06:11:01','2000-04-03 16:33:32','v');
INSERT INTO t1 VALUES ('13:02:46',NULL,'x');
INSERT INTO t1 VALUES ('21:44:25','2001-04-25 01:26:12','m');
INSERT INTO t1 VALUES ('22:43:58','2000-12-27 00:00:00','c');
CREATE TABLE t2 (
col_time_key time DEFAULT NULL,
col_datetime_key datetime DEFAULT NULL,
col_varchar_nokey varchar(1) DEFAULT NULL,
KEY col_time_key (col_time_key),
KEY col_datetime_key (col_datetime_key)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t2 VALUES ('11:28:45','2004-10-11 18:13:16','w');
SELECT col_time_key, col_datetime_key
FROM
( SELECT * FROM t1 ) AS table1
HAVING ( 'r' , 'e' ) IN
( SELECT col_varchar_nokey , col_varchar_nokey FROM t2 )
ORDER BY col_datetime_key
LIMIT 10;
col_time_key col_datetime_key
DROP TABLE t1;
DROP TABLE t2;
# End of Bug #58756
#
# Bug#60085 crash in Item::save_in_field() with time data type
#
CREATE TABLE t1(a date, b int, unique(b), unique(a), key(b)) engine=innodb;
......
......@@ -148,7 +148,7 @@ FROM t2 GROUP BY f1
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 SUBQUERY t1 system NULL NULL NULL NULL 1
3 SUBQUERY internal_tmp_table ALL group_key NULL NULL NULL 1 Using temporary; Using filesort
3 SUBQUERY internal_tmp_table ALL group_key NULL NULL NULL 2 Using temporary; Using filesort
drop table t1, t2, t3;
#
# LP BUG#715034 Item_sum_distinct::clear(): Assertion `tree != 0' failed
......
......@@ -11,7 +11,7 @@ SET @old_debug= @@session.debug;
CREATE TABLE t1(f0 int auto_increment primary key, f1 int);
INSERT INTO t1(f1) VALUES (0),(1),(2),(3),(4),(5);
SET session debug_dbug= '+d,make_sort_keys_alloc_fail';
SET session debug_dbug= '+d,alloc_sort_buffer_fail';
CALL mtr.add_suppression("Out of sort memory");
--error ER_OUT_OF_SORTMEMORY
SELECT * FROM t1 ORDER BY f1 ASC, f0;
......
......@@ -1493,3 +1493,52 @@ SELECT f1,MIN(f2),MAX(f2) FROM t1 GROUP BY 1;
DROP TABLE t1;
--echo #End of test#49771
--echo #
--echo # Bug #58782
--echo # Missing rows with SELECT .. WHERE .. IN subquery
--echo # with full GROUP BY and no aggr
--echo #
CREATE TABLE t1 (
pk INT NOT NULL,
col_int_nokey INT,
PRIMARY KEY (pk)
);
INSERT INTO t1 VALUES (10,7);
INSERT INTO t1 VALUES (11,1);
INSERT INTO t1 VALUES (12,5);
INSERT INTO t1 VALUES (13,3);
## original query:
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2;
## store query results in a new table:
CREATE TABLE where_subselect
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2
;
## query the new table and compare to original using WHERE ... IN():
SELECT *
FROM where_subselect
WHERE (field1, field2) IN (
SELECT pk AS field1, col_int_nokey AS field2
FROM t1
WHERE col_int_nokey > 0
GROUP BY field1, field2
);
DROP TABLE t1;
DROP TABLE where_subselect;
--echo # End of Bug #58782
......@@ -865,6 +865,7 @@ CALL mtr.add_suppression("Out of sort memory");
--error ER_OUT_OF_SORTMEMORY
select * from t1 order by b;
drop table t1;
set session sort_buffer_size= 30000;
--echo #
--echo # Bug #39844: Query Crash Mysql Server 5.0.67
......@@ -1391,7 +1392,205 @@ ORDER BY t2.c LIMIT 5;
DROP TABLE t1,t2,t3;
#
--echo #
--echo # WL#1393 - Optimizing filesort with small limit
--echo #
CREATE TABLE t1(f0 int auto_increment primary key, f1 int, f2 varchar(200));
INSERT INTO t1(f1, f2) VALUES
(0,"0"),(1,"1"),(2,"2"),(3,"3"),(4,"4"),(5,"5"),
(6,"6"),(7,"7"),(8,"8"),(9,"9"),(10,"10"),
(11,"11"),(12,"12"),(13,"13"),(14,"14"),(15,"15"),
(16,"16"),(17,"17"),(18,"18"),(19,"19"),(20,"20"),
(21,"21"),(22,"22"),(23,"23"),(24,"24"),(25,"25"),
(26,"26"),(27,"27"),(28,"28"),(29,"29"),(30,"30"),
(31,"31"),(32,"32"),(33,"33"),(34,"34"),(35,"35"),
(36,"36"),(37,"37"),(38,"38"),(39,"39"),(40,"40"),
(41,"41"),(42,"42"),(43,"43"),(44,"44"),(45,"45"),
(46,"46"),(47,"47"),(48,"48"),(49,"49"),(50,"50"),
(51,"51"),(52,"52"),(53,"53"),(54,"54"),(55,"55"),
(56,"56"),(57,"57"),(58,"58"),(59,"59"),(60,"60"),
(61,"61"),(62,"62"),(63,"63"),(64,"64"),(65,"65"),
(66,"66"),(67,"67"),(68,"68"),(69,"69"),(70,"70"),
(71,"71"),(72,"72"),(73,"73"),(74,"74"),(75,"75"),
(76,"76"),(77,"77"),(78,"78"),(79,"79"),(80,"80"),
(81,"81"),(82,"82"),(83,"83"),(84,"84"),(85,"85"),
(86,"86"),(87,"87"),(88,"88"),(89,"89"),(90,"90"),
(91,"91"),(92,"92"),(93,"93"),(94,"94"),(95,"95"),
(96,"96"),(97,"97"),(98,"98"),(99,"99");
################
## Test sort when source data fits in memory
SELECT * FROM t1 ORDER BY f1 ASC, f0 LIMIT 100;
SELECT * FROM t1 ORDER BY f1 ASC, f0 LIMIT 30;
SELECT * FROM t1 ORDER BY f1 ASC, f0 LIMIT 0;
SELECT * FROM t1 ORDER BY f2 DESC, f0 LIMIT 30;
SELECT * FROM t1 ORDER BY f2 DESC, f0 LIMIT 0;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 20;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 0;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 10 OFFSET 10;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 0 OFFSET 10;
################
## Test sort when source data does not fit in memory
set sort_buffer_size= 32768;
CREATE TEMPORARY TABLE tmp (f1 int, f2 varchar(20));
INSERT INTO tmp SELECT f1, f2 FROM t1;
INSERT INTO t1(f1, f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1, f2 FROM t1;
INSERT INTO t1(f1, f2) SELECT * FROM tmp;
SELECT * FROM t1 ORDER BY f1 ASC, f0 LIMIT 30;
SELECT * FROM t1 ORDER BY f1 ASC, f0 LIMIT 0;
SELECT * FROM t1 ORDER BY f2 DESC, f0 LIMIT 30;
SELECT * FROM t1 ORDER BY f2 DESC, f0 LIMIT 0;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 20;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 0;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 10 OFFSET 10;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 0 OFFSET 10;
################
## Test with SQL_CALC_FOUND_ROWS
set sort_buffer_size= 32768;
SELECT SQL_CALC_FOUND_ROWS * FROM t1
ORDER BY f1, f0 LIMIT 30;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1
ORDER BY f1, f0 LIMIT 0;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE f1>10
ORDER BY f2, f0 LIMIT 20;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE f1>10
ORDER BY f2, f0 LIMIT 0;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE f1>10
ORDER BY f2, f0 LIMIT 10 OFFSET 10;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE f1>10
ORDER BY f2, f0 LIMIT 0 OFFSET 10;
SELECT FOUND_ROWS();
################
## Test sorting with join
## These are re-written to use PQ during execution.
set sort_buffer_size= 327680;
SELECT * FROM t1 JOIN tmp on t1.f2=tmp.f2
ORDER BY tmp.f1, f0 LIMIT 30;
SELECT * FROM t1 JOIN tmp on t1.f2=tmp.f2
ORDER BY tmp.f1, f0 LIMIT 30 OFFSET 30;
SELECT SQL_CALC_FOUND_ROWS * FROM t1 JOIN tmp on t1.f2=tmp.f2
ORDER BY tmp.f1, f0 LIMIT 30 OFFSET 30;
SELECT FOUND_ROWS();
SELECT SQL_CALC_FOUND_ROWS * FROM t1 JOIN tmp on t1.f2=tmp.f2
WHERE t1.f2>20
ORDER BY tmp.f1, f0 LIMIT 30 OFFSET 30;
SELECT FOUND_ROWS();
################
## Test views
CREATE VIEW v1 as SELECT * FROM t1 ORDER BY f1, f0 LIMIT 30;
SELECT * FROM v1;
drop view v1;
CREATE VIEW v1 as SELECT * FROM t1 ORDER BY f1, f0 LIMIT 100;
SELECT * FROM v1 ORDER BY f2, f0 LIMIT 30;
CREATE VIEW v2 as SELECT * FROM t1 ORDER BY f2, f0 LIMIT 100;
SELECT * FROM v1 JOIN v2 on v1.f1=v2.f1 ORDER BY v1.f2,v1.f0,v2.f0
LIMIT 30;
################
## Test group & having
SELECT floor(f1/10) f3, count(f2) FROM t1
GROUP BY 1 ORDER BY 2,1 LIMIT 5;
SELECT floor(f1/10) f3, count(f2) FROM t1
GROUP BY 1 ORDER BY 2,1 LIMIT 0;
################
## Test SP
delimiter |;
CREATE PROCEDURE wl1393_sp_test()
BEGIN
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 30;
SELECT * FROM t1 WHERE f1>10 ORDER BY f2, f0 LIMIT 15 OFFSET 15;
SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE f1>10
ORDER BY f2, f0 LIMIT 15 OFFSET 15;
SELECT FOUND_ROWS();
SELECT * FROM v1 ORDER BY f2, f0 LIMIT 30;
END|
CALL wl1393_sp_test()|
DROP PROCEDURE wl1393_sp_test|
delimiter ;|
################
## Test with subqueries
SELECT d1.f1, d1.f2 FROM t1
LEFT JOIN (SELECT * FROM t1 ORDER BY f1 LIMIT 30) d1 on t1.f1=d1.f1
ORDER BY d1.f2 DESC LIMIT 30;
SELECT * FROM t1 WHERE f1 = (SELECT f1 FROM t1 ORDER BY 1 LIMIT 1);
--error ER_SUBQUERY_NO_1_ROW
SELECT * FROM t1 WHERE f1 = (SELECT f1 FROM t1 ORDER BY 1 LIMIT 2);
DROP TABLE t1, tmp;
DROP VIEW v1, v2;
--echo # end of WL#1393 - Optimizing filesort with small limit
--echo #
--echo # Bug #58761
--echo # Crash in Field::is_null in field.h on subquery in WHERE clause
--echo #
CREATE TABLE t1 (
pk INT NOT NULL AUTO_INCREMENT,
col_int_key INT DEFAULT NULL,
col_varchar_key VARCHAR(1) DEFAULT NULL,
PRIMARY KEY (pk),
KEY col_varchar_key (col_varchar_key,col_int_key)
);
INSERT INTO t1 VALUES (27,7,'x');
INSERT INTO t1 VALUES (28,6,'m');
INSERT INTO t1 VALUES (29,4,'c');
CREATE TABLE where_subselect
SELECT DISTINCT `pk` AS field1 , `pk` AS field2
FROM t1 AS alias1
WHERE alias1 . `col_int_key` > 229
OR alias1 . `col_varchar_key` IS NOT NULL
GROUP BY field1, field2
;
SELECT *
FROM where_subselect
WHERE (field1, field2) IN (
SELECT DISTINCT `pk` AS field1 , `pk` AS field2
FROM t1 AS alias1
WHERE alias1 . `col_int_key` > 229
OR alias1 . `col_varchar_key` IS NOT NULL
GROUP BY field1, field2
);
DROP TABLE t1;
DROP TABLE where_subselect;
--echo # End of Bug #58761
##
# Bug#35844: Covering index for ref access not compatible with ORDER BY list
#
......
#
# WL#1393 - ORDER BY with LIMIT tests
#
# A big test in a separate file, so that we can run the original
# order_by test with --debug without timeout.
#
# All the sort keys fit in memory, but the addon fields do not.
#
CREATE TABLE t1(
f0 int auto_increment PRIMARY KEY,
f1 int,
f2 varchar(200)
);
INSERT INTO t1(f1, f2) VALUES
(0,"0"),(1,"1"),(2,"2"),(3,"3"),(4,"4"),(5,"5"),
(6,"6"),(7,"7"),(8,"8"),(9,"9"),(10,"10"),
(11,"11"),(12,"12"),(13,"13"),(14,"14"),(15,"15"),
(16,"16"),(17,"17"),(18,"18"),(19,"19"),(20,"20"),
(21,"21"),(22,"22"),(23,"23"),(24,"24"),(25,"25"),
(26,"26"),(27,"27"),(28,"28"),(29,"29"),(30,"30"),
(31,"31"),(32,"32"),(33,"33"),(34,"34"),(35,"35"),
(36,"36"),(37,"37"),(38,"38"),(39,"39"),(40,"40"),
(41,"41"),(42,"42"),(43,"43"),(44,"44"),(45,"45"),
(46,"46"),(47,"47"),(48,"48"),(49,"49"),(50,"50"),
(51,"51"),(52,"52"),(53,"53"),(54,"54"),(55,"55"),
(56,"56"),(57,"57"),(58,"58"),(59,"59"),(60,"60"),
(61,"61"),(62,"62"),(63,"63"),(64,"64"),(65,"65"),
(66,"66"),(67,"67"),(68,"68"),(69,"69"),(70,"70"),
(71,"71"),(72,"72"),(73,"73"),(74,"74"),(75,"75"),
(76,"76"),(77,"77"),(78,"78"),(79,"79"),(80,"80"),
(81,"81"),(82,"82"),(83,"83"),(84,"84"),(85,"85"),
(86,"86"),(87,"87"),(88,"88"),(89,"89"),(90,"90"),
(91,"91"),(92,"92"),(93,"93"),(94,"94"),(95,"95"),
(96,"96"),(97,"97"),(98,"98"),(99,"99");
CREATE TEMPORARY TABLE tmp (f1 int, f2 varchar(20));
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
INSERT INTO tmp SELECT f1,f2 FROM t1;
INSERT INTO t1(f1,f2) SELECT * FROM tmp;
# Test when only sortkeys fits to memory
set sort_buffer_size= 32768;
FLUSH STATUS;
SHOW SESSION STATUS LIKE 'Sort%';
SELECT * FROM t1 ORDER BY f2 LIMIT 100;
SHOW SESSION STATUS LIKE 'Sort%';
DROP TABLE t1, tmp;
......@@ -243,6 +243,54 @@ call p1();
drop procedure p1;
drop tables t1,t2,t3;
--echo #
--echo # Bug #58756
--echo # Crash in heap_rrnd on query with HAVING ... IN (subquery) + LIMIT
--echo #
CREATE TABLE t1 (
col_time_key time DEFAULT NULL,
col_datetime_key datetime DEFAULT NULL,
col_varchar_nokey varchar(1) DEFAULT NULL,
KEY col_time_key (col_time_key),
KEY col_datetime_key (col_datetime_key)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES ('17:53:30','2005-11-10 12:40:29','h');
INSERT INTO t1 VALUES ('11:35:49','2009-04-25 00:00:00','b');
INSERT INTO t1 VALUES (NULL,'2002-11-27 00:00:00','s');
INSERT INTO t1 VALUES ('06:01:40','2004-01-26 20:32:32','e');
INSERT INTO t1 VALUES ('05:45:11','2007-10-26 11:41:40','j');
INSERT INTO t1 VALUES ('00:00:00','2005-10-07 00:00:00','e');
INSERT INTO t1 VALUES ('00:00:00','2000-07-15 05:00:34','f');
INSERT INTO t1 VALUES ('06:11:01','2000-04-03 16:33:32','v');
INSERT INTO t1 VALUES ('13:02:46',NULL,'x');
INSERT INTO t1 VALUES ('21:44:25','2001-04-25 01:26:12','m');
INSERT INTO t1 VALUES ('22:43:58','2000-12-27 00:00:00','c');
CREATE TABLE t2 (
col_time_key time DEFAULT NULL,
col_datetime_key datetime DEFAULT NULL,
col_varchar_nokey varchar(1) DEFAULT NULL,
KEY col_time_key (col_time_key),
KEY col_datetime_key (col_datetime_key)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t2 VALUES ('11:28:45','2004-10-11 18:13:16','w');
SELECT col_time_key, col_datetime_key
FROM
( SELECT * FROM t1 ) AS table1
HAVING ( 'r' , 'e' ) IN
( SELECT col_varchar_nokey , col_varchar_nokey FROM t2 )
ORDER BY col_datetime_key
LIMIT 10;
DROP TABLE t1;
DROP TABLE t2;
--echo # End of Bug #58756
--echo #
--echo # Bug#60085 crash in Item::save_in_field() with time data type
--echo #
......
......@@ -25,6 +25,11 @@
/* Radixsort */
my_bool radixsort_is_appliccable(uint n_items, size_t size_of_element)
{
return size_of_element <= 20 && n_items >= 1000 && n_items < 100000;
}
void radixsort_for_str_ptr(uchar **base, uint number_of_elements, size_t size_of_element, uchar **buffer)
{
uchar **end,**ptr,**buffer_ptr;
......
......@@ -23,7 +23,7 @@ void my_string_ptr_sort(uchar *base, uint items, size_t size)
#if INT_MAX > 65536L
uchar **ptr=0;
if (size <= 20 && items >= 1000 && items < 100000 &&
if (radixsort_is_appliccable(items, size) &&
(ptr= (uchar**) my_malloc(items*sizeof(char*),MYF(0))))
{
radixsort_for_str_ptr((uchar**) base,items,size,ptr);
......
......@@ -39,6 +39,7 @@ ENDIF()
SET (SQL_SOURCE
../sql-common/client.c derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
filesort_utils.cc
filesort.cc gstream.cc sha2.cc
signal_handler.cc
handler.cc hash_filo.h sql_plugin_services.h
......
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifndef BOUNDED_QUEUE_INCLUDED
#define BOUNDED_QUEUE_INCLUDED
#include <string.h>
#include "my_global.h"
#include "my_base.h"
#include "my_sys.h"
#include "queues.h"
class Sort_param;
/**
A priority queue with a fixed, limited size.
This is a wrapper on top of QUEUE and the queue_xxx() functions.
It keeps the top-N elements which are inserted.
Elements of type Element_type are pushed into the queue.
For each element, we call a user-supplied keymaker_function,
to generate a key of type Key_type for the element.
Instances of Key_type are compared with the user-supplied compare_function.
The underlying QUEUE implementation needs one extra element for replacing
the lowest/highest element when pushing into a full queue.
*/
template<typename Element_type, typename Key_type>
class Bounded_queue
{
public:
Bounded_queue()
{
memset(&m_queue, 0, sizeof(m_queue));
}
~Bounded_queue()
{
delete_queue(&m_queue);
}
/**
Function for making sort-key from input data.
@param param Sort parameters.
@param to Where to put the key.
@param from The input data.
*/
typedef void (*keymaker_function)(Sort_param *param,
Key_type *to,
Element_type *from);
/**
Function for comparing two keys.
@param n Pointer to number of bytes to compare.
@param a First key.
@param b Second key.
@retval -1, 0, or 1 depending on whether the left argument is
less than, equal to, or greater than the right argument.
*/
typedef int (*compare_function)(size_t *n, Key_type **a, Key_type **b);
/**
Initialize the queue.
@param max_elements The size of the queue.
@param max_at_top Set to true if you want biggest element on top.
false: We keep the n largest elements.
pop() will return the smallest key in the result set.
true: We keep the n smallest elements.
pop() will return the largest key in the result set.
@param compare Compare function for elements, takes 3 arguments.
If NULL, we use get_ptr_compare(compare_length).
@param compare_length Length of the data (i.e. the keys) used for sorting.
@param keymaker Function which generates keys for elements.
@param sort_param Sort parameters.
@param sort_keys Array of pointers to keys to sort.
@retval 0 OK, 1 Could not allocate memory.
We do *not* take ownership of any of the input pointer arguments.
*/
int init(ha_rows max_elements, bool max_at_top,
compare_function compare, size_t compare_length,
keymaker_function keymaker, Sort_param *sort_param,
Key_type **sort_keys);
/**
Pushes an element on the queue.
If the queue is already full, we discard one element.
Calls keymaker_function to generate a key for the element.
@param element The element to be pushed.
*/
void push(Element_type *element);
/**
Removes the top element from the queue.
@retval Pointer to the (key of the) removed element.
@note This function is for unit testing, where we push elements into to the
queue, and test that the appropriate keys are retained.
Interleaving of push() and pop() operations has not been tested.
*/
Key_type **pop()
{
// Don't return the extra element to the client code.
if (queue_is_full((&m_queue)))
queue_remove(&m_queue, 0);
DBUG_ASSERT(m_queue.elements > 0);
if (m_queue.elements == 0)
return NULL;
return reinterpret_cast<Key_type**>(queue_remove(&m_queue, 0));
}
/**
The number of elements in the queue.
*/
uint num_elements() const { return m_queue.elements; }
/**
Is the queue initialized?
*/
bool is_initialized() const { return m_queue.max_elements > 0; }
private:
Key_type **m_sort_keys;
size_t m_compare_length;
keymaker_function m_keymaker;
Sort_param *m_sort_param;
st_queue m_queue;
};
template<typename Element_type, typename Key_type>
int Bounded_queue<Element_type, Key_type>::init(ha_rows max_elements,
bool max_at_top,
compare_function compare,
size_t compare_length,
keymaker_function keymaker,
Sort_param *sort_param,
Key_type **sort_keys)
{
DBUG_ASSERT(sort_keys != NULL);
m_sort_keys= sort_keys;
m_compare_length= compare_length;
m_keymaker= keymaker;
m_sort_param= sort_param;
// init_queue() takes an uint, and also does (max_elements + 1)
if (max_elements >= (UINT_MAX - 1))
return 1;
if (compare == NULL)
compare=
reinterpret_cast<compare_function>(get_ptr_compare(compare_length));
// We allocate space for one extra element, for replace when queue is full.
return init_queue(&m_queue, (uint) max_elements + 1,
0, max_at_top,
reinterpret_cast<queue_compare>(compare),
&m_compare_length, 0, 0);
}
template<typename Element_type, typename Key_type>
void Bounded_queue<Element_type, Key_type>::push(Element_type *element)
{
DBUG_ASSERT(is_initialized());
if (queue_is_full((&m_queue)))
{
// Replace top element with new key, and re-order the queue.
Key_type **pq_top= reinterpret_cast<Key_type **>(queue_top(&m_queue));
(*m_keymaker)(m_sort_param, *pq_top, element);
queue_replace_top(&m_queue);
} else {
// Insert new key into the queue.
(*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements], element);
queue_insert(&m_queue,
reinterpret_cast<uchar*>(&m_sort_keys[m_queue.elements]));
}
}
#endif // BOUNDED_QUEUE_INCLUDED
This diff is collapsed.
......@@ -29,10 +29,8 @@ typedef struct st_sort_field SORT_FIELD;
ha_rows filesort(THD *thd, TABLE *table, st_sort_field *sortorder,
uint s_length, SQL_SELECT *select,
ha_rows max_rows, bool sort_positions,
ha_rows *examined_rows);
ha_rows *examined_rows, ha_rows *found_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);
#endif /* FILESORT_INCLUDED */
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "filesort_utils.h"
#include "sql_const.h"
#include "sql_sort.h"
#include "table.h"
#include "my_sys.h"
namespace {
/**
A local helper function. See comments for get_merge_buffers_cost().
*/
double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
{
return
2.0 * ((double) num_elements * elem_size) / IO_SIZE
+ (double) num_elements * log((double) num_buffers) /
(TIME_FOR_COMPARE_ROWID * M_LN2);
}
}
/**
This is a simplified, and faster version of @see get_merge_many_buffs_cost().
We calculate the cost of merging buffers, by simulating the actions
of @see merge_many_buff. For explanations of formulas below,
see comments for get_merge_buffers_cost().
TODO: Use this function for Unique::get_use_cost().
*/
double get_merge_many_buffs_cost_fast(ha_rows num_rows,
ha_rows num_keys_per_buffer,
uint elem_size)
{
ha_rows num_buffers= num_rows / num_keys_per_buffer;
ha_rows last_n_elems= num_rows % num_keys_per_buffer;
double total_cost;
// Calculate CPU cost of sorting buffers.
total_cost=
( num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) +
last_n_elems * log(1.0 + last_n_elems) )
/ TIME_FOR_COMPARE_ROWID;
// Simulate behavior of merge_many_buff().
while (num_buffers >= MERGEBUFF2)
{
// Calculate # of calls to merge_buffers().
const ha_rows loop_limit= num_buffers - MERGEBUFF*3/2;
const ha_rows num_merge_calls= 1 + loop_limit/MERGEBUFF;
const ha_rows num_remaining_buffs=
num_buffers - num_merge_calls * MERGEBUFF;
// Cost of merge sort 'num_merge_calls'.
total_cost+=
num_merge_calls *
get_merge_cost(num_keys_per_buffer * MERGEBUFF, MERGEBUFF, elem_size);
// # of records in remaining buffers.
last_n_elems+= num_remaining_buffs * num_keys_per_buffer;
// Cost of merge sort of remaining buffers.
total_cost+=
get_merge_cost(last_n_elems, 1 + num_remaining_buffs, elem_size);
num_buffers= num_merge_calls;
num_keys_per_buffer*= MERGEBUFF;
}
// Simulate final merge_buff call.
last_n_elems+= num_keys_per_buffer * num_buffers;
total_cost+= get_merge_cost(last_n_elems, 1 + num_buffers, elem_size);
return total_cost;
}
uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
{
DBUG_ENTER("alloc_sort_buffer");
DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
DBUG_SET("+d,simulate_out_of_memory"););
if (m_idx_array.is_null())
{
uchar **sort_keys=
(uchar**) my_malloc(num_records * (record_length + sizeof(uchar*)),
MYF(0));
m_idx_array= Idx_array(sort_keys, num_records);
m_record_length= record_length;
uchar **start_of_data= m_idx_array.array() + m_idx_array.size();
m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
}
else
{
DBUG_ASSERT(num_records == m_idx_array.size());
DBUG_ASSERT(record_length == m_record_length);
}
DBUG_RETURN(m_idx_array.array());
}
void Filesort_buffer::free_sort_buffer()
{
my_free(m_idx_array.array());
m_idx_array= Idx_array();
m_record_length= 0;
m_start_of_data= NULL;
}
void Filesort_buffer::sort_buffer(const Sort_param *param, uint count)
{
if (count <= 1)
return;
uchar **keys= get_sort_keys();
uchar **buffer= NULL;
if (radixsort_is_appliccable(count, param->sort_length) &&
(buffer= (uchar**) my_malloc(count*sizeof(char*), MYF(0))))
{
radixsort_for_str_ptr(keys, count, param->sort_length, buffer);
my_free(buffer);
return;
}
size_t size= param->sort_length;
my_qsort2(keys, count, sizeof(uchar*), get_ptr_compare(size), &size);
}
/* Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef FILESORT_UTILS_INCLUDED
#define FILESORT_UTILS_INCLUDED
#include "my_global.h"
#include "my_base.h"
#include "sql_array.h"
class Sort_param;
/*
Calculate cost of merge sort
@param num_rows Total number of rows.
@param num_keys_per_buffer Number of keys per buffer.
@param elem_size Size of each element.
Calculates cost of merge sort by simulating call to merge_many_buff().
@retval
Computed cost of merge sort in disk seeks.
@note
Declared here in order to be able to unit test it,
since library dependencies have not been sorted out yet.
See also comments get_merge_many_buffs_cost().
*/
double get_merge_many_buffs_cost_fast(ha_rows num_rows,
ha_rows num_keys_per_buffer,
uint elem_size);
/**
A wrapper class around the buffer used by filesort().
The buffer is a contiguous chunk of memory,
where the first part is <num_records> pointers to the actual data.
We wrap the buffer in order to be able to do lazy initialization of the
pointers: the buffer is often much larger than what we actually need.
The buffer must be kept available for multiple executions of the
same sort operation, so we have explicit allocate and free functions,
rather than doing alloc/free in CTOR/DTOR.
*/
class Filesort_buffer
{
public:
Filesort_buffer() :
m_idx_array(), m_record_length(0), m_start_of_data(NULL)
{}
/** Sort me... */
void sort_buffer(const Sort_param *param, uint count);
/// Initializes a record pointer.
uchar *get_record_buffer(uint idx)
{
m_idx_array[idx]= m_start_of_data + (idx * m_record_length);
return m_idx_array[idx];
}
/// Initializes all the record pointers.
void init_record_pointers()
{
for (uint ix= 0; ix < m_idx_array.size(); ++ix)
(void) get_record_buffer(ix);
}
/// Returns total size: pointer array + record buffers.
size_t sort_buffer_size() const
{
return m_idx_array.size() * (m_record_length + sizeof(uchar*));
}
/// Allocates the buffer, but does *not* initialize pointers.
uchar **alloc_sort_buffer(uint num_records, uint record_length);
/// Check <num_records, record_length> for the buffer
bool check_sort_buffer_properties(uint num_records, uint record_length)
{
return (static_cast<uint>(m_idx_array.size()) == num_records &&
m_record_length == m_record_length);
}
/// Frees the buffer.
void free_sort_buffer();
/// Getter, for calling routines which still use the uchar** interface.
uchar **get_sort_keys() { return m_idx_array.array(); }
/**
We need an assignment operator, see filesort().
This happens to have the same semantics as the one that would be
generated by the compiler. We still implement it here, to show shallow
assignment explicitly: we have two objects sharing the same array.
*/
Filesort_buffer &operator=(const Filesort_buffer &rhs)
{
m_idx_array= rhs.m_idx_array;
m_record_length= rhs.m_record_length;
m_start_of_data= rhs.m_start_of_data;
return *this;
}
private:
typedef Bounds_checked_array<uchar*> Idx_array;
Idx_array m_idx_array;
uint m_record_length;
uchar *m_start_of_data;
};
#endif // FILESORT_UTILS_INCLUDED
......@@ -19,6 +19,77 @@
#include <my_sys.h>
/**
A wrapper class which provides array bounds checking.
We do *not* own the array, we simply have a pointer to the first element,
and a length.
@remark
We want the compiler-generated versions of:
- the copy CTOR (memberwise initialization)
- the assignment operator (memberwise assignment)
@param Element_type The type of the elements of the container.
*/
template <typename Element_type> class Bounds_checked_array
{
public:
Bounds_checked_array() : m_array(NULL), m_size(0) {}
Bounds_checked_array(Element_type *el, size_t size)
: m_array(el), m_size(size)
{}
void reset() { m_array= NULL; m_size= 0; }
void reset(Element_type *array, size_t size)
{
m_array= array;
m_size= size;
}
/**
Set a new bound on the array. Does not resize the underlying
array, so the new size must be smaller than or equal to the
current size.
*/
void resize(size_t new_size)
{
DBUG_ASSERT(new_size <= m_size);
m_size= new_size;
}
Element_type &operator[](size_t n)
{
DBUG_ASSERT(n < m_size);
return m_array[n];
}
const Element_type &operator[](size_t n) const
{
DBUG_ASSERT(n < m_size);
return m_array[n];
}
size_t element_size() const { return sizeof(Element_type); }
size_t size() const { return m_size; }
bool is_null() const { return m_array == NULL; }
void pop_front()
{
DBUG_ASSERT(m_size > 0);
m_array+= 1;
m_size-= 1;
}
Element_type *array() const { return m_array; }
private:
Element_type *m_array;
size_t m_size;
};
/*
A typesafe wrapper around DYNAMIC_ARRAY
*/
......
......@@ -244,6 +244,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
ha_rows found_rows;
table->update_const_key_parts(conds);
order= simple_remove_const(order, conds);
......@@ -264,9 +265,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
MYF(MY_FAE | MY_ZEROFILL));
if (!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
select, HA_POS_ERROR, 1,
&examined_rows))
(table->sort.found_records= filesort(thd, table, sortorder, length,
select, HA_POS_ERROR,
true,
&examined_rows, &found_rows))
== HA_POS_ERROR)
{
delete select;
......
......@@ -1435,9 +1435,10 @@ JOIN::optimize()
We have found that grouping can be removed since groups correspond to
only one row anyway, but we still have to guarantee correct result
order. The line below effectively rewrites the query from GROUP BY
<fields> to ORDER BY <fields>. There are two exceptions:
<fields> to ORDER BY <fields>. There are three exceptions:
- if skip_sort_order is set (see above), then we can simply skip
GROUP BY;
- if we are in a subquery, we don't have to maintain order
- we can only rewrite ORDER BY if the ORDER BY fields are 'compatible'
with the GROUP BY ones, i.e. either one is a prefix of another.
We only check if the ORDER BY is a prefix of GROUP BY. In this case
......@@ -1447,7 +1448,13 @@ JOIN::optimize()
'order' as is.
*/
if (!order || test_if_subpart(group_list, order))
order= skip_sort_order ? 0 : group_list;
{
if (skip_sort_order ||
select_lex->master_unit()->item) // This is a subquery
order= NULL;
else
order= group_list;
}
/*
If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be
rewritten to IGNORE INDEX FOR ORDER BY(fields).
......@@ -1853,6 +1860,7 @@ int JOIN::init_execution()
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
DBUG_PRINT("info",("Sorting for order"));
thd_proc_info(thd, "Sorting for order");
if (create_sort_index(thd, this, order,
HA_POS_ERROR, HA_POS_ERROR, TRUE))
......@@ -2177,6 +2185,8 @@ JOIN::exec()
int tmp_error;
DBUG_ENTER("JOIN::exec");
const bool has_group_by= this->group;
thd_proc_info(thd, "executing");
error= 0;
if (procedure)
......@@ -2515,11 +2525,12 @@ JOIN::exec()
}
if (curr_join->group_list)
{
thd_proc_info(thd, "Creating sort index");
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
}
DBUG_PRINT("info",("Sorting for index"));
thd_proc_info(thd, "Creating sort index");
if (create_sort_index(thd, curr_join, curr_join->group_list,
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
make_group_fields(this, curr_join))
......@@ -2785,13 +2796,39 @@ JOIN::exec()
the query. XXX: it's never shown in EXPLAIN!
OPTION_FOUND_ROWS supersedes LIMIT and is taken into account.
*/
if (create_sort_index(thd, curr_join,
curr_join->group_list ?
curr_join->group_list : curr_join->order,
curr_join->select_limit,
(select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR : unit->select_limit_cnt),
curr_join->group_list ? TRUE : FALSE))
DBUG_PRINT("info",("Sorting for order by/group by"));
ORDER *order_arg=
curr_join->group_list ? curr_join->group_list : curr_join->order;
/*
filesort_limit: Return only this many rows from filesort().
We can use select_limit_cnt only if we have no group_by and 1 table.
This allows us to use Bounded_queue for queries like:
"select SQL_CALC_FOUND_ROWS * from t1 order by b desc limit 1;"
select_limit == HA_POS_ERROR (we need a full table scan)
unit->select_limit_cnt == 1 (we only need one row in the result set)
*/
const ha_rows filesort_limit_arg=
(has_group_by || curr_join->table_count > 1)
? curr_join->select_limit : unit->select_limit_cnt;
const ha_rows select_limit_arg=
select_options & OPTION_FOUND_ROWS
? HA_POS_ERROR : unit->select_limit_cnt;
DBUG_PRINT("info", ("has_group_by %d "
"curr_join->table_count %d "
"curr_join->m_select_limit %d "
"unit->select_limit_cnt %d",
has_group_by,
curr_join->table_count,
(int) curr_join->select_limit,
(int) unit->select_limit_cnt));
if (create_sort_index(thd,
curr_join,
order_arg,
filesort_limit_arg,
select_limit_arg,
curr_join->group_list ? FALSE : TRUE))
DBUG_VOID_RETURN;
sortorder= curr_join->sortorder;
if (curr_join->const_tables != curr_join->table_count &&
......@@ -2823,6 +2860,14 @@ JOIN::exec()
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
if (curr_join->order &&
curr_join->sortorder)
{
/* Use info provided by filesort. */
DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
JOIN_TAB *tab= curr_join->join_tab + curr_join->const_tables;
thd->limit_found_rows= tab->records;
}
/* Accumulate the counts from all join iterations of all join parts. */
thd->examined_row_count+= curr_join->examined_rows;
......@@ -17163,7 +17208,25 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((error= join->result->send_data(*join->fields)))
DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR);
}
if (++join->send_records >= join->unit->select_limit_cnt &&
++join->send_records;
if (join->send_records >= join->unit->select_limit_cnt &&
!join->do_send_rows)
{
/*
If filesort is used for sorting, stop after select_limit_cnt+1
records are read. Because of optimization in some cases it can
provide only select_limit_cnt+1 records.
*/
if (join->order &&
join->sortorder &&
join->select_options & OPTION_FOUND_ROWS)
{
DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
}
}
if (join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
......@@ -18848,6 +18911,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
uint length= 0;
ha_rows examined_rows;
ha_rows found_rows;
ha_rows filesort_retval= HA_POS_ERROR;
TABLE *table;
SQL_SELECT *select;
JOIN_TAB *tab;
......@@ -18925,10 +18990,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err;
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
table->sort.found_records=filesort(thd, table,join->sortorder, length,
select, filesort_limit, 0,
&examined_rows);
tab->records= table->sort.found_records; // For SQL_CALC_ROWS
filesort_retval= filesort(thd, table, join->sortorder, length,
select, filesort_limit, 0,
&examined_rows, &found_rows);
table->sort.found_records= filesort_retval;
tab->records= found_rows; // For SQL_CALC_ROWS
if (select)
{
/*
......@@ -18957,7 +19023,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tab->read_first_record= join_init_read_record;
tab->join->examined_rows+=examined_rows;
table->disable_keyread(); // Restore if we used indexes
DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
DBUG_RETURN(filesort_retval == HA_POS_ERROR);
err:
DBUG_RETURN(-1);
}
......@@ -19288,7 +19354,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
pos= sort= sortorder;
if (!pos)
return 0;
DBUG_RETURN(0);
for (;order;order=order->next,pos++)
{
......@@ -19305,6 +19371,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
else
pos->item= *order->item;
pos->reverse=! order->asc;
DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
*length=count;
DBUG_RETURN(sort);
......
......@@ -16,6 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "m_string.h" /* memset */
#include "my_global.h" /* uchar */
#include "my_base.h" /* ha_rows */
#include "my_sys.h" /* qsort2_cmp */
......@@ -27,7 +28,6 @@ typedef struct st_sort_field SORT_FIELD;
class Field;
struct TABLE;
/* Defines used by filesort and uniques */
#define MERGEBUFF 7
......@@ -65,41 +65,51 @@ struct BUFFPEK_COMPARE_CONTEXT
void *key_compare_arg;
};
typedef struct st_sort_param {
uint rec_length; /* Length of sorted records */
uint sort_length; /* Length of sorted columns */
uint ref_length; /* Length of record ref. */
uint addon_length; /* Length of added packed fields */
uint res_length; /* Length of records in final sorted file/buffer */
uint keys; /* Max keys / buffer */
class Sort_param {
public:
uint rec_length; // Length of sorted records.
uint sort_length; // Length of sorted columns.
uint ref_length; // Length of record ref.
uint addon_length; // Length of added packed fields.
uint res_length; // Length of records in final sorted file/buffer.
uint max_keys_per_buffer; // Max keys / buffer.
uint min_dupl_count;
ha_rows max_rows,examined_rows;
TABLE *sort_form; /* For quicker make_sortkey */
ha_rows max_rows; // Select limit, or HA_POS_ERROR if unlimited.
ha_rows examined_rows; // Number of examined rows.
TABLE *sort_form; // For quicker make_sortkey.
SORT_FIELD *local_sortorder;
SORT_FIELD *end;
SORT_ADDON_FIELD *addon_field; /* Descriptors for companion fields */
SORT_ADDON_FIELD *addon_field; // Descriptors for companion fields.
uchar *unique_buff;
bool not_killable;
char* tmp_buffer;
/* The fields below are used only by Unique class */
// The fields below are used only by Unique class.
qsort2_cmp compare;
BUFFPEK_COMPARE_CONTEXT cmp_context;
} SORTPARAM;
Sort_param()
{
memset(this, 0, sizeof(*this));
}
void init_for_filesort(uint sortlen, TABLE *table,
ulong max_length_for_sort_data,
ha_rows maxrows, bool sort_positions);
};
int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
int merge_many_buff(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek,
uint *maxbuffer, IO_CACHE *t_file);
uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
uint sort_length);
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,
int merge_buffers(Sort_param *param,IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff,BUFFPEK *Fb,
BUFFPEK *Tb,int flag);
int merge_index(Sort_param *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);
#endif /* SQL_SORT_INCLUDED */
......@@ -7201,6 +7201,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Item> fields;
List<Item> all_fields;
ha_rows examined_rows;
ha_rows found_rows;
bool auto_increment_field_copied= 0;
ulonglong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id, time_to_report_progress;
......@@ -7284,8 +7285,9 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
&tables, fields, all_fields, order) ||
!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(from->sort.found_records= filesort(thd, from, sortorder, length,
(SQL_SELECT *) 0, HA_POS_ERROR,
1, &examined_rows)) ==
NULL, HA_POS_ERROR,
true,
&examined_rows, &found_rows)) ==
HA_POS_ERROR)
goto err;
}
......
......@@ -498,13 +498,15 @@ int mysql_update(THD *thd,
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
ha_rows found_rows;
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records= filesort(thd, table, sortorder, length,
select, limit, 1,
&examined_rows))
select, limit,
true,
&examined_rows, &found_rows))
== HA_POS_ERROR)
{
goto err;
......
......@@ -29,6 +29,7 @@
#include "handler.h" /* row_type, ha_choice, handler */
#include "mysql_com.h" /* enum_field_types */
#include "thr_lock.h" /* thr_lock_type */
#include "filesort_utils.h"
/* Structs that defines the TABLE */
......@@ -299,11 +300,14 @@ enum tmp_table_type
};
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
typedef struct st_filesort_info
class Filesort_info
{
/// Buffer for sorting keys.
Filesort_buffer filesort_buffer;
public:
IO_CACHE *io_cache; /* If sorted through filesort */
uchar **sort_keys; /* Buffer for sorting keys */
uint keys; /* Number of key pointers in buffer */
uchar *buffpek; /* Buffer for buffpek structures */
uint buffpek_len; /* Max number of buffpeks in the buffer */
uchar *addon_buf; /* Pointer to a buffer if sorted with fields */
......@@ -312,7 +316,38 @@ typedef struct st_filesort_info
void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); /* To unpack back */
uchar *record_pointers; /* If sorted in memory */
ha_rows found_records; /* How many records in sort */
} FILESORT_INFO;
/** Sort filesort_buffer */
void sort_buffer(Sort_param *param, uint count)
{ filesort_buffer.sort_buffer(param, count); }
/**
Accessors for Filesort_buffer (which @c).
*/
uchar *get_record_buffer(uint idx)
{ return filesort_buffer.get_record_buffer(idx); }
uchar **get_sort_keys()
{ return filesort_buffer.get_sort_keys(); }
uchar **alloc_sort_buffer(uint num_records, uint record_length)
{ return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
bool check_sort_buffer_properties(uint num_records, uint record_length)
{
return filesort_buffer.check_sort_buffer_properties(num_records,
record_length);
}
void free_sort_buffer()
{ filesort_buffer.free_sort_buffer(); }
void init_record_pointers()
{ filesort_buffer.init_record_pointers(); }
size_t sort_buffer_size() const
{ return filesort_buffer.sort_buffer_size(); }
};
/*
......@@ -1136,7 +1171,7 @@ public:
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
FILESORT_INFO sort;
Filesort_info sort;
/*
The arena which the items for expressions from the table definition
are associated with.
......
......@@ -620,7 +620,6 @@ bool Unique::walk(tree_walk_action action, void *walk_action_arg)
bool Unique::get(TABLE *table)
{
SORTPARAM sort_param;
table->sort.found_records=elements+tree.elements_in_tree;
if (my_b_tell(&file) == 0)
{
......@@ -660,6 +659,7 @@ bool Unique::get(TABLE *table)
return 1;
reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);
Sort_param sort_param;
bzero((char*) &sort_param,sizeof(sort_param));
sort_param.max_rows= elements;
sort_param.sort_form=table;
......@@ -667,14 +667,15 @@ bool Unique::get(TABLE *table)
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.max_keys_per_buffer=
(uint) (max_in_memory_size / sort_param.sort_length);
sort_param.not_killable=1;
if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) *
if (!(sort_buffer=(uchar*) my_malloc((sort_param.max_keys_per_buffer+1) *
sort_param.sort_length,
MYF(0))))
return 1;
sort_param.unique_buff= sort_buffer+(sort_param.keys*
sort_param.unique_buff= sort_buffer+(sort_param.max_keys_per_buffer *
sort_param.sort_length);
sort_param.compare= (qsort2_cmp) buffpek_compare;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment