summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVarun Gupta <varun.gupta@mariadb.com>2019-06-04 13:43:14 +0530
committerVarun Gupta <varun.gupta@mariadb.com>2019-10-04 02:02:55 +0530
commitc01fe5a58bf6c1133e122ebc30f25aa2be4d91fb (patch)
tree26f5f8e378da4d7fcbb78b11e4e910303304e874
parentc2d8db66be4e9a564fd3d199234a38e03035cb85 (diff)
downloadmariadb-git-c01fe5a58bf6c1133e122ebc30f25aa2be4d91fb.tar.gz
ORDER BY LIMIT
-rw-r--r--mysql-test/main/mysqld--help.result2
-rw-r--r--mysql-test/main/sort_nest.result2123
-rw-r--r--mysql-test/main/sort_nest.test670
-rw-r--r--mysql-test/main/sort_nest_dbt3.result610
-rw-r--r--mysql-test/main/sort_nest_dbt3.test216
-rw-r--r--mysql-test/main/sort_nest_dbt3_innodb.result611
-rw-r--r--mysql-test/main/sort_nest_dbt3_innodb.test6
-rw-r--r--mysql-test/main/sort_nest_index.result342
-rw-r--r--mysql-test/main/sort_nest_index.test249
-rw-r--r--mysql-test/main/sort_nest_sj.result697
-rw-r--r--mysql-test/main/sort_nest_sj.test201
-rw-r--r--mysql-test/main/sort_nest_subselect.test99
-rw-r--r--sql/CMakeLists.txt1
-rw-r--r--sql/item.cc248
-rw-r--r--sql/item.h81
-rw-r--r--sql/item_cmpfunc.cc49
-rw-r--r--sql/item_cmpfunc.h13
-rw-r--r--sql/item_func.cc11
-rw-r--r--sql/item_func.h10
-rw-r--r--sql/item_row.cc5
-rw-r--r--sql/item_row.h7
-rw-r--r--sql/item_subselect.cc74
-rw-r--r--sql/item_subselect.h3
-rw-r--r--sql/opt_split.cc6
-rw-r--r--sql/opt_subselect.cc93
-rw-r--r--sql/opt_subselect.h2
-rw-r--r--sql/opt_trace.cc43
-rw-r--r--sql/opt_trace.h2
-rw-r--r--sql/sql_class.h1
-rw-r--r--sql/sql_const.h8
-rw-r--r--sql/sql_derived.cc7
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc198
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_select.cc998
-rw-r--r--sql/sql_select.h231
-rw-r--r--sql/sql_sort_nest.cc1596
-rw-r--r--sql/sql_tvc.cc7
-rw-r--r--sql/sys_vars.cc6
-rw-r--r--sql/table.h2
40 files changed, 9061 insertions, 472 deletions
diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result
index c55f535cd35..a9963b17b14 100644
--- a/mysql-test/main/mysqld--help.result
+++ b/mysql-test/main/mysqld--help.result
@@ -1401,6 +1401,7 @@ The following specify which files/extra groups are read (specified before remain
Prohibit update of a VIEW, which does not contain a key
of the underlying table and the query uses a LIMIT clause
(usually get from GUI tools)
+ --use-sort-nest Enable the sort nest
--use-stat-tables=name
Specifies how to use system statistics tables. One of:
NEVER, COMPLEMENTARY, PREFERABLY,
@@ -1780,6 +1781,7 @@ transaction-isolation REPEATABLE-READ
transaction-prealloc-size 4096
transaction-read-only FALSE
updatable-views-with-limit YES
+use-sort-nest FALSE
use-stat-tables PREFERABLY_FOR_QUERIES
userstat FALSE
verbose TRUE
diff --git a/mysql-test/main/sort_nest.result b/mysql-test/main/sort_nest.result
new file mode 100644
index 00000000000..11bfd8ad1db
--- /dev/null
+++ b/mysql-test/main/sort_nest.result
@@ -0,0 +1,2123 @@
+set use_sort_nest=1;
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_100;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+#
+# sort nest on (t2,t1)
+# ref(sort-nest.b) access on table t3
+#
+EXPLAIN SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t2.b=t3.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5
+1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 SIMPLE t3 ref a a 5 sort-nest.b 1 Using index
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t2.b=t3.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.a = t1.a"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.b desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["sort-nest.b"],
+ "rows": 1,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+}
+SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t2.b=t3.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+a b b a
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+DROP TABLE t0,t1,t2,t3;
+CREATE TABLE t1(a int, b int);
+INSERT INTO t1 SELECT seq-1, seq-1 from seq_1_to_100;
+CREATE TABLE t2(a int, b int);
+INSERT INTO t2(a,b) VALUES (1,1), (2,2);
+INSERT INTO t2 SELECT seq-1, seq-1 from seq_1_to_100;
+CREATE TABLE t3(a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+CREATE FUNCTION f1(a int) RETURNS INT
+BEGIN
+DECLARE b INT DEFAULT 0;
+RETURN a + b;
+END|
+Covering 3 table joins
+
+# sorting on table t2
+# t2.a > 95 would be attached to table t2
+# t1.b=t2.a would be attached to table t1;
+# t3.a= sort-nest.b would be attached to table t3
+
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using filesort
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "t2.b",
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 4.6875,
+ "attached_condition": "t2.a > 95"
+ }
+ }
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 4.6875,
+ "attached_condition": "t1.a = t2.a"
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a = t1.b"
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+a b a b a b
+96 96 96 96 96 96
+97 97 97 97 97 97
+98 98 98 98 98 98
+99 99 99 99 99 99
+# {t1,t2} part of the nest
+# t1.a > 95 would be attached to table t1
+# t1.b=t2.a would be attached to table t2;
+# t3.a= sort-nest.b would be attached to table t3
+
+ALTER TABLE t2 ADD KEY(a);
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where
+1 SIMPLE t2 ref a a 5 test.t1.a 1
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 4.6875,
+ "attached_condition": "t1.a > 95 and t1.a is not null"
+ },
+ "table": {
+ "table_name": "t2",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t1.a"],
+ "rows": 1,
+ "filtered": 100
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a = `sort-nest`.b"
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b
+LIMIT 5;
+a b a b a b
+96 96 96 96 96 96
+97 97 97 97 97 97
+98 98 98 98 98 98
+99 99 99 99 99 99
+ALTER TABLE t2 DROP KEY a;
+
+# {t1,t2} part of the sort nest
+# (t2.a < 2 or t1.b > 98) would be attached to table t2
+
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE (t3.a < 2 and t2.a < 2) OR (t1.b > 98 and t3.b > 98)
+ORDER BY t1.a, t2.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE (t3.a < 2 and t2.a < 2) OR (t1.b > 98 and t3.b > 98)
+ORDER BY t1.a, t2.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL",
+ "attached_condition": "t2.a < 2 or t1.b > 98"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a, `sort-nest`.b",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a < 2 and `sort-nest`.a < 2 or `sort-nest`.b > 98 and t3.b > 98"
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE (t3.a < 2 and t2.a < 2) OR (t1.b > 98 and t3.b > 98)
+ORDER BY t1.a, t2.b
+LIMIT 5;
+a b a b a b
+0 0 1 1 0 0
+0 0 1 1 1 1
+1 1 1 1 0 0
+1 1 1 1 1 1
+2 2 1 1 0 0
+
+# {t1,t2} part of the nest
+# t2.a < 2 or f1(t1.b) attached to table t2
+# t1.b=t2.a would be attached to table t2;
+
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE (t3.a<2 AND t2.a <2) OR (f1(t1.b) > 98 AND t3.b > 98)
+ORDER BY t1.a,t2.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE (t3.a<2 AND t2.a <2) OR (f1(t1.b) > 98 AND t3.b > 98)
+ORDER BY t1.a,t2.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a, `sort-nest`.b",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a < 2 and `sort-nest`.a < 2 or f1(`sort-nest`.b) > 98 and t3.b > 98"
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE (t3.a<2 AND t2.a <2) OR (f1(t1.b) > 98 AND t3.b > 98)
+ORDER BY t1.a,t2.b
+LIMIT 5;
+a b a b a b
+0 0 0 0 0 0
+0 0 0 0 1 1
+0 0 1 1 0 0
+0 0 1 1 1 1
+0 0 1 1 0 0
+#
+# Removing constant from the order by clause
+#
+EXPLAIN SELECT * FROM t1,t2
+WHERE t1.a > 95 AND t1.a=t2.a
+ORDER BY t2.a
+LIMIT 4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2
+WHERE t1.a > 95 AND t1.a=t2.a
+ORDER BY t2.a
+LIMIT 4;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "t2.a",
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 4.6875,
+ "attached_condition": "t1.a > 95"
+ }
+ }
+ },
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 4.6875,
+ "attached_condition": "t2.a = t1.a"
+ }
+ }
+}
+SELECT * FROM t1,t2
+WHERE t1.a > 95 AND t1.a=t2.a
+ORDER BY t2.a
+LIMIT 4;
+a b a b
+96 96 96 96
+97 97 97 97
+98 98 98 98
+99 99 99 99
+EXPLAIN SELECT * FROM t1,t2
+WHERE t1.a > 95 and t1.a=t2.a
+ORDER BY 1+2,t2.a limit 4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2
+WHERE t1.a > 95 and t1.a=t2.a
+ORDER BY 1+2,t2.a limit 4;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "t2.a",
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 4.6875,
+ "attached_condition": "t1.a > 95"
+ }
+ }
+ },
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 4.6875,
+ "attached_condition": "t2.a = t1.a"
+ }
+ }
+}
+SELECT * FROM t1,t2
+WHERE t1.a > 95 and t1.a=t2.a
+ORDER BY 1+2,t2.a limit 4;
+a b a b
+96 96 96 96
+97 97 97 97
+98 98 98 98
+99 99 99 99
+#
+# Equality propagation, both the queries should use a
+# sort nest on {t1,t2}
+#
+EXPLAIN SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t1.b DESC, t2.a DESC limit 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t1.b DESC, t2.a DESC limit 3;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.b = `sort-nest`.b"
+ }
+ }
+}
+SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t1.b DESC, t2.a DESC limit 3;
+b a b a
+99 99 99 99
+99 98 99 99
+99 97 99 99
+EXPLAIN SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b DESC, t2.a DESC
+LIMIT 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b DESC, t2.a DESC
+LIMIT 3;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.b = `sort-nest`.b"
+ }
+ }
+}
+SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b DESC, t2.a DESC
+LIMIT 3;
+b a b a
+99 99 99 99
+99 98 99 99
+99 97 99 99
+#
+# Equality propagation also for arguments of expressions,
+# the plan should use a sort nest on {t1,t2}
+#
+EXPLAIN SELECT t3.b,t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b + 1 DESC, t2.a DESC
+LIMIT 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT t3.b,t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b + 1 DESC, t2.a DESC
+LIMIT 3;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b + 1 desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.b = `sort-nest`.b"
+ }
+ }
+}
+SELECT t3.b,t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.b=t3.b
+ORDER BY t3.b + 1 DESC, t2.a DESC
+LIMIT 3;
+b a b a
+99 99 99 99
+99 98 99 99
+99 97 99 99
+#
+# Rows for the sort-nest should be the cardinality of the join of
+# inner tables of the sort-nest
+#
+# Rows for sort nest would be 9894 here
+ALTER TABLE t1 ADD KEY(a);
+EXPLAIN SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.a > 5 and t1.b=t3.b
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL a NULL NULL NULL 100 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+SELECT t3.b, t2.a, t1.b, t1.a
+FROM t1,t2,t3
+WHERE t1.a > 5 and t1.b=t3.b
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 3;
+b a b a
+99 99 99 99
+99 98 99 99
+99 97 99 99
+ALTER TABLE t1 DROP KEY a;
+#
+# With having clause we can't have a sort-nest
+#
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a
+HAVING t1.a > 95
+ORDER BY t2.b,t1.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using temporary; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where; Using join buffer (incremental, BNL join)
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a
+HAVING t1.a > 95
+ORDER BY t2.b,t1.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "having_condition": "t1.a > 95",
+ "filesort": {
+ "sort_key": "t2.b, t1.b",
+ "temporary_table": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "1Kb",
+ "join_type": "BNL",
+ "attached_condition": "t2.a = t1.a"
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100
+ },
+ "buffer_type": "incremental",
+ "buffer_size": "149Kb",
+ "join_type": "BNL",
+ "attached_condition": "t3.a = t1.b"
+ }
+ }
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a
+HAVING t1.a > 95
+ORDER BY t2.b,t1.b
+LIMIT 5;
+a b a b a b
+96 96 96 96 96 96
+97 97 97 97 97 97
+98 98 98 98 98 98
+99 99 99 99 99 99
+EXPLAIN SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b,t1.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b,t1.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 4.6875,
+ "attached_condition": "t1.a > 95"
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 102,
+ "filtered": 4.6875
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.a = t1.a"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b, `sort-nest`.b",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a = `sort-nest`.b"
+ }
+ }
+}
+SELECT * FROM t1,t2,t3
+WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ORDER BY t2.b,t1.b
+LIMIT 5;
+a b a b a b
+96 96 96 96 96 96
+97 97 97 97 97 97
+98 98 98 98 98 98
+99 99 99 99 99 99
+#
+# Selectivity estimates taken into account for sort-nest{t1,t2}
+#
+CREATE INDEX idx1 ON t1(b);
+CREATE INDEX idx2 ON t2(a);
+CREATE INDEX idx3 ON t3(b);
+EXPLAIN SELECT * from t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a AND t1.b < 5 AND t3.b < 900
+ORDER BY t2.b
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range idx1 idx1 5 NULL 3 Using index condition; Using where
+1 SIMPLE t2 ref idx2 idx2 5 test.t1.a 1
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL idx3 NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * from t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a AND t1.b < 5 AND t3.b < 900
+ORDER BY t2.b
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "range",
+ "possible_keys": ["idx1"],
+ "key": "idx1",
+ "key_length": "5",
+ "used_key_parts": ["b"],
+ "rows": 3,
+ "filtered": 100,
+ "index_condition": "t1.b < 5",
+ "attached_condition": "t1.a is not null"
+ },
+ "table": {
+ "table_name": "t2",
+ "access_type": "ref",
+ "possible_keys": ["idx2"],
+ "key": "idx2",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t1.a"],
+ "rows": 1,
+ "filtered": 100
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "possible_keys": ["idx3"],
+ "rows": 1000,
+ "filtered": 0.6953,
+ "attached_condition": "t3.a = `sort-nest`.b and t3.b < 900"
+ }
+ }
+}
+SELECT * from t1,t2,t3
+WHERE t1.a=t2.a AND t1.b = t3.a AND t1.b < 5 AND t3.b < 900
+ORDER BY t2.b
+LIMIT 5;
+a b a b a b
+0 0 0 0 0 0
+1 1 1 1 1 1
+1 1 1 1 1 1
+2 2 2 2 2 2
+2 2 2 2 2 2
+DROP INDEX idx1 ON t1;
+DROP INDEX idx2 ON t2;
+DROP INDEX idx3 ON t3;
+DROP TABLE t1,t2,t3;
+DROP FUNCTION f1;
+Derived table inside a sort-nest
+CREATE TABLE t1 (f1 varchar(1), f2 varchar(1), KEY (f2));
+INSERT INTO t1 VALUES
+('r','x'), ('x','x'), ('x','x'), ('r','x'), ('x','x');
+CREATE TABLE t2 (f1 varchar(1), f2 varchar(1));
+INSERT INTO t2 VALUES ('s','x');
+CREATE TABLE t3 (f1 varchar(1), f2 varchar(1), KEY (f2));
+INSERT INTO t3 VALUES
+(NULL,'x'), (NULL,'f'), ('t','x'), (NULL,'j'), ('g','x');
+CREATE TABLE t4 (f1 int, f2 varchar(1), KEY (f2,f1)) ;
+INSERT INTO t4 VALUES (2,'x'), (1,'x');
+EXPLAIN SELECT t.f1 as f
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1
+ORDER BY f LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t4 index f2 f2 9 NULL 2 Using where; Using index
+1 PRIMARY <derived2> ref key1 key1 4 test.t4.f2 2
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 4 Using filesort
+1 PRIMARY t3 ref f2 f2 4 sort-nest.f2 2 Using index
+2 DERIVED t2 system NULL NULL NULL NULL 1 Using temporary
+2 DERIVED t1 ALL f2 NULL NULL NULL 5 Using where
+EXPLAIN FORMAT=JSON SELECT t.f1 as f
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1
+ORDER BY f LIMIT 10;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t4",
+ "access_type": "index",
+ "possible_keys": ["f2"],
+ "key": "f2",
+ "key_length": "9",
+ "used_key_parts": ["f2", "f1"],
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t4.f2 is not null",
+ "using_index": true
+ },
+ "table": {
+ "table_name": "<derived2>",
+ "access_type": "ref",
+ "possible_keys": ["key1"],
+ "key": "key1",
+ "key_length": "4",
+ "used_key_parts": ["f1"],
+ "ref": ["test.t4.f2"],
+ "rows": 2,
+ "filtered": 100,
+ "materialized": {
+ "query_block": {
+ "select_id": 2,
+ "temporary_table": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "system",
+ "rows": 1,
+ "filtered": 100
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "possible_keys": ["f2"],
+ "rows": 5,
+ "filtered": 100,
+ "attached_condition": "t1.f2 = 'x'"
+ }
+ }
+ }
+ }
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.f1",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 4,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["f2"],
+ "key": "f2",
+ "key_length": "4",
+ "used_key_parts": ["f2"],
+ "ref": ["sort-nest.f2"],
+ "rows": 2,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+}
+SELECT t.f1 as f
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1
+ORDER BY f LIMIT 10;
+f
+x
+x
+x
+x
+x
+x
+should use the sort-nest too like the query above
+EXPLAIN SELECT t4.f1 as f, t.f1 as g
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1 ORDER BY f,g
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t4 index f2 f2 9 NULL 2 Using where; Using index
+1 PRIMARY <derived2> ref key1 key1 4 test.t4.f2 2
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 4 Using filesort
+1 PRIMARY t3 ref f2 f2 4 sort-nest.f2 2 Using index
+2 DERIVED t2 system NULL NULL NULL NULL 1 Using temporary
+2 DERIVED t1 ALL f2 NULL NULL NULL 5 Using where
+EXPLAIN FORMAT=JSON SELECT t4.f1 as f, t.f1 as g
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1 ORDER BY f,g
+LIMIT 10;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t4",
+ "access_type": "index",
+ "possible_keys": ["f2"],
+ "key": "f2",
+ "key_length": "9",
+ "used_key_parts": ["f2", "f1"],
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t4.f2 is not null",
+ "using_index": true
+ },
+ "table": {
+ "table_name": "<derived2>",
+ "access_type": "ref",
+ "possible_keys": ["key1"],
+ "key": "key1",
+ "key_length": "4",
+ "used_key_parts": ["f1"],
+ "ref": ["test.t4.f2"],
+ "rows": 2,
+ "filtered": 100,
+ "materialized": {
+ "query_block": {
+ "select_id": 2,
+ "temporary_table": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "system",
+ "rows": 1,
+ "filtered": 100
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "possible_keys": ["f2"],
+ "rows": 5,
+ "filtered": 100,
+ "attached_condition": "t1.f2 = 'x'"
+ }
+ }
+ }
+ }
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.f1, `sort-nest`.f1",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 4,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["f2"],
+ "key": "f2",
+ "key_length": "4",
+ "used_key_parts": ["f2"],
+ "ref": ["sort-nest.f2"],
+ "rows": 2,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+}
+SELECT t4.f1 as f, t.f1 as g
+FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1 ORDER BY f,g
+LIMIT 10;
+f g
+1 x
+1 x
+1 x
+2 x
+2 x
+2 x
+DROP TABLE t1,t2,t3,t4;
+views inside a sort-nest
+CREATE TABLE t0 (x int);
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (b int, c int default 0);
+INSERT t0 (x) VALUES (0),(10);
+INSERT t1 (a) VALUES (1), (2);
+INSERT t2 (b) VALUES (1), (2);
+CREATE VIEW v1 as SELECT t2.b,t2.c FROM t1, t2
+WHERE t1.a=t2.b and t2.b < 3 WITH CHECK OPTION;
+EXPLAIN SELECT * FROM v1,t0
+WHERE b<3
+ORDER BY x,b DESC
+LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t0 ALL NULL NULL NULL NULL 2 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using where
+EXPLAIN FORMAT=JSON SELECT * FROM v1,t0
+WHERE b<3
+ORDER BY x,b DESC
+LIMIT 2;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t1.a < 3 and t1.a < 3"
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t0",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "65",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.x, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t2.b = `sort-nest`.a"
+ }
+ }
+}
+SELECT * FROM v1,t0
+WHERE b<3
+ORDER BY x,b DESC
+LIMIT 2;
+b c x
+2 0 0
+1 0 0
+DROP TABLE t0,t1,t2;
+DROP VIEW v1;
+# Primary key considered as the key that could achieve ordering
+CREATE TABLE t1 (id char(32) NOT NULL primary key);
+INSERT INTO t1 VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
+CREATE TABLE t2 (id char(32) NOT NULL primary key);
+INSERT INTO t2 VALUES (0), (1), (2), (3);
+EXPLAIN SELECT t1.id
+FROM t1 INNER JOIN t2 ON t1.id=t2.id
+ORDER BY t2.id LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 index PRIMARY PRIMARY 32 NULL 2 Using index
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 32 test.t2.id 1 Using index
+EXPLAIN FORMAT=JSON SELECT t1.id
+FROM t1 INNER JOIN t2 ON t1.id=t2.id
+ORDER BY t2.id LIMIT 2;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "index",
+ "possible_keys": ["PRIMARY"],
+ "key": "PRIMARY",
+ "key_length": "32",
+ "used_key_parts": ["id"],
+ "rows": 2,
+ "filtered": 100,
+ "using_index": true
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "eq_ref",
+ "possible_keys": ["PRIMARY"],
+ "key": "PRIMARY",
+ "key_length": "32",
+ "used_key_parts": ["id"],
+ "ref": ["test.t2.id"],
+ "rows": 1,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+}
+SELECT t1.id
+FROM t1 INNER JOIN t2 ON t1.id=t2.id
+ORDER BY t2.id LIMIT 2;
+id
+0
+1
+DROP TABLE t1,t2;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT seq-1, seq-1 from seq_1_to_10;
+CREATE TABLE t2 as SELECT * from t1;
+CREATE TABLE t3 (a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+#
+# Outer Join
+#
+# sort-nest(t2,t1) and t3 outside the nest
+EXPLAIN SELECT * from t2,t1 left join t3 on t3.a=t1.b
+order by t2.a desc,t1.a desc limit 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT * from t2,t1 left join t3 on t3.a=t1.b
+order by t2.a desc,t1.a desc limit 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "const_condition": "1",
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "trigcond(t3.a = `sort-nest`.b)"
+ }
+ }
+}
+SELECT * from t2,t1 left join t3 on t3.a=t1.b
+order by t2.a desc,t1.a desc limit 5;
+a b a b a b
+9 9 9 9 9 9
+9 9 8 8 8 8
+9 9 7 7 7 7
+9 9 6 6 6 6
+9 9 5 5 5 5
+#
+# no sort-nest as all the inner tables of the outer join will be
+# inside the nest, this should use temporary table to sort after the
+# entire join is computed
+#
+EXPLAIN SELECT * FROM t2 LEFT JOIN (t1 LEFT JOIN t3 ON t3.a=t1.b)
+ON t2.b=t1.a
+ORDER BY t2.a DESC,t1.a DESC LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using temporary; Using filesort
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where; Using join buffer (incremental, BNL join)
+EXPLAIN FORMAT=JSON SELECT * FROM t2 LEFT JOIN (t1 LEFT JOIN t3 ON t3.a=t1.b)
+ON t2.b=t1.a
+ORDER BY t2.a DESC,t1.a DESC LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "const_condition": "1",
+ "filesort": {
+ "sort_key": "t2.a desc, t1.a desc",
+ "temporary_table": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "141",
+ "join_type": "BNL",
+ "attached_condition": "trigcond(t1.a = t2.b)"
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100
+ },
+ "buffer_type": "incremental",
+ "buffer_size": "1Kb",
+ "join_type": "BNL",
+ "attached_condition": "trigcond(t3.a = t1.b)"
+ }
+ }
+ }
+ }
+}
+SELECT * FROM t2 LEFT JOIN (t1 LEFT JOIN t3 ON t3.a=t1.b)
+ON t2.b=t1.a
+ORDER BY t2.a DESC,t1.a DESC LIMIT 5;
+a b a b a b
+9 9 9 9 9 9
+8 8 8 8 8 8
+7 7 7 7 7 7
+6 6 6 6 6 6
+5 5 5 5 5 5
+DROP TABLE t1,t2,t3;
+#
+# Sort-nest with prepared statements
+#
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT seq-1,seq-1 from seq_1_to_10;
+CREATE TABLE t2 as SELECT * from t1;
+CREATE TABLE t3 (a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+# sort-nest on table t1,t2
+prepare ps1 from "EXPLAIN SELECT * FROM t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC,t1.a DESC
+ LIMIT 5";
+EXECUTE ps1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+EXECUTE ps1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where
+# sort-nest on table t1,t2
+PREPARE ps2 from "EXPLAIN FORMAT=JSON
+ SELECT * from t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC, t1.a DESC
+ LIMIT 5";
+EXECUTE ps2;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a = `sort-nest`.b + 1"
+ }
+ }
+}
+EXECUTE ps2;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100,
+ "attached_condition": "t3.a = `sort-nest`.b + 1"
+ }
+ }
+}
+# sort-nest on table t1,t2
+PREPARE ps3 from "SELECT * from t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC, t1.a DESC
+ LIMIT 5";
+EXECUTE ps3;
+a b a b a b
+9 9 9 9 10 10
+9 9 8 8 9 9
+9 9 7 7 8 8
+9 9 6 6 7 7
+9 9 5 5 6 6
+EXECUTE ps3;
+a b a b a b
+9 9 9 9 10 10
+9 9 8 8 9 9
+9 9 7 7 8 8
+9 9 6 6 7 7
+9 9 5 5 6 6
+DEALLOCATE PREPARE ps1;
+DEALLOCATE PREPARE ps2;
+DEALLOCATE PREPARE ps3;
+DROP TABLE t1,t2,t3;
+# INDEPENDENT SUBQUERIES
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a,a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3 (a int, b int, c int);
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_1000;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+#
+# sort-nest(t2,t1) and independent subquery in the SELECT list
+#
+EXPLAIN SELECT (SELECT A.a FROM t3 A WHERE A.a > 5 limit 1) as x,
+t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a = t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 PRIMARY t3 ALL NULL NULL NULL NULL 1000
+2 SUBQUERY A ALL NULL NULL NULL NULL 1000 Using where
+EXPLAIN FORMAT=JSON SELECT (SELECT A.a FROM t3 A WHERE A.a > 5 limit 1) as x,
+t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a = t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.a = t1.a"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.b desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100
+ },
+ "subqueries": [
+ {
+ "query_block": {
+ "select_id": 2,
+ "table": {
+ "table_name": "A",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 99.219,
+ "attached_condition": "A.a > 5"
+ }
+ }
+ }
+ ]
+ }
+}
+SELECT (SELECT A.a FROM t3 A WHERE A.a > 5 limit 1) as x,
+t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a = t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+x b b a
+6 4 4 0
+6 4 4 1
+6 4 4 2
+6 4 4 3
+6 4 4 4
+DROP TABLE t0,t1,t2,t3;
+#
+# Const table should not form the sort-nest
+#
+CREATE TABLE t1 (i1 integer NOT NULL PRIMARY KEY);
+CREATE TABLE t2 (i2 integer NOT NULL PRIMARY KEY);
+CREATE TABLE t3 (i3 integer);
+INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8),
+(9), (10), (11), (12);
+INSERT INTO t2 SELECT * FROM t1;
+EXPLAIN SELECT t1.*, t2.*
+FROM t1 JOIN t2 ON t1.i1 = t2.i2
+LEFT JOIN t3 ON t2.i2 = t3.i3
+ORDER BY t1.i1
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t3 system NULL NULL NULL NULL 0 Const row not found
+1 SIMPLE t1 index PRIMARY PRIMARY 4 NULL 12 Using index
+1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.i1 1 Using index
+SELECT t1.*, t2.*
+FROM t1 JOIN t2 ON t1.i1 = t2.i2
+LEFT JOIN t3 ON t2.i2 = t3.i3
+ORDER BY t1.i1
+LIMIT 5;
+i1 i2
+1 1
+2 2
+3 3
+4 4
+5 5
+DROP TABLE t1,t2,t3;
+# All tables are const tables
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (0,0);
+CREATE TABLE t2(a int, b int);
+INSERT INTO t2 VALUES (0,0);
+CREATE TABLE t3(a int, b int);
+INSERT INTO t3 VALUES (0,0);
+EXPLAIN SELECT t1.a,t2.a,t3.a
+FROM t1,t2,t3
+WHERE t1.b = t2.b AND t3.b=t1.b
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 system NULL NULL NULL NULL 1
+1 SIMPLE t2 system NULL NULL NULL NULL 1
+1 SIMPLE t3 system NULL NULL NULL NULL 1
+SELECT t1.a,t2.a,t3.a
+FROM t1,t2,t3
+WHERE t1.b = t2.b AND t3.b=t1.b
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+a a a
+0 0 0
+DROP TABLE t1,t2,t3;
+#
+# Tests where Index(scan, ref or range access) satisfies the ORDERING
+#
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+INSERT INTO t1 VALUES (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+INSERT INTO t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+INSERT INTO t1 VALUES (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+INSERT INTO t1 VALUES (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+INSERT INTO t1 VALUES (1,1,2);
+# index key a_b, no need for filesort
+EXPLAIN SELECT a,b,c FROM t1
+WHERE a=1 and c=2
+ORDER BY b
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a_b,a_c a_b 5 NULL 19 Using index condition; Using where
+SELECT a,b,c FROM t1
+WHERE a=1 and c=2
+ORDER BY b
+LIMIT 10;
+a b c
+1 1 2
+1 7 2
+1 7 2
+1 8 2
+1 8 2
+1 9 2
+1 9 2
+1 10 2
+1 10 2
+1 11 2
+DROP TABLE t1;
+#
+# Testing ORDER BY LIMIT with OFFSET, should show the same plan and same
+# estimate of rows for the sort-nest
+#
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0;
+CREATE TABLE t2 as SELECT * from t1;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_100;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t3.a = t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 10 Using filesort
+1 SIMPLE t3 ref a a 5 sort-nest.a 1 Using index
+SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t3.a = t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 10;
+a b b a
+9 9 9 9
+8 8 8 8
+7 7 7 7
+6 6 6 6
+5 5 5 5
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+EXPLAIN SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t3.a=t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5 OFFSET 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 10 Using filesort
+1 SIMPLE t3 ref a a 5 sort-nest.a 1 Using index
+SELECT t1.a, t2.b, t1.b, t3.a
+FROM t1,t2,t3
+WHERE t1.a=t2.a AND t3.a=t2.a
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5 OFFSET 5;
+a b b a
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+drop table t0,t1,t2,t3;
+#
+# Constant removed from ORDER BY , so no need of sorting
+#
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0;
+CREATE TABLE t2 as SELECT * from t1;
+set use_sort_nest=1;
+EXPLAIN SELECT *
+FROM t1,t2
+WHERE t1.a=t2.a and t1.b= 4
+ORDER BY t1.b DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 10 Using where
+SELECT *
+FROM t1,t2
+WHERE t1.a=t2.a and t1.b= 4
+ORDER BY t1.b DESC
+LIMIT 5;
+a b a b
+4 4 4 4
+drop table t0,t1,t2;
+#
+# ORDER BY clause containing expressions
+#
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_10;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY abs(t3.a+t1.b) DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5
+1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 SIMPLE t3 ref a a 5 sort-nest.a 1 Using index
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY abs(t3.a+t1.b) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+set use_sort_nest=0;
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY abs(t3.a+t1.b) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+#
+# No sort nest where ORDER BY item are expensive to compute like
+# stored functions, subqueries etc
+CREATE FUNCTION f1(a INT) RETURNS INT
+BEGIN
+RETURN a;
+END|
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY f1(t3.a+t1.b) DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 ref a a 5 test.t2.a 1 Using index
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY f1(t3.a+t1.b) DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "filesort": {
+ "sort_key": "f1(t3.a + t1.b) desc",
+ "temporary_table": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.b = t1.a and t2.a is not null"
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t2.a"],
+ "rows": 1,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+ }
+ }
+}
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY f1(t3.a+t1.b) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+set use_sort_nest=0;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY f1(t3.a+t1.b) DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 ref a a 5 test.t2.a 1 Using index
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY f1(t3.a+t1.b) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+drop function f1;
+#
+# Window function in order by clause, sort-nest not allowed
+#
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b AND t2.a=t3.a
+ORDER BY row_number() OVER (ORDER BY t1.a) DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
+1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 ref a a 5 test.t2.a 1 Using index
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b AND t2.a=t3.a
+ORDER BY row_number() OVER (ORDER BY t1.a) DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "filesort": {
+ "sort_key": "row_number() over ( order by t1.a) desc",
+ "window_functions_computation": {
+ "sorts": {
+ "filesort": {
+ "sort_key": "t1.a"
+ }
+ },
+ "temporary_table": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.b = t1.a and t2.a is not null"
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t2.a"],
+ "rows": 1,
+ "filtered": 100,
+ "using_index": true
+ }
+ }
+ }
+ }
+ }
+}
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b AND t2.a=t3.a
+ORDER BY row_number() OVER (ORDER BY t1.a) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+set use_sort_nest=0;
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b AND t2.a=t3.a
+ORDER BY row_number() OVER (ORDER BY t1.a) DESC
+LIMIT 5;
+a b b t1.b + t2.b
+4 4 4 8
+3 3 3 6
+2 2 2 4
+1 1 1 2
+0 0 0 0
+#
+# Subqueries used in order by clause
+#
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY (select A.a+t1.b from t1 A limit 1)
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 PRIMARY t3 ref a a 5 test.t2.a 1 Using index
+2 DEPENDENT SUBQUERY A ALL NULL NULL NULL NULL 5
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY (select A.a+t1.b from t1 A limit 1)
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "filesort": {
+ "sort_key": "(subquery#2)",
+ "temporary_table": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.b = t1.a and t2.a is not null"
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t2.a"],
+ "rows": 1,
+ "filtered": 100,
+ "using_index": true
+ },
+ "subqueries": [
+ {
+ "expression_cache": {
+ "state": "uninitialized",
+ "query_block": {
+ "select_id": 2,
+ "table": {
+ "table_name": "A",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY (select A.a+t1.b from t1 A limit 1)
+LIMIT 5;
+a b b t1.b + t2.b
+0 0 0 0
+1 1 1 2
+2 2 2 4
+3 3 3 6
+4 4 4 8
+set use_sort_nest=0;
+SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+FROM t1,t2, t3
+WHERE t1.a=t2.b and t2.a=t3.a
+ORDER BY (select A.a+t1.b from t1 A limit 1)
+LIMIT 5;
+a b b t1.b + t2.b
+0 0 0 0
+1 1 1 2
+2 2 2 4
+3 3 3 6
+4 4 4 8
+drop table t0,t1,t2,t3;
diff --git a/mysql-test/main/sort_nest.test b/mysql-test/main/sort_nest.test
new file mode 100644
index 00000000000..a26dba836fe
--- /dev/null
+++ b/mysql-test/main/sort_nest.test
@@ -0,0 +1,670 @@
+--source include/have_sequence.inc
+
+set use_sort_nest=1;
+
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_100;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+--echo #
+--echo # sort nest on (t2,t1)
+--echo # ref(sort-nest.b) access on table t3
+--echo #
+
+let $query= SELECT t1.a, t2.b, t1.b, t3.a
+ FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t2.b=t3.a
+ ORDER BY t2.b DESC, t1.b DESC
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+DROP TABLE t0,t1,t2,t3;
+
+CREATE TABLE t1(a int, b int);
+INSERT INTO t1 SELECT seq-1, seq-1 from seq_1_to_100;
+
+CREATE TABLE t2(a int, b int);
+INSERT INTO t2(a,b) VALUES (1,1), (2,2);
+INSERT INTO t2 SELECT seq-1, seq-1 from seq_1_to_100;
+CREATE TABLE t3(a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+delimiter |;
+CREATE FUNCTION f1(a int) RETURNS INT
+BEGIN
+ DECLARE b INT DEFAULT 0;
+ RETURN a + b;
+END|
+delimiter ;|
+
+--echo Covering 3 table joins
+
+--echo
+--echo # sorting on table t2
+--echo # t2.a > 95 would be attached to table t2
+--echo # t1.b=t2.a would be attached to table t1;
+--echo # t3.a= sort-nest.b would be attached to table t3
+--echo
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ ORDER BY t2.b
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo # {t1,t2} part of the nest
+--echo # t1.a > 95 would be attached to table t1
+--echo # t1.b=t2.a would be attached to table t2;
+--echo # t3.a= sort-nest.b would be attached to table t3
+--echo
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ ORDER BY t2.b
+ LIMIT 5;
+
+ALTER TABLE t2 ADD KEY(a);
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+ALTER TABLE t2 DROP KEY a;
+
+--echo
+--echo # {t1,t2} part of the sort nest
+--echo # (t2.a < 2 or t1.b > 98) would be attached to table t2
+--echo
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE (t3.a < 2 and t2.a < 2) OR (t1.b > 98 and t3.b > 98)
+ ORDER BY t1.a, t2.b
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo
+--echo # {t1,t2} part of the nest
+--echo # t2.a < 2 or f1(t1.b) attached to table t2
+--echo # t1.b=t2.a would be attached to table t2;
+--echo
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE (t3.a<2 AND t2.a <2) OR (f1(t1.b) > 98 AND t3.b > 98)
+ ORDER BY t1.a,t2.b
+ LIMIT 5;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # Removing constant from the order by clause
+--echo #
+
+let $query= SELECT * FROM t1,t2
+ WHERE t1.a > 95 AND t1.a=t2.a
+ ORDER BY t2.a
+ LIMIT 4;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+let $query= SELECT * FROM t1,t2
+ WHERE t1.a > 95 and t1.a=t2.a
+ ORDER BY 1+2,t2.a limit 4;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # Equality propagation, both the queries should use a
+--echo # sort nest on {t1,t2}
+--echo #
+
+let $query= SELECT t3.b, t2.a, t1.b, t1.a
+ FROM t1,t2,t3
+ WHERE t1.b=t3.b
+ ORDER BY t1.b DESC, t2.a DESC limit 3;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+let $query= SELECT t3.b, t2.a, t1.b, t1.a
+ FROM t1,t2,t3
+ WHERE t1.b=t3.b
+ ORDER BY t3.b DESC, t2.a DESC
+ LIMIT 3;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # Equality propagation also for arguments of expressions,
+--echo # the plan should use a sort nest on {t1,t2}
+--echo #
+
+let $query=SELECT t3.b,t2.a, t1.b, t1.a
+ FROM t1,t2,t3
+ WHERE t1.b=t3.b
+ ORDER BY t3.b + 1 DESC, t2.a DESC
+ LIMIT 3;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # Rows for the sort-nest should be the cardinality of the join of
+--echo # inner tables of the sort-nest
+--echo #
+
+--echo # Rows for sort nest would be 9894 here
+
+ALTER TABLE t1 ADD KEY(a);
+let $query= SELECT t3.b, t2.a, t1.b, t1.a
+ FROM t1,t2,t3
+ WHERE t1.a > 5 and t1.b=t3.b
+ ORDER BY t1.b DESC, t2.a DESC
+ LIMIT 3;
+
+eval EXPLAIN $query;
+eval $query;
+ALTER TABLE t1 DROP KEY a;
+
+--echo #
+--echo # With having clause we can't have a sort-nest
+--echo #
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t1.b = t3.a
+ HAVING t1.a > 95
+ ORDER BY t2.b,t1.b
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE t1.a > 95 AND t1.a=t2.a AND t1.b = t3.a
+ ORDER BY t2.b,t1.b
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # Selectivity estimates taken into account for sort-nest{t1,t2}
+--echo #
+
+CREATE INDEX idx1 ON t1(b);
+CREATE INDEX idx2 ON t2(a);
+CREATE INDEX idx3 ON t3(b);
+
+let $query= SELECT * from t1,t2,t3
+ WHERE t1.a=t2.a AND t1.b = t3.a AND t1.b < 5 AND t3.b < 900
+ ORDER BY t2.b
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+DROP INDEX idx1 ON t1;
+DROP INDEX idx2 ON t2;
+DROP INDEX idx3 ON t3;
+DROP TABLE t1,t2,t3;
+DROP FUNCTION f1;
+
+--echo Derived table inside a sort-nest
+
+CREATE TABLE t1 (f1 varchar(1), f2 varchar(1), KEY (f2));
+INSERT INTO t1 VALUES
+('r','x'), ('x','x'), ('x','x'), ('r','x'), ('x','x');
+
+CREATE TABLE t2 (f1 varchar(1), f2 varchar(1));
+INSERT INTO t2 VALUES ('s','x');
+
+CREATE TABLE t3 (f1 varchar(1), f2 varchar(1), KEY (f2));
+INSERT INTO t3 VALUES
+(NULL,'x'), (NULL,'f'), ('t','x'), (NULL,'j'), ('g','x');
+
+CREATE TABLE t4 (f1 int, f2 varchar(1), KEY (f2,f1)) ;
+INSERT INTO t4 VALUES (2,'x'), (1,'x');
+
+let $query= SELECT t.f1 as f
+ FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+ WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1
+ ORDER BY f LIMIT 10;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo should use the sort-nest too like the query above
+
+let $query= SELECT t4.f1 as f, t.f1 as g
+ FROM (SELECT DISTINCT t1.* FROM t1,t2 WHERE t2.f2 = t1.f2) t,t3,t4
+ WHERE t4.f2 = t3.f2 AND t4.f2 = t.f1 ORDER BY f,g
+ LIMIT 10;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+DROP TABLE t1,t2,t3,t4;
+
+--echo views inside a sort-nest
+CREATE TABLE t0 (x int);
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (b int, c int default 0);
+
+INSERT t0 (x) VALUES (0),(10);
+INSERT t1 (a) VALUES (1), (2);
+INSERT t2 (b) VALUES (1), (2);
+
+CREATE VIEW v1 as SELECT t2.b,t2.c FROM t1, t2
+ WHERE t1.a=t2.b and t2.b < 3 WITH CHECK OPTION;
+
+let $query= SELECT * FROM v1,t0
+ WHERE b<3
+ ORDER BY x,b DESC
+ LIMIT 2;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+DROP TABLE t0,t1,t2;
+DROP VIEW v1;
+
+--echo # Primary key considered as the key that could achieve ordering
+
+CREATE TABLE t1 (id char(32) NOT NULL primary key);
+INSERT INTO t1 VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
+CREATE TABLE t2 (id char(32) NOT NULL primary key);
+INSERT INTO t2 VALUES (0), (1), (2), (3);
+
+let $query= SELECT t1.id
+ FROM t1 INNER JOIN t2 ON t1.id=t2.id
+ ORDER BY t2.id LIMIT 2;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+DROP TABLE t1,t2;
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT seq-1, seq-1 from seq_1_to_10;
+CREATE TABLE t2 as SELECT * from t1;
+
+CREATE TABLE t3 (a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+let $query= SELECT * from t2,t1 left join t3 on t3.a=t1.b
+ order by t2.a desc,t1.a desc limit 5;
+
+--echo #
+--echo # Outer Join
+--echo #
+
+--echo # sort-nest(t2,t1) and t3 outside the nest
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo #
+--echo # no sort-nest as all the inner tables of the outer join will be
+--echo # inside the nest, this should use temporary table to sort after the
+--echo # entire join is computed
+--echo #
+
+let $query= SELECT * FROM t2 LEFT JOIN (t1 LEFT JOIN t3 ON t3.a=t1.b)
+ ON t2.b=t1.a
+ ORDER BY t2.a DESC,t1.a DESC LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+DROP TABLE t1,t2,t3;
+
+--echo #
+--echo # Sort-nest with prepared statements
+--echo #
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT seq-1,seq-1 from seq_1_to_10;
+CREATE TABLE t2 as SELECT * from t1;
+
+CREATE TABLE t3 (a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 from seq_1_to_1000;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+--echo # sort-nest on table t1,t2
+prepare ps1 from "EXPLAIN SELECT * FROM t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC,t1.a DESC
+ LIMIT 5";
+
+EXECUTE ps1;
+EXECUTE ps1;
+
+--echo # sort-nest on table t1,t2
+PREPARE ps2 from "EXPLAIN FORMAT=JSON
+ SELECT * from t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC, t1.a DESC
+ LIMIT 5";
+EXECUTE ps2;
+EXECUTE ps2;
+
+--echo # sort-nest on table t1,t2
+PREPARE ps3 from "SELECT * from t2,t1,t3
+ WHERE t3.a=t1.b+1
+ ORDER BY t2.a DESC, t1.a DESC
+ LIMIT 5";
+EXECUTE ps3;
+EXECUTE ps3;
+
+DEALLOCATE PREPARE ps1;
+DEALLOCATE PREPARE ps2;
+DEALLOCATE PREPARE ps3;
+DROP TABLE t1,t2,t3;
+
+--echo # INDEPENDENT SUBQUERIES
+
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a,a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3 (a int, b int, c int);
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_1000;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+--echo #
+--echo # sort-nest(t2,t1) and independent subquery in the SELECT list
+--echo #
+
+let $query= SELECT (SELECT A.a FROM t3 A WHERE A.a > 5 limit 1) as x,
+ t2.b, t1.b, t3.a
+ FROM t1,t2,t3
+ WHERE t1.a = t2.a
+ ORDER BY t2.b DESC, t1.b DESC
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+DROP TABLE t0,t1,t2,t3;
+
+--echo #
+--echo # Const table should not form the sort-nest
+--echo #
+
+CREATE TABLE t1 (i1 integer NOT NULL PRIMARY KEY);
+CREATE TABLE t2 (i2 integer NOT NULL PRIMARY KEY);
+CREATE TABLE t3 (i3 integer);
+INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6), (7), (8),
+ (9), (10), (11), (12);
+
+INSERT INTO t2 SELECT * FROM t1;
+
+let $query= SELECT t1.*, t2.*
+ FROM t1 JOIN t2 ON t1.i1 = t2.i2
+ LEFT JOIN t3 ON t2.i2 = t3.i3
+ ORDER BY t1.i1
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval $query;
+
+DROP TABLE t1,t2,t3;
+
+--echo # All tables are const tables
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (0,0);
+CREATE TABLE t2(a int, b int);
+INSERT INTO t2 VALUES (0,0);
+CREATE TABLE t3(a int, b int);
+INSERT INTO t3 VALUES (0,0);
+
+let $query= SELECT t1.a,t2.a,t3.a
+ FROM t1,t2,t3
+ WHERE t1.b = t2.b AND t3.b=t1.b
+ ORDER BY t2.a DESC,t1.a DESC
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval $query;
+
+DROP TABLE t1,t2,t3;
+
+--echo #
+--echo # Tests where Index(scan, ref or range access) satisfies the ORDERING
+--echo #
+
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+
+INSERT INTO t1 VALUES (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+INSERT INTO t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+INSERT INTO t1 VALUES (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+INSERT INTO t1 VALUES (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+INSERT INTO t1 VALUES (1,1,2);
+
+--echo # index key a_b, no need for filesort
+
+let $query= SELECT a,b,c FROM t1
+ WHERE a=1 and c=2
+ ORDER BY b
+ LIMIT 10;
+
+eval EXPLAIN $query;
+eval $query;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing ORDER BY LIMIT with OFFSET, should show the same plan and same
+--echo # estimate of rows for the sort-nest
+--echo #
+
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0;
+CREATE TABLE t2 as SELECT * from t1;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_100;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+let $query= SELECT t1.a, t2.b, t1.b, t3.a
+ FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t3.a = t2.a
+ ORDER BY t2.b DESC, t1.b DESC
+ LIMIT 10;
+eval EXPLAIN $query;
+eval $query;
+
+let $query= SELECT t1.a, t2.b, t1.b, t3.a
+ FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t3.a=t2.a
+ ORDER BY t2.b DESC, t1.b DESC
+ LIMIT 5 OFFSET 5;
+
+eval EXPLAIN $query;
+eval $query;
+
+drop table t0,t1,t2,t3;
+
+
+--echo #
+--echo # Constant removed from ORDER BY , so no need of sorting
+--echo #
+
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0;
+CREATE TABLE t2 as SELECT * from t1;
+
+let $query= SELECT *
+ FROM t1,t2
+ WHERE t1.a=t2.a and t1.b= 4
+ ORDER BY t1.b DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval $query;
+
+drop table t0,t1,t2;
+
+--echo #
+--echo # ORDER BY clause containing expressions
+--echo #
+
+CREATE TABLE t0 (a int);
+INSERT INTO t0 SELECT seq-1 from seq_1_to_10;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 SELECT a, a from t0 where a <5;
+CREATE TABLE t2 as SELECT * from t1 where a < 5;
+CREATE TABLE t3(a int, b int, c int, key(a));
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 from seq_1_to_10;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+--enable_result_log
+
+
+let $query= SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+ FROM t1,t2, t3
+ WHERE t1.a=t2.b and t2.a=t3.a
+ ORDER BY abs(t3.a+t1.b) DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+--echo #
+--echo # No sort nest where ORDER BY item are expensive to compute like
+--echo # stored functions, subqueries etc
+
+delimiter |;
+
+CREATE FUNCTION f1(a INT) RETURNS INT
+BEGIN
+ RETURN a;
+END|
+
+delimiter ;|
+
+let $query= SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+ FROM t1,t2, t3
+ WHERE t1.a=t2.b and t2.a=t3.a
+ ORDER BY f1(t3.a+t1.b) DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+eval $query;
+
+drop function f1;
+
+--echo #
+--echo # Window function in order by clause, sort-nest not allowed
+--echo #
+
+let $query= SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+ FROM t1,t2, t3
+ WHERE t1.a=t2.b AND t2.a=t3.a
+ ORDER BY row_number() OVER (ORDER BY t1.a) DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+--echo #
+--echo # Subqueries used in order by clause
+--echo #
+
+let $query= SELECT t1.a, t2.b, t1.b, t1.b + t2.b
+ FROM t1,t2, t3
+ WHERE t1.a=t2.b and t2.a=t3.a
+ ORDER BY (select A.a+t1.b from t1 A limit 1)
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+drop table t0,t1,t2,t3;
diff --git a/mysql-test/main/sort_nest_dbt3.result b/mysql-test/main/sort_nest_dbt3.result
new file mode 100644
index 00000000000..6143ede4d1b
--- /dev/null
+++ b/mysql-test/main/sort_nest_dbt3.result
@@ -0,0 +1,610 @@
+create database dbt3;
+use dbt3;
+Table Op Msg_type Msg_text
+dbt3.customer analyze status Engine-independent statistics collected
+dbt3.customer analyze status OK
+Table Op Msg_type Msg_text
+dbt3.orders analyze status Engine-independent statistics collected
+dbt3.orders analyze status OK
+Table Op Msg_type Msg_text
+dbt3.lineitem analyze status Engine-independent statistics collected
+dbt3.lineitem analyze status OK
+Table Op Msg_type Msg_text
+dbt3.nation analyze status Engine-independent statistics collected
+dbt3.nation analyze status OK
+# done to avoid filter for now
+set optimizer_switch='rowid_filter=off';
+set use_sort_nest=1;
+#
+# USING INDEXES FOR ORDERING
+#
+#
+# Using index scan on first table
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders index i_o_custkey i_o_orderdate 4 NULL 5 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal
+16 Customer#000000088 1998-08-02 131752.07 8031.44
+0 Customer#000000080 1998-07-30 141858.97 7383.53
+10 Customer#000000049 1998-07-29 37776.79 4573.94
+3 Customer#000000022 1998-07-28 139104.17 591.98
+3 Customer#000000022 1998-07-27 82746.74 591.98
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders index i_o_custkey i_o_orderdate 4 NULL 5 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal
+16 Customer#000000088 1998-08-02 131752.07 8031.44
+0 Customer#000000080 1998-07-30 141858.97 7383.53
+10 Customer#000000049 1998-07-29 37776.79 4573.94
+3 Customer#000000022 1998-07-28 139104.17 591.98
+3 Customer#000000022 1998-07-27 82746.74 591.98
+#
+# Using range access for customer with index on (c), this does
+# the ordering
+#
+alter table orders add key o_orderdate(o_orderDATE, o_custkey);
+set use_sort_nest=1;
+explain SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey,o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+o_custkey c_name o_orderDate o_totalprice c_acctbal
+130 Customer#000000130 1997-07-30 67979.49 5073.58
+101 Customer#000000101 1997-07-31 79189.58 7470.96
+13 Customer#000000013 1997-07-31 125188.72 3857.34
+64 Customer#000000064 1997-08-03 76164.41 -646.64
+50 Customer#000000050 1997-08-03 20791.5 4266.13
+set use_sort_nest=0;
+explain SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey,o_orderdate i_o_orderdate 4 NULL 242 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+o_custkey c_name o_orderDate o_totalprice c_acctbal
+130 Customer#000000130 1997-07-30 67979.49 5073.58
+101 Customer#000000101 1997-07-31 79189.58 7470.96
+13 Customer#000000013 1997-07-31 125188.72 3857.34
+64 Customer#000000064 1997-08-03 76164.41 -646.64
+50 Customer#000000050 1997-08-03 20791.5 4266.13
+alter table orders drop key o_orderDate;
+#
+# USING FILESORT
+#
+#
+# Filesort on first table (orders)
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 197 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+5 Customer#000000010 1997-04-04 258779.02 2753.54 ETHIOPIA
+0 Customer#000000076 1997-08-24 231831.35 5745.33 ALGERIA
+9 Customer#000000094 1997-10-17 224382.57 5500.11 INDONESIA
+6 Customer#000000046 1997-05-26 216826.73 5744.59 FRANCE
+3 Customer#000000005 1997-04-23 210643.96 794.47 CANADA
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+3 Customer#000000005 1997-11-09 190142.17 794.47 CANADA
+9 Customer#000000094 1997-11-30 187516.29 5500.11 INDONESIA
+3 Customer#000000005 1997-11-12 185496.66 794.47 CANADA
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 197 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+5 Customer#000000010 1997-04-04 258779.02 2753.54 ETHIOPIA
+0 Customer#000000076 1997-08-24 231831.35 5745.33 ALGERIA
+9 Customer#000000094 1997-10-17 224382.57 5500.11 INDONESIA
+6 Customer#000000046 1997-05-26 216826.73 5744.59 FRANCE
+3 Customer#000000005 1997-04-23 210643.96 794.47 CANADA
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+3 Customer#000000005 1997-11-09 190142.17 794.47 CANADA
+9 Customer#000000094 1997-11-30 187516.29 5500.11 INDONESIA
+3 Customer#000000005 1997-11-12 185496.66 794.47 CANADA
+#
+# Filesort on first table (lineitem)
+#
+show create table orders;
+Table Create Table
+orders CREATE TABLE `orders` (
+ `o_orderkey` int(11) NOT NULL,
+ `o_custkey` int(11) DEFAULT NULL,
+ `o_orderstatus` char(1) DEFAULT NULL,
+ `o_totalprice` double DEFAULT NULL,
+ `o_orderDATE` date DEFAULT NULL,
+ `o_orderpriority` char(15) DEFAULT NULL,
+ `o_clerk` char(15) DEFAULT NULL,
+ `o_shippriority` int(11) DEFAULT NULL,
+ `o_comment` varchar(79) DEFAULT NULL,
+ PRIMARY KEY (`o_orderkey`),
+ KEY `i_o_orderdate` (`o_orderDATE`),
+ KEY `i_o_custkey` (`o_custkey`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+set use_sort_nest=1;
+explain SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 812 Using index condition; Using filesort
+1 SIMPLE orders eq_ref PRIMARY PRIMARY 4 dbt3.lineitem.l_orderkey 1
+SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+l_extendedprice o_orderkey o_totalprice l_shipDATE
+54559.5 1574 179923.54 1996-12-14
+53508.5 2342 104038.78 1996-08-28
+53458 1153 220727.97 1996-06-27
+53075.82 2721 59180.25 1996-02-14
+52830.33 1284 106122.38 1996-04-11
+52657.5 2276 141159.63 1996-07-13
+52290.84 4773 196080.26 1996-01-26
+51752.16 1607 166335.03 1996-02-22
+51751.35 1700 89143.36 1996-09-26
+51709.4 1254 94649.25 1996-03-07
+set use_sort_nest=0;
+explain SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 812 Using index condition; Using filesort
+1 SIMPLE orders eq_ref PRIMARY PRIMARY 4 dbt3.lineitem.l_orderkey 1
+SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+l_extendedprice o_orderkey o_totalprice l_shipDATE
+54559.5 1574 179923.54 1996-12-14
+53508.5 2342 104038.78 1996-08-28
+53458 1153 220727.97 1996-06-27
+53075.82 2721 59180.25 1996-02-14
+52830.33 1284 106122.38 1996-04-11
+52657.5 2276 141159.63 1996-07-13
+52290.84 4773 196080.26 1996-01-26
+51752.16 1607 166335.03 1996-02-22
+51751.35 1700 89143.36 1996-09-26
+51709.4 1254 94649.25 1996-03-07
+#
+# Using Filesort with Sort Nest
+#
+#
+# FILESORT USING SORT-NEST on (orders, customer)
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 20 Using index condition; Using where
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 10 Using filesort
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 sort-nest.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+2 Customer#000000101 1997-07-31 79189.58 7470.96 BRAZIL
+3 Customer#000000013 1997-07-31 125188.72 3857.34 CANADA
+9 Customer#000000130 1997-07-30 67979.49 5073.58 INDONESIA
+11 Customer#000000148 1997-07-29 88448.24 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+11 Customer#000000052 1997-07-28 46393.97 5630.28 IRAQ
+4 Customer#000000004 1997-07-25 11405.4 2866.83 EGYPT
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+18 Customer#000000082 1997-07-20 15082.82 9468.34 CHINA
+10 Customer#000000055 1997-07-16 46380.69 4572.11 IRAN
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 20 Using index condition; Using where; Using temporary; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+2 Customer#000000101 1997-07-31 79189.58 7470.96 BRAZIL
+3 Customer#000000013 1997-07-31 125188.72 3857.34 CANADA
+9 Customer#000000130 1997-07-30 67979.49 5073.58 INDONESIA
+11 Customer#000000148 1997-07-29 88448.24 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+11 Customer#000000052 1997-07-28 46393.97 5630.28 IRAQ
+4 Customer#000000004 1997-07-25 11405.4 2866.83 EGYPT
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+18 Customer#000000082 1997-07-20 15082.82 9468.34 CHINA
+10 Customer#000000055 1997-07-16 46380.69 4572.11 IRAN
+#
+# Sort-nest on table (lineitem, customer)
+#
+set use_sort_nest=1;
+explain SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_orderkey_quantity 4 NULL 330 Using index condition; Using where; Using temporary; Using filesort
+1 SIMPLE orders eq_ref PRIMARY,i_o_custkey PRIMARY 4 dbt3.lineitem.l_orderkey 1 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+c_acctbal c_name o_orderDate l_extendedprice l_orderkey
+1842.49 Customer#000000124 1997-11-06 54759.5 5857
+2135.6 Customer#000000148 1997-04-14 53909.8 5952
+4681.03 Customer#000000016 1998-07-06 53811.31 5761
+794.47 Customer#000000005 1997-04-23 53758.5 5859
+-234.12 Customer#000000125 1997-01-11 53468.31 5829
+5121.28 Customer#000000079 1998-05-31 53208 5633
+5744.59 Customer#000000046 1998-04-14 50770.37 5604
+-917.75 Customer#000000037 1997-07-13 50310.72 5793
+121.65 Customer#000000002 1998-05-28 49830.24 5507
+121.65 Customer#000000002 1998-05-28 49542.24 5507
+7470.96 Customer#000000101 1997-05-27 49411.82 5923
+5327.38 Customer#000000095 1997-10-04 48859.36 5505
+1842.49 Customer#000000124 1997-11-06 48661.41 5857
+4573.94 Customer#000000049 1997-02-14 48557.11 5762
+-646.64 Customer#000000064 1997-01-01 48219.92 5895
+-646.64 Customer#000000064 1997-01-01 48039.64 5895
+5121.28 Customer#000000079 1998-05-31 48004.8 5633
+9904.28 Customer#000000043 1998-02-06 47339.52 5671
+8959.65 Customer#000000149 1996-11-12 47247.52 5606
+5744.59 Customer#000000046 1998-04-14 45589.72 5604
+set use_sort_nest=0;
+explain SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_orderkey_quantity 4 NULL 330 Using index condition; Using where; Using temporary; Using filesort
+1 SIMPLE orders eq_ref PRIMARY,i_o_custkey PRIMARY 4 dbt3.lineitem.l_orderkey 1 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+c_acctbal c_name o_orderDate l_extendedprice l_orderkey
+1842.49 Customer#000000124 1997-11-06 54759.5 5857
+2135.6 Customer#000000148 1997-04-14 53909.8 5952
+4681.03 Customer#000000016 1998-07-06 53811.31 5761
+794.47 Customer#000000005 1997-04-23 53758.5 5859
+-234.12 Customer#000000125 1997-01-11 53468.31 5829
+5121.28 Customer#000000079 1998-05-31 53208 5633
+5744.59 Customer#000000046 1998-04-14 50770.37 5604
+-917.75 Customer#000000037 1997-07-13 50310.72 5793
+121.65 Customer#000000002 1998-05-28 49830.24 5507
+121.65 Customer#000000002 1998-05-28 49542.24 5507
+7470.96 Customer#000000101 1997-05-27 49411.82 5923
+5327.38 Customer#000000095 1997-10-04 48859.36 5505
+1842.49 Customer#000000124 1997-11-06 48661.41 5857
+4573.94 Customer#000000049 1997-02-14 48557.11 5762
+-646.64 Customer#000000064 1997-01-01 48219.92 5895
+-646.64 Customer#000000064 1997-01-01 48039.64 5895
+5121.28 Customer#000000079 1998-05-31 48004.8 5633
+9904.28 Customer#000000043 1998-02-06 47339.52 5671
+8959.65 Customer#000000149 1996-11-12 47247.52 5606
+5744.59 Customer#000000046 1998-04-14 45589.72 5604
+########################################################################
+#
+# Sort-nest on table (customer, orders, lineitem)
+#
+set use_sort_nest=1;
+explain SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE customer range PRIMARY,i_c_nationkey PRIMARY 4 NULL 7 Using index condition
+1 SIMPLE orders ref PRIMARY,i_o_custkey i_o_custkey 5 dbt3.customer.c_custkey 15 Using where
+1 SIMPLE lineitem ref PRIMARY,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 20 Using filesort
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 sort-nest.c_nationkey 1 Using index
+SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+l_extendedprice c_acctbal o_totalprice o_orderDate l_orderkey
+53758.5 794.47 210643.96 1997-04-23 5859
+49830.24 121.65 140363.7 1998-05-28 5507
+49542.24 121.65 140363.7 1998-05-28 5507
+48745.11 6819.74 122823.78 1993-04-05 5794
+47992.64 6819.74 140838.11 1998-06-26 5763
+47615.98 6819.74 182966.39 1994-07-17 5572
+46705.74 9561.95 101429.61 1993-04-21 5670
+44467.59 121.65 44777.63 1992-07-08 5893
+44442.3 6819.74 122823.78 1993-04-05 5794
+39723.6 794.47 210643.96 1997-04-23 5859
+37048.32 9561.95 95312.81 1992-03-28 5953
+36860.25 794.47 210643.96 1997-04-23 5859
+32996.16 6819.74 140838.11 1998-06-26 5763
+31416.68 6819.74 182966.39 1994-07-17 5572
+31219.32 794.47 210643.96 1997-04-23 5859
+31042.34 9561.95 95312.81 1992-03-28 5953
+29462.13 794.47 210643.96 1997-04-23 5859
+28948.59 6819.74 182966.39 1994-07-17 5572
+26732.43 9561.95 101429.61 1993-04-21 5670
+24590.68 9561.95 95312.81 1992-03-28 5953
+set use_sort_nest=0;
+explain SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE customer range PRIMARY,i_c_nationkey PRIMARY 4 NULL 7 Using index condition; Using where; Using temporary; Using filesort
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1 Using index
+1 SIMPLE orders ref PRIMARY,i_o_custkey i_o_custkey 5 dbt3.customer.c_custkey 15 Using where
+1 SIMPLE lineitem ref PRIMARY,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4
+SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+l_extendedprice c_acctbal o_totalprice o_orderDate l_orderkey
+53758.5 794.47 210643.96 1997-04-23 5859
+49830.24 121.65 140363.7 1998-05-28 5507
+49542.24 121.65 140363.7 1998-05-28 5507
+48745.11 6819.74 122823.78 1993-04-05 5794
+47992.64 6819.74 140838.11 1998-06-26 5763
+47615.98 6819.74 182966.39 1994-07-17 5572
+46705.74 9561.95 101429.61 1993-04-21 5670
+44467.59 121.65 44777.63 1992-07-08 5893
+44442.3 6819.74 122823.78 1993-04-05 5794
+39723.6 794.47 210643.96 1997-04-23 5859
+37048.32 9561.95 95312.81 1992-03-28 5953
+36860.25 794.47 210643.96 1997-04-23 5859
+32996.16 6819.74 140838.11 1998-06-26 5763
+31416.68 6819.74 182966.39 1994-07-17 5572
+31219.32 794.47 210643.96 1997-04-23 5859
+31042.34 9561.95 95312.81 1992-03-28 5953
+29462.13 794.47 210643.96 1997-04-23 5859
+28948.59 6819.74 182966.39 1994-07-17 5572
+26732.43 9561.95 101429.61 1993-04-21 5670
+24590.68 9561.95 95312.81 1992-03-28 5953
+drop database dbt3;
diff --git a/mysql-test/main/sort_nest_dbt3.test b/mysql-test/main/sort_nest_dbt3.test
new file mode 100644
index 00000000000..503bed0733a
--- /dev/null
+++ b/mysql-test/main/sort_nest_dbt3.test
@@ -0,0 +1,216 @@
+create database dbt3;
+use dbt3;
+--disable_query_log
+--source include/dbt3_s001.inc
+
+
+analyze table customer persistent for all;
+analyze table orders persistent for all;
+analyze table lineitem persistent for all;
+analyze table nation persistent for all;
+--enable_query_log
+
+--echo # done to avoid filter for now
+set optimizer_switch='rowid_filter=off';
+set use_sort_nest=1;
+
+--echo #
+--echo # USING INDEXES FOR ORDERING
+--echo #
+
+
+--echo #
+--echo # Using index scan on first table
+--echo #
+
+let $query= SELECT
+ c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+ FROM
+ orders, customer
+ WHERE
+ c_custkey= o_custkey
+ ORDER BY o_orderDATE DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+--echo #
+--echo # Using range access for customer with index on (c), this does
+--echo # the ordering
+--echo #
+
+alter table orders add key o_orderdate(o_orderDATE, o_custkey);
+
+let $query= SELECT
+ o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+ FROM
+ orders, customer
+ WHERE
+ c_custkey= o_custkey AND
+ o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ ORDER BY o_orderDATE, o_custkey DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+alter table orders drop key o_orderDate;
+
+###############################################################################
+
+--echo #
+--echo # USING FILESORT
+--echo #
+
+--echo #
+--echo # Filesort on first table (orders)
+--echo #
+
+let $query= SELECT
+ c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+ FROM
+ orders, customer, nation
+ WHERE
+ c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+ o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ ORDER BY o_totalprice DESC
+ LIMIT 10;
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+
+--echo #
+--echo # Filesort on first table (lineitem)
+--echo #
+
+show create table orders;
+
+let $query= SELECT
+ l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+ FROM
+ orders, lineitem
+ WHERE
+ o_orderkey = l_orderkey AND
+ l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+ LIMIT 10;
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+###############################################################################
+
+
+--echo #
+--echo # Using Filesort with Sort Nest
+--echo #
+
+--echo #
+--echo # FILESORT USING SORT-NEST on (orders, customer)
+--echo #
+
+let $query= SELECT
+ c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+ FROM
+ orders, customer, nation
+ WHERE
+ c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+ o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ ORDER BY o_orderDATE DESC, c_acctbal DESC
+ LIMIT 10;
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+
+
+--echo #
+--echo # Sort-nest on table (lineitem, customer)
+--echo #
+
+let $query= SELECT
+ c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+ FROM
+ orders, customer, lineitem
+ WHERE
+ c_custkey= o_custkey
+ AND
+ l_orderkey = o_orderkey
+ AND
+ l_orderkey between 5500 AND 6000
+ AND
+ l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+ LIMIT 20;
+
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+--echo ########################################################################
+
+--echo #
+--echo # Sort-nest on table (customer, orders, lineitem)
+--echo #
+
+let $query= SELECT
+ l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+ FROM
+ orders, customer, lineitem, nation
+ WHERE
+ c_custkey= o_custkey
+ AND
+ l_orderkey = o_orderkey
+ AND
+ n_nationkey = c_nationkey
+ AND
+ l_orderkey between 5500 AND 6000
+ AND
+ c_custkey between 1 and 10
+ ORDER BY
+ l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+ LIMIT 20;
+
+
+set use_sort_nest=1;
+eval explain $query;
+eval $query;
+
+set use_sort_nest=0;
+eval explain $query;
+eval $query;
+
+
+drop database dbt3;
diff --git a/mysql-test/main/sort_nest_dbt3_innodb.result b/mysql-test/main/sort_nest_dbt3_innodb.result
new file mode 100644
index 00000000000..9c4c8144225
--- /dev/null
+++ b/mysql-test/main/sort_nest_dbt3_innodb.result
@@ -0,0 +1,611 @@
+SET SESSION STORAGE_ENGINE='InnoDB';
+create database dbt3;
+use dbt3;
+Table Op Msg_type Msg_text
+dbt3.customer analyze status Engine-independent statistics collected
+dbt3.customer analyze status OK
+Table Op Msg_type Msg_text
+dbt3.orders analyze status Engine-independent statistics collected
+dbt3.orders analyze status OK
+Table Op Msg_type Msg_text
+dbt3.lineitem analyze status Engine-independent statistics collected
+dbt3.lineitem analyze status OK
+Table Op Msg_type Msg_text
+dbt3.nation analyze status Engine-independent statistics collected
+dbt3.nation analyze status OK
+# done to avoid filter for now
+set optimizer_switch='rowid_filter=off';
+set use_sort_nest=1;
+#
+# USING INDEXES FOR ORDERING
+#
+#
+# Using index scan on first table
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders index i_o_custkey i_o_orderdate 4 NULL 5 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal
+16 Customer#000000088 1998-08-02 131752.07 8031.44
+0 Customer#000000080 1998-07-30 141858.97 7383.53
+10 Customer#000000049 1998-07-29 37776.79 4573.94
+3 Customer#000000022 1998-07-28 139104.17 591.98
+3 Customer#000000022 1998-07-27 82746.74 591.98
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders index i_o_custkey i_o_orderdate 4 NULL 5 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey
+ORDER BY o_orderDATE DESC
+LIMIT 5;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal
+16 Customer#000000088 1998-08-02 131752.07 8031.44
+0 Customer#000000080 1998-07-30 141858.97 7383.53
+10 Customer#000000049 1998-07-29 37776.79 4573.94
+3 Customer#000000022 1998-07-28 139104.17 591.98
+3 Customer#000000022 1998-07-27 82746.74 591.98
+#
+# Using range access for customer with index on (c), this does
+# the ordering
+#
+alter table orders add key o_orderdate(o_orderDATE, o_custkey);
+set use_sort_nest=1;
+explain SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey,o_orderdate i_o_orderdate 4 NULL 217 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+o_custkey c_name o_orderDate o_totalprice c_acctbal
+130 Customer#000000130 1997-07-30 67979.49 5073.58
+101 Customer#000000101 1997-07-31 79189.58 7470.96
+13 Customer#000000013 1997-07-31 125188.72 3857.34
+64 Customer#000000064 1997-08-03 76164.41 -646.64
+50 Customer#000000050 1997-08-03 20791.5 4266.13
+set use_sort_nest=0;
+explain SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey,o_orderdate i_o_orderdate 4 NULL 217 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+o_custkey, c_name, o_orderDate, o_totalprice, c_acctbal
+FROM
+orders, customer
+WHERE
+c_custkey= o_custkey AND
+o_orderDATE >='1997-07-30' and o_orderDATE <= '1998-07-30'
+ORDER BY o_orderDATE, o_custkey DESC
+LIMIT 5;
+o_custkey c_name o_orderDate o_totalprice c_acctbal
+130 Customer#000000130 1997-07-30 67979.49 5073.58
+101 Customer#000000101 1997-07-31 79189.58 7470.96
+13 Customer#000000013 1997-07-31 125188.72 3857.34
+64 Customer#000000064 1997-08-03 76164.41 -646.64
+50 Customer#000000050 1997-08-03 20791.5 4266.13
+alter table orders drop key o_orderDate;
+#
+# USING FILESORT
+#
+#
+# Filesort on first table (orders)
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 201 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+5 Customer#000000010 1997-04-04 258779.02 2753.54 ETHIOPIA
+0 Customer#000000076 1997-08-24 231831.35 5745.33 ALGERIA
+9 Customer#000000094 1997-10-17 224382.57 5500.11 INDONESIA
+6 Customer#000000046 1997-05-26 216826.73 5744.59 FRANCE
+3 Customer#000000005 1997-04-23 210643.96 794.47 CANADA
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+3 Customer#000000005 1997-11-09 190142.17 794.47 CANADA
+9 Customer#000000094 1997-11-30 187516.29 5500.11 INDONESIA
+3 Customer#000000005 1997-11-12 185496.66 794.47 CANADA
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 201 Using index condition; Using where; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-01-30' and o_orderDATE <= '1997-12-31'
+ORDER BY o_totalprice DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+5 Customer#000000010 1997-04-04 258779.02 2753.54 ETHIOPIA
+0 Customer#000000076 1997-08-24 231831.35 5745.33 ALGERIA
+9 Customer#000000094 1997-10-17 224382.57 5500.11 INDONESIA
+6 Customer#000000046 1997-05-26 216826.73 5744.59 FRANCE
+3 Customer#000000005 1997-04-23 210643.96 794.47 CANADA
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+3 Customer#000000005 1997-11-09 190142.17 794.47 CANADA
+9 Customer#000000094 1997-11-30 187516.29 5500.11 INDONESIA
+3 Customer#000000005 1997-11-12 185496.66 794.47 CANADA
+#
+# Filesort on first table (lineitem)
+#
+show create table orders;
+Table Create Table
+orders CREATE TABLE `orders` (
+ `o_orderkey` int(11) NOT NULL,
+ `o_custkey` int(11) DEFAULT NULL,
+ `o_orderstatus` char(1) DEFAULT NULL,
+ `o_totalprice` double DEFAULT NULL,
+ `o_orderDATE` date DEFAULT NULL,
+ `o_orderpriority` char(15) DEFAULT NULL,
+ `o_clerk` char(15) DEFAULT NULL,
+ `o_shippriority` int(11) DEFAULT NULL,
+ `o_comment` varchar(79) DEFAULT NULL,
+ PRIMARY KEY (`o_orderkey`),
+ KEY `i_o_orderdate` (`o_orderDATE`),
+ KEY `i_o_custkey` (`o_custkey`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+set use_sort_nest=1;
+explain SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 910 Using index condition; Using filesort
+1 SIMPLE orders eq_ref PRIMARY PRIMARY 4 dbt3.lineitem.l_orderkey 1
+SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+l_extendedprice o_orderkey o_totalprice l_shipDATE
+54559.5 1574 179923.54 1996-12-14
+53508.5 2342 104038.78 1996-08-28
+53458 1153 220727.97 1996-06-27
+53075.82 2721 59180.25 1996-02-14
+52830.33 1284 106122.38 1996-04-11
+52657.5 2276 141159.63 1996-07-13
+52290.84 4773 196080.26 1996-01-26
+51752.16 1607 166335.03 1996-02-22
+51751.35 1700 89143.36 1996-09-26
+51709.4 1254 94649.25 1996-03-07
+set use_sort_nest=0;
+explain SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 NULL 910 Using index condition; Using filesort
+1 SIMPLE orders eq_ref PRIMARY PRIMARY 4 dbt3.lineitem.l_orderkey 1
+SELECT
+l_extendedprice, o_orderkey, o_totalprice, l_shipDATE
+FROM
+orders, lineitem
+WHERE
+o_orderkey = l_orderkey AND
+l_shipDATE >= '1996-01-01' AND l_shipDATE <= '1996-12-31'
+ ORDER BY l_extendedprice DESC
+LIMIT 10;
+l_extendedprice o_orderkey o_totalprice l_shipDATE
+54559.5 1574 179923.54 1996-12-14
+53508.5 2342 104038.78 1996-08-28
+53458 1153 220727.97 1996-06-27
+53075.82 2721 59180.25 1996-02-14
+52830.33 1284 106122.38 1996-04-11
+52657.5 2276 141159.63 1996-07-13
+52290.84 4773 196080.26 1996-01-26
+51752.16 1607 166335.03 1996-02-22
+51751.35 1700 89143.36 1996-09-26
+51709.4 1254 94649.25 1996-03-07
+#
+# Using Filesort with Sort Nest
+#
+#
+# FILESORT USING SORT-NEST on (orders, customer)
+#
+set use_sort_nest=1;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 20 Using index condition; Using where
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 10 Using filesort
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 sort-nest.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+2 Customer#000000101 1997-07-31 79189.58 7470.96 BRAZIL
+3 Customer#000000013 1997-07-31 125188.72 3857.34 CANADA
+9 Customer#000000130 1997-07-30 67979.49 5073.58 INDONESIA
+11 Customer#000000148 1997-07-29 88448.24 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+11 Customer#000000052 1997-07-28 46393.97 5630.28 IRAQ
+4 Customer#000000004 1997-07-25 11405.4 2866.83 EGYPT
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+18 Customer#000000082 1997-07-20 15082.82 9468.34 CHINA
+10 Customer#000000055 1997-07-16 46380.69 4572.11 IRAN
+set use_sort_nest=0;
+explain SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range i_o_orderdate,i_o_custkey i_o_orderdate 4 NULL 20 Using index condition; Using where; Using temporary; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1
+SELECT
+c_nationkey, c_name, o_orderDate, o_totalprice, c_acctbal, n_name
+FROM
+orders, customer, nation
+WHERE
+c_custkey= o_custkey AND c_nationkey=n_nationkey AND
+o_orderDATE >='1997-07-01' and o_orderDATE <= '1997-07-31'
+ORDER BY o_orderDATE DESC, c_acctbal DESC
+LIMIT 10;
+c_nationkey c_name o_orderDate o_totalprice c_acctbal n_name
+2 Customer#000000101 1997-07-31 79189.58 7470.96 BRAZIL
+3 Customer#000000013 1997-07-31 125188.72 3857.34 CANADA
+9 Customer#000000130 1997-07-30 67979.49 5073.58 INDONESIA
+11 Customer#000000148 1997-07-29 88448.24 2135.6 IRAQ
+2 Customer#000000101 1997-07-28 204163.1 7470.96 BRAZIL
+11 Customer#000000052 1997-07-28 46393.97 5630.28 IRAQ
+4 Customer#000000004 1997-07-25 11405.4 2866.83 EGYPT
+11 Customer#000000148 1997-07-25 206179.68 2135.6 IRAQ
+18 Customer#000000082 1997-07-20 15082.82 9468.34 CHINA
+10 Customer#000000055 1997-07-16 46380.69 4572.11 IRAN
+#
+# Sort-nest on table (lineitem, customer)
+#
+set use_sort_nest=1;
+explain SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range PRIMARY,i_o_custkey PRIMARY 4 NULL 125 Using where; Using temporary; Using filesort
+1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4 Using where
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+c_acctbal c_name o_orderDate l_extendedprice l_orderkey
+1842.49 Customer#000000124 1997-11-06 54759.5 5857
+2135.6 Customer#000000148 1997-04-14 53909.8 5952
+4681.03 Customer#000000016 1998-07-06 53811.31 5761
+794.47 Customer#000000005 1997-04-23 53758.5 5859
+-234.12 Customer#000000125 1997-01-11 53468.31 5829
+5121.28 Customer#000000079 1998-05-31 53208 5633
+5744.59 Customer#000000046 1998-04-14 50770.37 5604
+-917.75 Customer#000000037 1997-07-13 50310.72 5793
+121.65 Customer#000000002 1998-05-28 49830.24 5507
+121.65 Customer#000000002 1998-05-28 49542.24 5507
+7470.96 Customer#000000101 1997-05-27 49411.82 5923
+5327.38 Customer#000000095 1997-10-04 48859.36 5505
+1842.49 Customer#000000124 1997-11-06 48661.41 5857
+4573.94 Customer#000000049 1997-02-14 48557.11 5762
+-646.64 Customer#000000064 1997-01-01 48219.92 5895
+-646.64 Customer#000000064 1997-01-01 48039.64 5895
+5121.28 Customer#000000079 1998-05-31 48004.8 5633
+9904.28 Customer#000000043 1998-02-06 47339.52 5671
+8959.65 Customer#000000149 1996-11-12 47247.52 5606
+5744.59 Customer#000000046 1998-04-14 45589.72 5604
+set use_sort_nest=0;
+explain SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range PRIMARY,i_o_custkey PRIMARY 4 NULL 125 Using where; Using temporary; Using filesort
+1 SIMPLE customer eq_ref PRIMARY PRIMARY 4 dbt3.orders.o_custkey 1
+1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4 Using where
+SELECT
+c_acctbal, c_name, o_orderDate, l_extendedprice, l_orderkey
+FROM
+orders, customer, lineitem
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+l_shipDATE >= '1997-01-01' and l_shipDATE <= '1998-08-10'
+ ORDER BY l_extendedprice DESC, c_acctbal DESC
+LIMIT 20;
+c_acctbal c_name o_orderDate l_extendedprice l_orderkey
+1842.49 Customer#000000124 1997-11-06 54759.5 5857
+2135.6 Customer#000000148 1997-04-14 53909.8 5952
+4681.03 Customer#000000016 1998-07-06 53811.31 5761
+794.47 Customer#000000005 1997-04-23 53758.5 5859
+-234.12 Customer#000000125 1997-01-11 53468.31 5829
+5121.28 Customer#000000079 1998-05-31 53208 5633
+5744.59 Customer#000000046 1998-04-14 50770.37 5604
+-917.75 Customer#000000037 1997-07-13 50310.72 5793
+121.65 Customer#000000002 1998-05-28 49830.24 5507
+121.65 Customer#000000002 1998-05-28 49542.24 5507
+7470.96 Customer#000000101 1997-05-27 49411.82 5923
+5327.38 Customer#000000095 1997-10-04 48859.36 5505
+1842.49 Customer#000000124 1997-11-06 48661.41 5857
+4573.94 Customer#000000049 1997-02-14 48557.11 5762
+-646.64 Customer#000000064 1997-01-01 48219.92 5895
+-646.64 Customer#000000064 1997-01-01 48039.64 5895
+5121.28 Customer#000000079 1998-05-31 48004.8 5633
+9904.28 Customer#000000043 1998-02-06 47339.52 5671
+8959.65 Customer#000000149 1996-11-12 47247.52 5606
+5744.59 Customer#000000046 1998-04-14 45589.72 5604
+########################################################################
+#
+# Sort-nest on table (customer, orders, lineitem)
+#
+set use_sort_nest=1;
+explain SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range PRIMARY,i_o_custkey PRIMARY 4 NULL 125 Using where
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1
+1 SIMPLE lineitem ref PRIMARY,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4
+1 SIMPLE <sort-nest> ALL NULL NULL NULL NULL 20 Using filesort
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 sort-nest.c_nationkey 1 Using index
+SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+l_extendedprice c_acctbal o_totalprice o_orderDate l_orderkey
+53758.5 794.47 210643.96 1997-04-23 5859
+49830.24 121.65 140363.7 1998-05-28 5507
+49542.24 121.65 140363.7 1998-05-28 5507
+48745.11 6819.74 122823.78 1993-04-05 5794
+47992.64 6819.74 140838.11 1998-06-26 5763
+47615.98 6819.74 182966.39 1994-07-17 5572
+46705.74 9561.95 101429.61 1993-04-21 5670
+44467.59 121.65 44777.63 1992-07-08 5893
+44442.3 6819.74 122823.78 1993-04-05 5794
+39723.6 794.47 210643.96 1997-04-23 5859
+37048.32 9561.95 95312.81 1992-03-28 5953
+36860.25 794.47 210643.96 1997-04-23 5859
+32996.16 6819.74 140838.11 1998-06-26 5763
+31416.68 6819.74 182966.39 1994-07-17 5572
+31219.32 794.47 210643.96 1997-04-23 5859
+31042.34 9561.95 95312.81 1992-03-28 5953
+29462.13 794.47 210643.96 1997-04-23 5859
+28948.59 6819.74 182966.39 1994-07-17 5572
+26732.43 9561.95 101429.61 1993-04-21 5670
+24590.68 9561.95 95312.81 1992-03-28 5953
+set use_sort_nest=0;
+explain SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE orders range PRIMARY,i_o_custkey PRIMARY 4 NULL 125 Using where; Using temporary; Using filesort
+1 SIMPLE customer eq_ref PRIMARY,i_c_nationkey PRIMARY 4 dbt3.orders.o_custkey 1 Using where
+1 SIMPLE nation eq_ref PRIMARY PRIMARY 4 dbt3.customer.c_nationkey 1 Using index
+1 SIMPLE lineitem ref PRIMARY,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 dbt3.orders.o_orderkey 4
+SELECT
+l_extendedprice, c_acctbal, o_totalprice, o_orderDate, l_orderkey
+FROM
+orders, customer, lineitem, nation
+WHERE
+c_custkey= o_custkey
+AND
+l_orderkey = o_orderkey
+AND
+n_nationkey = c_nationkey
+AND
+l_orderkey between 5500 AND 6000
+AND
+c_custkey between 1 and 10
+ORDER BY
+l_extendedprice DESC, c_acctbal DESC, o_totalprice DESC
+LIMIT 20;
+l_extendedprice c_acctbal o_totalprice o_orderDate l_orderkey
+53758.5 794.47 210643.96 1997-04-23 5859
+49830.24 121.65 140363.7 1998-05-28 5507
+49542.24 121.65 140363.7 1998-05-28 5507
+48745.11 6819.74 122823.78 1993-04-05 5794
+47992.64 6819.74 140838.11 1998-06-26 5763
+47615.98 6819.74 182966.39 1994-07-17 5572
+46705.74 9561.95 101429.61 1993-04-21 5670
+44467.59 121.65 44777.63 1992-07-08 5893
+44442.3 6819.74 122823.78 1993-04-05 5794
+39723.6 794.47 210643.96 1997-04-23 5859
+37048.32 9561.95 95312.81 1992-03-28 5953
+36860.25 794.47 210643.96 1997-04-23 5859
+32996.16 6819.74 140838.11 1998-06-26 5763
+31416.68 6819.74 182966.39 1994-07-17 5572
+31219.32 794.47 210643.96 1997-04-23 5859
+31042.34 9561.95 95312.81 1992-03-28 5953
+29462.13 794.47 210643.96 1997-04-23 5859
+28948.59 6819.74 182966.39 1994-07-17 5572
+26732.43 9561.95 101429.61 1993-04-21 5670
+24590.68 9561.95 95312.81 1992-03-28 5953
+drop database dbt3;
diff --git a/mysql-test/main/sort_nest_dbt3_innodb.test b/mysql-test/main/sort_nest_dbt3_innodb.test
new file mode 100644
index 00000000000..d72329eaab6
--- /dev/null
+++ b/mysql-test/main/sort_nest_dbt3_innodb.test
@@ -0,0 +1,6 @@
+--source include/have_innodb.inc
+
+SET SESSION STORAGE_ENGINE='InnoDB';
+
+--source sort_nest_dbt3.test
+
diff --git a/mysql-test/main/sort_nest_index.result b/mysql-test/main/sort_nest_index.result
new file mode 100644
index 00000000000..7dae6c6c93f
--- /dev/null
+++ b/mysql-test/main/sort_nest_index.result
@@ -0,0 +1,342 @@
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+insert into t1 values (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+insert into t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+#
+# index a_b should be used, no need for filesort
+#
+set use_sort_nest= 1;
+select a,b,c from t1 where a=1 and c=2 order by b limit 5;
+a b c
+1 7 2
+1 7 2
+1 8 2
+1 8 2
+1 9 2
+explain select a,b,c from t1 where a=1 and c=2 order by b limit 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a_b,a_c a_b 5 NULL 10 Using index condition; Using where
+set use_sort_nest= 0;
+explain select a,b,c from t1 where a=1 and c=2 order by b limit 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a_b,a_c a_b 5 NULL 18 Using where
+drop table t1;
+#
+# Tests where Index(scan, ref or range access) satisfies the ORDERING
+#
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+insert into t1 values (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+insert into t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,1,2);
+# index key a_b, no need for filesort
+set optimizer_trace=1;
+set use_sort_nest=1;
+select a,b,c from t1 where a=1 and c=2 order by b limit 10;
+a b c
+1 1 2
+1 7 2
+1 7 2
+1 8 2
+1 8 2
+1 9 2
+1 9 2
+1 10 2
+1 10 2
+1 11 2
+explain select a,b,c from t1 where a=1 and c=2 order by b limit 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a_b,a_c a_b 5 NULL 19 Using index condition; Using where
+set use_sort_nest=0;
+explain select a,b,c from t1 where a=1 and c=2 order by b limit 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a_b,a_c a_b 5 NULL 19 Using where
+drop table t1;
+CREATE TABLE t1(
+a int NOT NULL,
+b char NULL,
+PRIMARY KEY(a)
+);
+INSERT INTO t1 VALUES (1,'a'), (2,'b'), (3,'c'), (4,'d');
+#
+# Should use index condition
+#
+set use_sort_nest= 1;
+SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a
+LIMIT 2;
+a b
+1 a
+2 b
+EXPLAIN SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a
+LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using index condition
+set use_sort_nest= 0;
+EXPLAIN SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a
+LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using index condition
+#
+# Should not use index condition as ORDER by DESC is used
+#
+set use_sort_nest= 1;
+EXPLAIN SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a DESC
+LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where
+SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a DESC
+LIMIT 2;
+a b
+2 b
+1 a
+set use_sort_nest= 0;
+EXPLAIN SELECT * FROM t1
+WHERE a BETWEEN 1 and 2
+ORDER BY a DESC
+LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where
+drop table t1;
+create table t1(a int, b int, c int, key(a), key a_b(a,b));
+insert into t1 values (0,1,0), (0,2,0), (0,3,0);
+insert into t1 values (1,6,1), (1,7,1), (1,5,1);
+insert into t1 values (2,8,2), (2,9,3), (2,10,4);
+insert into t1 values (3,1,5);
+create table t2(a int, b int, c int, key(b), key(c));
+insert into t2 select a, b, c from t1;
+#
+# Testing using of Indexes on first non-const table
+#
+#
+# Using range scan
+#
+set use_sort_nest= 1;
+SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.b > 8 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+a b c a b c
+2 9 3 2 9 3
+2 10 4 2 10 4
+EXPLAIN SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.b > 8 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a,a_b a_b 10 NULL 2 Using index condition
+1 SIMPLE t2 ref b b 5 test.t1.b 1
+set use_sort_nest= 0;
+EXPLAIN SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.b > 8 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range a,a_b a_b 10 NULL 2 Using index condition
+1 SIMPLE t2 ref b b 5 test.t1.b 1
+#
+# Using ref access
+#
+set use_sort_nest= 1;
+SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.c >= 1 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+a b c a b c
+2 8 2 2 8 2
+2 9 3 2 9 3
+2 10 4 2 10 4
+EXPLAIN SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.c >= 1 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref a,a_b a_b 5 const 3 Using index condition; Using where
+1 SIMPLE t2 ref b,c b 5 test.t1.b 1 Using where
+set use_sort_nest= 0;
+EXPLAIN SELECT *
+FROM
+t1,t2
+WHERE
+t1.a=2 AND t2.c >= 1 AND
+t1.b=t2.b
+ORDER BY t1.b LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref a,a_b a_b 5 const 3 Using where
+1 SIMPLE t2 ref b,c b 5 test.t1.b 1 Using where
+drop table t1,t2;
+# TESTS with INDEX HINTS
+set use_sort_nest=1;
+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, key idx1(a), key idx2(a,b), key idx3(c));
+insert into t1 select a,a,a from t0 where a <5;
+analyze table t1 persistent for all;
+#
+# Index idx1 to be used for index scan
+#
+set use_sort_nest=1;
+SELECT * from t1 where b > 0 order by t1.a limit 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 where b > 0 order by t1.a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx1 5 NULL 2 Using where
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 where b > 0 order by t1.a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx1 5 NULL 2 Using where
+#
+# Index idx2 to be used for index scan(USE INDEX is used)
+#
+set use_sort_nest=1;
+SELECT * from t1 USE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 USE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 USE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+#
+# Index idx2 to be used for index scan(USE INDEX for ORDER BY is used)
+#
+set use_sort_nest=1;
+SELECT * from t1 USE INDEX FOR ORDER BY(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 USE INDEX FOR ORDER BY(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 USE INDEX FOR ORDER BY(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+#
+# Use Filesort as idx3 does not resolve ORDER BY clause
+#
+set use_sort_nest=1;
+SELECT * from t1 USE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 USE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where; Using filesort
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 USE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where; Using filesort
+#
+# Using index idx2 as idx1 is ignored
+#
+set use_sort_nest=1;
+SELECT * from t1 IGNORE INDEX(idx1)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 IGNORE INDEX(idx1)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 IGNORE INDEX(idx1)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+#
+# Use index idx2 for sorting, it is forced here
+#
+set use_sort_nest=1;
+SELECT * from t1 FORCE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 FORCE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 FORCE INDEX(idx2)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL idx2 10 NULL 2 Using where
+#
+# Use FILESORT as idx3 cannot resolve ORDER BY clause
+#
+set use_sort_nest=1;
+SELECT * from t1 FORCE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+a b c
+1 1 1
+2 2 2
+EXPLAIN SELECT * from t1 FORCE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where; Using filesort
+set use_sort_nest=0;
+EXPLAIN SELECT * from t1 FORCE INDEX FOR ORDER BY(idx3)
+WHERE b > 0
+ORDER BY t1.a LIMIT 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where; Using filesort
+drop table t0,t1;
diff --git a/mysql-test/main/sort_nest_index.test b/mysql-test/main/sort_nest_index.test
new file mode 100644
index 00000000000..08856fc1723
--- /dev/null
+++ b/mysql-test/main/sort_nest_index.test
@@ -0,0 +1,249 @@
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+
+insert into t1 values (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+insert into t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+
+--echo #
+--echo # index a_b should be used, no need for filesort
+--echo #
+
+let $query= select a,b,c from t1 where a=1 and c=2 order by b limit 5;
+set use_sort_nest= 1;
+eval $query;
+eval explain $query;
+
+set use_sort_nest= 0;
+eval explain $query;
+drop table t1;
+
+--echo #
+--echo # Tests where Index(scan, ref or range access) satisfies the ORDERING
+--echo #
+
+CREATE TABLE t1 (a int, b int, c int, KEY a_b (a,b), KEY a_c (a,c));
+
+insert into t1 values (0,1,0), (0,2,0), (0,3,0), (0,4,0), (0,5,0), (0,6,0);
+insert into t1 values (1,7,1), (1,8,1), (1,9,1), (1,10,1), (1,11,1), (1,12,1);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,7,2), (1,8,2), (1,9,2), (1,10,2), (1,11,2), (1,12,2);
+insert into t1 values (1,1,2);
+
+--echo # index key a_b, no need for filesort
+
+let $query= select a,b,c from t1 where a=1 and c=2 order by b limit 10;
+set optimizer_trace=1;
+
+set use_sort_nest=1;
+eval $query;
+eval explain $query;
+
+set use_sort_nest=0;
+eval explain $query;
+
+drop table t1;
+
+CREATE TABLE t1(
+ a int NOT NULL,
+ b char NULL,
+ PRIMARY KEY(a)
+);
+
+INSERT INTO t1 VALUES (1,'a'), (2,'b'), (3,'c'), (4,'d');
+
+--echo #
+--echo # Should use index condition
+--echo #
+
+let $query= SELECT * FROM t1
+ WHERE a BETWEEN 1 and 2
+ ORDER BY a
+ LIMIT 2;
+
+set use_sort_nest= 1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest= 0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Should not use index condition as ORDER by DESC is used
+--echo #
+
+let $query= SELECT * FROM t1
+ WHERE a BETWEEN 1 and 2
+ ORDER BY a DESC
+ LIMIT 2;
+
+set use_sort_nest= 1;
+eval EXPLAIN $query;
+eval $query;
+
+set use_sort_nest= 0;
+eval EXPLAIN $query;
+
+drop table t1;
+
+create table t1(a int, b int, c int, key(a), key a_b(a,b)); # 10 rows
+insert into t1 values (0,1,0), (0,2,0), (0,3,0);
+insert into t1 values (1,6,1), (1,7,1), (1,5,1);
+insert into t1 values (2,8,2), (2,9,3), (2,10,4);
+insert into t1 values (3,1,5);
+
+create table t2(a int, b int, c int, key(b), key(c)); # 10 rows
+insert into t2 select a, b, c from t1;
+
+--echo #
+--echo # Testing using of Indexes on first non-const table
+--echo #
+
+--echo #
+--echo # Using range scan
+--echo #
+let $query= SELECT *
+ FROM
+ t1,t2
+ WHERE
+ t1.a=2 AND t2.b > 8 AND
+ t1.b=t2.b
+ ORDER BY t1.b LIMIT 10;
+
+set use_sort_nest= 1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest= 0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Using ref access
+--echo #
+let $query= SELECT *
+ FROM
+ t1,t2
+ WHERE
+ t1.a=2 AND t2.c >= 1 AND
+ t1.b=t2.b
+ ORDER BY t1.b LIMIT 10;
+
+set use_sort_nest= 1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest= 0;
+eval EXPLAIN $query;
+
+drop table t1,t2;
+
+--echo # TESTS with INDEX HINTS
+
+set use_sort_nest=1;
+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, key idx1(a), key idx2(a,b), key idx3(c));
+insert into t1 select a,a,a from t0 where a <5; # 5 rows
+
+--disable_result_log
+analyze table t1 persistent for all;
+--enable_result_log
+
+--echo #
+--echo # Index idx1 to be used for index scan
+--echo #
+
+let $query= SELECT * from t1 where b > 0 order by t1.a limit 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Index idx2 to be used for index scan(USE INDEX is used)
+--echo #
+
+let $query= SELECT * from t1 USE INDEX(idx2)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Index idx2 to be used for index scan(USE INDEX for ORDER BY is used)
+--echo #
+
+let $query= SELECT * from t1 USE INDEX FOR ORDER BY(idx2)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Use Filesort as idx3 does not resolve ORDER BY clause
+--echo #
+
+let $query= SELECT * from t1 USE INDEX FOR ORDER BY(idx3)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Using index idx2 as idx1 is ignored
+--echo #
+
+let $query= SELECT * from t1 IGNORE INDEX(idx1)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Use index idx2 for sorting, it is forced here
+--echo #
+
+let $query= SELECT * from t1 FORCE INDEX(idx2)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+--echo #
+--echo # Use FILESORT as idx3 cannot resolve ORDER BY clause
+--echo #
+
+let $query= SELECT * from t1 FORCE INDEX FOR ORDER BY(idx3)
+ WHERE b > 0
+ ORDER BY t1.a LIMIT 2;
+
+set use_sort_nest=1;
+eval $query;
+eval EXPLAIN $query;
+
+set use_sort_nest=0;
+eval EXPLAIN $query;
+
+drop table t0,t1;
diff --git a/mysql-test/main/sort_nest_sj.result b/mysql-test/main/sort_nest_sj.result
new file mode 100644
index 00000000000..73405cd8832
--- /dev/null
+++ b/mysql-test/main/sort_nest_sj.result
@@ -0,0 +1,697 @@
+#
+# SORT-NEST WITH SEMI JOINS
+#
+
+# MERGED SEMI-JOINS
+
+# SEMI JOIN MATERIALIZATION SCAN with SORT-NEST
+CREATE TABLE t0(a int);
+CREATE TABLE t1 (a int, b int, c int);
+CREATE TABLE t2 (a int, b int, c int);
+CREATE TABLE t3 (a int, b int, c int, key(a));
+CREATE TABLE t4 (a int, b int, c int, key(a));
+INSERT INTO t0 SELECT seq-1 FROM seq_1_to_10;
+INSERT INTO t1 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+INSERT INTO t2 SELECT a,a,a FROM t0;
+INSERT INTO t3 SELECT a,a,a FROM t0;
+INSERT INTO t4 SELECT a,a,a FROM t0;
+ANALYZE TABLE t0 PERSISTENT FOR ALL;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+ANALYZE TABLE t4 PERSISTENT FOR ALL;
+# SJM scan inside the sort-nest
+# sort-nest includes (t2, <subquery2>)
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t1.b DESC ,t2.b DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 10
+1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 3
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where
+2 MATERIALIZED t4 range a a 5 NULL 3 Using where; Using index
+2 MATERIALIZED t3 ref a a 5 test.t4.a 1
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t1.b DESC ,t2.b DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100
+ },
+ "table": {
+ "table_name": "<subquery2>",
+ "access_type": "ALL",
+ "possible_keys": ["distinct_key"],
+ "rows": 3,
+ "filtered": 100,
+ "materialized": {
+ "unique": 1,
+ "query_block": {
+ "select_id": 2,
+ "table": {
+ "table_name": "t4",
+ "access_type": "range",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "rows": 3,
+ "filtered": 100,
+ "attached_condition": "t4.a < 3 and t4.a is not null",
+ "using_index": true
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t4.a"],
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ }
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.b desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100,
+ "attached_condition": "t1.a = `sort-nest`.a and t1.b = `sort-nest`.b"
+ }
+ }
+}
+SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t1.b DESC ,t2.b DESC
+LIMIT 5;
+a a b b
+2 2 2 2
+1 1 1 1
+0 0 0 0
+set use_sort_nest=0;
+SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t1.b DESC ,t2.b DESC
+LIMIT 5;
+a a b b
+2 2 2 2
+1 1 1 1
+0 0 0 0
+#
+# SJM scan table is the first table inside the sort-nest
+#
+alter table t2 add key(b);
+set use_sort_nest=1;
+EXPLAIN SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND t2.b < 5 AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 3
+1 PRIMARY t2 ALL b NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join)
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where
+2 MATERIALIZED t4 range a a 5 NULL 3 Using where; Using index
+2 MATERIALIZED t3 ref a a 5 test.t4.a 1
+SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND t2.b < 5 AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+a a b b
+2 2 2 2
+1 1 1 1
+0 0 0 0
+set use_sort_nest= 0;
+SELECT t1.a, t2.a, t1.b,t2.b
+FROM t1, t2
+WHERE t1.a=t2.a AND t2.b < 5 AND
+t1.b IN (SELECT t3.b FROM t3,t4
+WHERE t3.a < 3 AND t3.a=t4.a)
+ORDER BY t2.b DESC, t1.b DESC
+LIMIT 5;
+a a b b
+2 2 2 2
+1 1 1 1
+0 0 0 0
+DROP TABLE t0, t1, t2, t3, t4;
+#
+# SJM Lookup with sort-nest, where SJM lookup table is outside the
+# sort-nest
+#
+create table t1 (a int, b int, c int, key(a));
+create table t2 (a int, b int, c int, key(c));
+create table t3 (a int, b int, c int, key(a));
+create table t4 (a int, b int, c int);
+INSERT INTO t1 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_10;
+INSERT INTO t2 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_1000;
+INSERT INTO t4 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+ANALYZE TABLE t4 PERSISTENT FOR ALL;
+set use_sort_nest= 1;
+EXPLAIN SELECT t1.a, t2.a, t2.b
+FROM t1, t2
+WHERE t2.a in (SELECT t3.b from t3)
+AND t1.a= t2.b
+AND t1.a < 5
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL a NULL NULL NULL 10 Using where
+1 PRIMARY t2 ALL NULL NULL NULL NULL 100 Using where; Using join buffer (flat, BNL join)
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
+2 MATERIALIZED t3 ALL NULL NULL NULL NULL 1000
+EXPLAIN FORMAT=JSON SELECT t1.a, t2.a, t2.b
+FROM t1, t2
+WHERE t2.a in (SELECT t3.b from t3)
+AND t1.a= t2.b
+AND t1.a < 5
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "possible_keys": ["a"],
+ "rows": 10,
+ "filtered": 50,
+ "attached_condition": "t1.a < 5"
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 5.4688
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t2.b = t1.a"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.b desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "<subquery2>",
+ "access_type": "eq_ref",
+ "possible_keys": ["distinct_key"],
+ "key": "distinct_key",
+ "key_length": "4",
+ "used_key_parts": ["b"],
+ "ref": ["func"],
+ "rows": 1,
+ "filtered": 100,
+ "materialized": {
+ "unique": 1,
+ "query_block": {
+ "select_id": 2,
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 1000,
+ "filtered": 100
+ }
+ }
+ }
+ }
+ }
+}
+SELECT t1.a, t2.a, t2.b
+FROM t1, t2
+WHERE t2.a in (SELECT t3.b from t3)
+AND t1.a= t2.b
+AND t1.a < 5
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 5;
+a a b
+4 4 4
+3 3 3
+2 2 2
+1 1 1
+0 0 0
+set use_sort_nest= 0;
+SELECT t1.a, t2.a, t2.b
+FROM t1, t2
+WHERE t2.a in (SELECT t3.b from t3)
+AND t1.a= t2.b
+AND t1.a < 5
+ORDER BY t1.b DESC, t2.a DESC
+LIMIT 5;
+a a b
+4 4 4
+3 3 3
+2 2 2
+1 1 1
+0 0 0
+DROP TABLE t1, t2, t3, t4;
+#
+# Firstmatch strategy
+#
+set @save_optimizer_switch=@@optimizer_switch;
+create table t0(a int);
+insert t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t1 (a int, b int, c int, key(a));
+insert t1 SELECT a,a,a from t0;
+create table t2 as SELECT * from t1;
+create table t3 as SELECT * from t1;
+set use_sort_nest=1;
+EXPLAIN SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 10 Using where
+1 PRIMARY t1 ref a a 5 test.t2.a 1
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 PRIMARY t3 ALL NULL NULL NULL NULL 10 Using where; FirstMatch(<sort-nest>)
+EXPLAIN FORMAT=JSON SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100,
+ "attached_condition": "t2.a is not null"
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t2.a"],
+ "rows": 1,
+ "filtered": 100
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.c desc, `sort-nest`.c desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100,
+ "attached_condition": "t3.b = `sort-nest`.b and t3.c <= `sort-nest`.c",
+ "first_match": "<sort-nest>"
+ }
+ }
+}
+SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+a b c a b c
+9 9 9 9 9 9
+8 8 8 8 8 8
+7 7 7 7 7 7
+6 6 6 6 6 6
+5 5 5 5 5 5
+set use_sort_nest=0;
+SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+a b c a b c
+9 9 9 9 9 9
+8 8 8 8 8 8
+7 7 7 7 7 7
+6 6 6 6 6 6
+5 5 5 5 5 5
+set optimizer_switch='firstmatch=off';
+#
+# Duplicate Weedout strategy
+#
+set use_sort_nest=1;
+EXPLAIN SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 10 Using where
+1 PRIMARY t1 ref a a 5 test.t2.a 1
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 PRIMARY t3 ALL NULL NULL NULL NULL 10 Using where; Start temporary; End temporary
+EXPLAIN FORMAT=JSON SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100,
+ "attached_condition": "t2.a is not null"
+ },
+ "table": {
+ "table_name": "t1",
+ "access_type": "ref",
+ "possible_keys": ["a"],
+ "key": "a",
+ "key_length": "5",
+ "used_key_parts": ["a"],
+ "ref": ["test.t2.a"],
+ "rows": 1,
+ "filtered": 100
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.c desc, `sort-nest`.c desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ },
+ "duplicates_removal": {
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 10,
+ "filtered": 100,
+ "attached_condition": "t3.b = `sort-nest`.b and t3.c <= `sort-nest`.c"
+ }
+ }
+ }
+}
+SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+a b c a b c
+9 9 9 9 9 9
+8 8 8 8 8 8
+7 7 7 7 7 7
+6 6 6 6 6 6
+5 5 5 5 5 5
+set use_sort_nest=0;
+SELECT * FROM t1, t2
+WHERE t1.a=t2.a AND
+t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ORDER BY t2.c DESC, t1.c DESC
+LIMIT 5;
+a b c a b c
+9 9 9 9 9 9
+8 8 8 8 8 8
+7 7 7 7 7 7
+6 6 6 6 6 6
+5 5 5 5 5 5
+set optimizer_switch=@save_optimizer_switch;
+DROP TABLE t0,t1,t2,t3;
+
+# NON-MERGED SEMI JOINS
+
+create table t0 (a int);
+INSERT INTO t0 SELECT seq-1 FROM seq_1_to_10;
+create table t1 (a int, b int);
+insert into t1 SELECT a,a from t0 where a <5;
+create table t2 as SELECT * from t1 where a < 5;
+create table t3(a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 FROM seq_1_to_100;
+<subquery2> outside the sort-nest
+set use_sort_nest=1;
+EXPLAIN SELECT * from t2,t1
+WHERE t2.b=t1.b
+AND
+t1.a IN (SELECT max(t3.a) FROM t3 GROUP BY t3.b)
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where; Using join buffer (flat, BNL join)
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 5 Using filesort
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 sort-nest.a 1
+2 MATERIALIZED t3 ALL NULL NULL NULL NULL 100 Using temporary
+EXPLAIN FORMAT=JSON SELECT * from t2,t1
+WHERE t2.b=t1.b
+AND
+t1.a IN (SELECT max(t3.a) FROM t3 GROUP BY t3.b)
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "attached_condition": "t1.b = t2.b"
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.a desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "<subquery2>",
+ "access_type": "eq_ref",
+ "possible_keys": ["distinct_key"],
+ "key": "distinct_key",
+ "key_length": "4",
+ "used_key_parts": ["max(t3.a)"],
+ "ref": ["sort-nest.a"],
+ "rows": 1,
+ "filtered": 100,
+ "materialized": {
+ "unique": 1,
+ "query_block": {
+ "select_id": 2,
+ "temporary_table": {
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100
+ }
+ }
+ }
+ }
+ }
+ }
+}
+SELECT * from t2,t1
+WHERE t2.b=t1.b
+AND
+t1.a IN (SELECT max(t3.a) FROM t3 GROUP BY t3.b)
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+a b a b
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+set use_sort_nest=0;
+SELECT * from t2,t1
+WHERE t2.b=t1.b
+AND
+t1.a IN (SELECT max(t3.a) FROM t3 GROUP BY t3.b)
+ORDER BY t2.a DESC,t1.a DESC
+LIMIT 5;
+a b a b
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+<subquery2> inside the sort-nest
+set use_sort_nest=1;
+EXPLAIN SELECT * FROM t3,t2
+WHERE t3.b=t2.b AND
+t3.a IN (SELECT max(t1.a) FROM t1 GROUP BY t1.b)
+ORDER BY t3.a DESC,t2.a DESC
+LIMIT 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 5
+1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 5 Using join buffer (flat, BNL join)
+1 PRIMARY <sort-nest> ALL NULL NULL NULL NULL 1 Using filesort
+1 PRIMARY t3 ALL NULL NULL NULL NULL 100 Using where
+2 MATERIALIZED t1 ALL NULL NULL NULL NULL 5 Using temporary
+EXPLAIN FORMAT=JSON SELECT * FROM t3,t2
+WHERE t3.b=t2.b AND
+t3.a IN (SELECT max(t1.a) FROM t1 GROUP BY t1.b)
+ORDER BY t3.a DESC,t2.a DESC
+LIMIT 5;
+EXPLAIN
+{
+ "query_block": {
+ "select_id": 1,
+ "table": {
+ "table_name": "t2",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ },
+ "block-nl-join": {
+ "table": {
+ "table_name": "<subquery2>",
+ "access_type": "ALL",
+ "possible_keys": ["distinct_key"],
+ "rows": 5,
+ "filtered": 100
+ },
+ "buffer_type": "flat",
+ "buffer_size": "119",
+ "join_type": "BNL",
+ "materialized": {
+ "unique": 1,
+ "query_block": {
+ "select_id": 2,
+ "temporary_table": {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 5,
+ "filtered": 100
+ }
+ }
+ }
+ }
+ },
+ "read_sorted_file": {
+ "filesort": {
+ "sort_key": "`sort-nest`.`max(t1.a)` desc, `sort-nest`.a desc",
+ "table": {
+ "table_name": "<sort-nest>",
+ "access_type": "ALL",
+ "rows": 1,
+ "filtered": 100
+ }
+ }
+ },
+ "table": {
+ "table_name": "t3",
+ "access_type": "ALL",
+ "rows": 100,
+ "filtered": 100,
+ "attached_condition": "t3.a = `sort-nest`.`max(t1.a)` and t3.b = `sort-nest`.b"
+ }
+ }
+}
+SELECT * FROM t3,t2
+WHERE t3.b=t2.b AND
+t3.a IN (SELECT max(t1.a) FROM t1 GROUP BY t1.b)
+ORDER BY t3.a DESC,t2.a DESC
+LIMIT 5;
+a b a b
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+set use_sort_nest=0;
+SELECT * FROM t3,t2
+WHERE t3.b=t2.b AND
+t3.a IN (SELECT max(t1.a) FROM t1 GROUP BY t1.b)
+ORDER BY t3.a DESC,t2.a DESC
+LIMIT 5;
+a b a b
+4 4 4 4
+3 3 3 3
+2 2 2 2
+1 1 1 1
+0 0 0 0
+DROP TABLE t1,t2,t3,t0;
diff --git a/mysql-test/main/sort_nest_sj.test b/mysql-test/main/sort_nest_sj.test
new file mode 100644
index 00000000000..2a06da7097c
--- /dev/null
+++ b/mysql-test/main/sort_nest_sj.test
@@ -0,0 +1,201 @@
+--source include/have_sequence.inc
+
+--echo #
+--echo # SORT-NEST WITH SEMI JOINS
+--echo #
+
+--echo
+--echo # MERGED SEMI-JOINS
+--echo
+
+--echo # SEMI JOIN MATERIALIZATION SCAN with SORT-NEST
+
+CREATE TABLE t0(a int);
+CREATE TABLE t1 (a int, b int, c int);
+CREATE TABLE t2 (a int, b int, c int);
+CREATE TABLE t3 (a int, b int, c int, key(a));
+CREATE TABLE t4 (a int, b int, c int, key(a));
+
+INSERT INTO t0 SELECT seq-1 FROM seq_1_to_10;
+INSERT INTO t1 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+INSERT INTO t2 SELECT a,a,a FROM t0;
+INSERT INTO t3 SELECT a,a,a FROM t0;
+INSERT INTO t4 SELECT a,a,a FROM t0;
+
+--disable_result_log
+ANALYZE TABLE t0 PERSISTENT FOR ALL;
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+ANALYZE TABLE t4 PERSISTENT FOR ALL;
+--enable_result_log
+
+--echo # SJM scan inside the sort-nest
+--echo # sort-nest includes (t2, <subquery2>)
+
+let $query= SELECT t1.a, t2.a, t1.b,t2.b
+ FROM t1, t2
+ WHERE t1.a=t2.a AND
+ t1.b IN (SELECT t3.b FROM t3,t4
+ WHERE t3.a < 3 AND t3.a=t4.a)
+ ORDER BY t1.b DESC ,t2.b DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+--echo #
+--echo # SJM scan table is the first table inside the sort-nest
+--echo #
+
+alter table t2 add key(b);
+let $query= SELECT t1.a, t2.a, t1.b,t2.b
+ FROM t1, t2
+ WHERE t1.a=t2.a AND t2.b < 5 AND
+ t1.b IN (SELECT t3.b FROM t3,t4
+ WHERE t3.a < 3 AND t3.a=t4.a)
+ ORDER BY t2.b DESC, t1.b DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval $query;
+
+set use_sort_nest= 0;
+eval $query;
+
+DROP TABLE t0, t1, t2, t3, t4;
+
+--echo #
+--echo # SJM Lookup with sort-nest, where SJM lookup table is outside the
+--echo # sort-nest
+--echo #
+
+create table t1 (a int, b int, c int, key(a));
+create table t2 (a int, b int, c int, key(c));
+create table t3 (a int, b int, c int, key(a));
+create table t4 (a int, b int, c int);
+
+INSERT INTO t1 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_10;
+INSERT INTO t2 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+INSERT INTO t3 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_1000;
+INSERT INTO t4 SELECT seq-1, seq-1, seq-1 FROM seq_1_to_100;
+
+--disable_result_log
+ANALYZE TABLE t1 PERSISTENT FOR ALL;
+ANALYZE TABLE t2 PERSISTENT FOR ALL;
+ANALYZE TABLE t3 PERSISTENT FOR ALL;
+ANALYZE TABLE t4 PERSISTENT FOR ALL;
+--enable_result_log
+
+let $query= SELECT t1.a, t2.a, t2.b
+ FROM t1, t2
+ WHERE t2.a in (SELECT t3.b from t3)
+ AND t1.a= t2.b
+ AND t1.a < 5
+ ORDER BY t1.b DESC, t2.a DESC
+ LIMIT 5;
+
+set use_sort_nest= 1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest= 0;
+eval $query;
+
+DROP TABLE t1, t2, t3, t4;
+
+--echo #
+--echo # Firstmatch strategy
+--echo #
+
+set @save_optimizer_switch=@@optimizer_switch;
+create table t0(a int);
+insert t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t1 (a int, b int, c int, key(a));
+insert t1 SELECT a,a,a from t0;
+create table t2 as SELECT * from t1;
+create table t3 as SELECT * from t1;
+let $query= SELECT * FROM t1, t2
+ WHERE t1.a=t2.a AND
+ t1.b IN (SELECT b FROM t3 WHERE t3.c<=t2.c)
+ ORDER BY t2.c DESC, t1.c DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+set optimizer_switch='firstmatch=off';
+
+--echo #
+--echo # Duplicate Weedout strategy
+--echo #
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+set optimizer_switch=@save_optimizer_switch;
+DROP TABLE t0,t1,t2,t3;
+
+--echo
+--echo # NON-MERGED SEMI JOINS
+--echo
+
+create table t0 (a int);
+INSERT INTO t0 SELECT seq-1 FROM seq_1_to_10;
+create table t1 (a int, b int);
+insert into t1 SELECT a,a from t0 where a <5;
+create table t2 as SELECT * from t1 where a < 5;
+create table t3(a int, b int);
+INSERT INTO t3 SELECT seq-1, seq-1 FROM seq_1_to_100;
+
+--echo <subquery2> outside the sort-nest
+
+let $query= SELECT * from t2,t1
+ WHERE t2.b=t1.b
+ AND
+ t1.a IN (SELECT max(t3.a) FROM t3 GROUP BY t3.b)
+ ORDER BY t2.a DESC,t1.a DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+--echo <subquery2> inside the sort-nest
+
+let $query= SELECT * FROM t3,t2
+ WHERE t3.b=t2.b AND
+ t3.a IN (SELECT max(t1.a) FROM t1 GROUP BY t1.b)
+ ORDER BY t3.a DESC,t2.a DESC
+ LIMIT 5;
+
+set use_sort_nest=1;
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set use_sort_nest=0;
+eval $query;
+
+DROP TABLE t1,t2,t3,t0;
diff --git a/mysql-test/main/sort_nest_subselect.test b/mysql-test/main/sort_nest_subselect.test
new file mode 100644
index 00000000000..e34a4817499
--- /dev/null
+++ b/mysql-test/main/sort_nest_subselect.test
@@ -0,0 +1,99 @@
+--echo #
+--echo # Testing SORT-NEST with non-flattened Subqueries
+--echo #
+
+--echo #
+--echo # Dependent subquery attached to table t3 outside the sort-nest(t1,t2)
+--echo #
+
+set use_sort_nest=1;
+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);
+insert into t1 select a,a from t0 where a <5; # 5 rows
+create table t2 as select * from t1 where a < 5; # 5 rows
+create table t3(a int, b int, c int);
+insert into t3 select A.a + 10*B.a, A.a + 10*B.a, A.a + 10*B.a from t0 A, t0 B; # 100 rows
+
+create table t4(a int, b int, c int, key(b));
+insert into t4 select A.a + 10*B.a, A.a + 10*B.a, A.a + 10*B.a from t0 A, t0 B; # 100 rows
+
+--echo # ref access inside the dependent subquery should be with sort-nest.b instead of t1.b
+--echo # subquery is attached to table t3 which is outside the sort-nest
+
+let $query= SELECT * FROM t1,t2,t3
+ WHERE t1.b=t2.b and
+ EXISTS (select 1 from t4 where t4.b=t1.b and t4.b < 4 group by t4.c having t3.b=max(t4.a))
+ ORDER BY t2.a desc,t1.a desc
+ LIMIT 5;
+
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+--echo # same as above but exists to in transformation not allowed
+--echo # subquery is attached to table t3 which is outside the sort-nest
+
+set optimizer_switch='exists_to_in=off';
+eval EXPLAIN $query;
+eval EXPLAIN FORMAT=JSON $query;
+eval $query;
+
+set optimizer_switch=default;
+
+drop table t0,t1,t2,t3,t4;
+
+
+--echo # DEPENDENT SUBQUERIES
+
+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);
+insert into t1 select a,a from t0 where a <5;
+create table t2 as select * from t1 where a < 5;
+create table t3 as select (A.a + 10*B.a+C.a*100) as a, (A.a + 10*B.a+C.a*100) as b,
+ (A.a + 10*B.a+C.a*100) as c from t0 A, t0 B,t0 C; # 1000 rows
+
+set optimizer_switch='exists_to_in=off';
+
+--echo # sort-nest(t2,t1) and subquery should be attached to table ot1
+let $query= select * from t2,t1,t3
+ where exists (select max(t3.a) from t3 t4 where t4.b=t1.b group by t4.c having t3.a= max(t4.a))
+ order by t2.a desc,t1.a desc limit 5;
+
+eval explain $query;
+eval explain format=json $query;
+eval $query;
+
+set optimizer_switch=default;
+--echo # sort-nest(t2,t1) and subquery should be attached to table ot1 (same as above)
+let $query= select * from t2,t1,t3
+ where t3.a in (select max(t4.a) from t3 t4 where t4.b=t1.b group by t4.c)
+ order by t2.a desc,t1.a desc limit 5;
+
+eval explain $query;
+eval explain format=json $query;
+eval $query;
+
+--echo # sort-nest(t2,t1) and dependent subquery in the select list
+let $query= select (select t4.a from t3 t4 where t4.a > t1.b limit 1) as x, t2.b, t1.b, t3.a from t1,t2,t3
+ where t1.a = t2.a order by t2.b desc, t1.b desc limit 5;
+
+eval explain $query;
+eval explain format=json $query;
+eval $query;
+
+--echo #
+--echo # sort-nest(t2,t1) and sort-nest fields substitution in the having clause of the subquery
+--echo # after IN -> EXISTS transformation
+--echo #
+
+let $query= select * from t2,t1,t3 ot1
+ where t2.a+ot1.a in (select max(t3.a) from t3 where t3.b=t1.b group by t3.c)
+ order by t2.a desc,t1.a desc limit 5;
+
+eval explain $query;
+eval explain format=json $query;
+eval $query;
+
+drop table t0,t1,t2,t3;
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 2591f21174b..4d3c1207aaa 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -141,6 +141,7 @@ SET (SQL_SOURCE
sql_cte.cc
item_vers.cc
sql_sequence.cc sql_sequence.h ha_sequence.h
+ sql_sort_nest.cc
sql_tvc.cc sql_tvc.h
opt_split.cc
rowid_filter.cc rowid_filter.h
diff --git a/sql/item.cc b/sql/item.cc
index bf59f4d8b85..f92390105c1 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -586,7 +586,8 @@ bool Item::cleanup_processor(void *arg)
pointer to newly allocated item is returned.
*/
-Item* Item::transform(THD *thd, Item_transformer transformer, uchar *arg)
+Item* Item::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
@@ -6182,6 +6183,52 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
}
+/*
+ Replace any Item_field object with the item in the nest table.
+ This is needed to substitute the item that are evaluated in the
+ post ORDER BY context that is when a sort-nest was created
+ and the ordering was already done.
+
+ @param arg NULL or points to the JOIN structure
+
+ @note
+ This function is supposed to be called as a callback parameter in calls
+ of the transformer method.
+
+ @return
+ - pointer to the nest items corresponding to the current item
+ - this - otherwise.
+*/
+
+Item *Item_field::replace_with_nest_items(THD *thd, uchar *arg)
+{
+ Mat_join_tab_nest_info *nest_info= (Mat_join_tab_nest_info*)arg;
+
+ if (!(used_tables() & nest_info->get_tables_map()) &&
+ !(used_tables() & OUTER_REF_TABLE_BIT))
+ return this;
+
+ List_iterator_fast<Item_pair> li(nest_info->mapping_of_items);
+ Item_pair *item_pair;
+ while((item_pair= li++))
+ {
+ Item *field_item= item_pair->base_item->real_item();
+ if (field->eq(((Item_field*)field_item)->field))
+ {
+ if (used_tables() == OUTER_REF_TABLE_BIT)
+ {
+ Item_field *clone_item= new (thd->mem_root) Item_field(thd, this);
+ Item *nest_item= item_pair->nest_item;
+ clone_item->set_field(((Item_field*)nest_item)->field);
+ return clone_item;
+ }
+ return item_pair->nest_item;
+ }
+ }
+ return this;
+}
+
+
void Item::init_make_send_field(Send_field *tmp_field,
const Type_handler *h)
{
@@ -7219,7 +7266,7 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg)
@note
This method is called for pushdown conditions into materialized
derived tables/views optimization.
- Item::pushable_cond_checker_for_derived() is passed as the actual callback
+ Item::pushable_cond_checker_for_tables() is passed as the actual callback
function.
Also it is called for pushdown conditions in materialized IN subqueries.
Item::pushable_cond_checker_for_subquery is passed as the actual
@@ -7259,6 +7306,86 @@ void Item::check_pushable_cond(Pushdown_checker checker, uchar *arg)
/**
@brief
+ For a condition check possibility of exraction a formula over grouping fields
+
+ @param thd The thread handle
+ @param cond The condition whose subformulas are to be analyzed
+ @param checker The checker callback function to be applied to the nodes
+ of the tree of the object
+
+ @details
+ This method traverses the AND-OR condition cond and for each subformula of
+ the condition it checks whether it can be usable for the extraction of a
+ condition over the grouping fields of this select. The method uses
+ the call-back parameter checker to check whether a primary formula
+ depends only on grouping fields.
+ The subformulas that are not usable are marked with the flag NO_EXTRACTION_FL.
+ The subformulas that can be entierly extracted are marked with the flag
+ FULL_EXTRACTION_FL.
+ @note
+ This method is called before any call of extract_cond_for_grouping_fields.
+ The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
+ for the subformula when extracting the pushable condition.
+ The flag FULL_EXTRACTION_FL allows to delete later all top level conjuncts
+ from cond.
+
+ TODO varun:
+ rewrite the comments to make sense
+ also a note for sort-nest using this would make sense
+
+*/
+
+void Item::check_pushable_cond_extraction(Pushdown_checker checker, uchar *arg)
+{
+ if (get_extraction_flag() == NO_EXTRACTION_FL)
+ return;
+ clear_extraction_flag();
+ if (type() == Item::COND_ITEM)
+ {
+ Item_cond_and *and_cond=
+ (((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC) ?
+ ((Item_cond_and*) this) : 0;
+
+ List<Item> *arg_list= ((Item_cond*) this)->argument_list();
+ List_iterator<Item> li(*arg_list);
+ uint count= 0; // to count items not containing NO_EXTRACTION_FL
+ uint count_full= 0; // to count items with FULL_EXTRACTION_FL
+ Item *item;
+ while ((item=li++))
+ {
+ item->check_pushable_cond_extraction(checker, arg);
+ if (item->get_extraction_flag() != NO_EXTRACTION_FL)
+ {
+ count++;
+ if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
+ count_full++;
+ }
+ else if (!and_cond)
+ break;
+ }
+ if ((and_cond && count == 0) || item)
+ set_extraction_flag(NO_EXTRACTION_FL);
+ if (count_full == arg_list->elements)
+ {
+ set_extraction_flag(FULL_EXTRACTION_FL);
+ }
+ if (get_extraction_flag() != 0)
+ {
+ li.rewind();
+ while ((item=li++))
+ item->clear_extraction_flag();
+ }
+ }
+ else
+ {
+ int fl= ((this->*checker) (arg)) ?
+ FULL_EXTRACTION_FL : NO_EXTRACTION_FL;
+ set_extraction_flag(fl);
+ }
+}
+
+/**
+ @brief
Build condition extractable from this condition for pushdown
@param thd the thread handle
@@ -7388,6 +7515,99 @@ Item *Item::build_pushable_cond(THD *thd,
return 0;
}
+/**
+ @brief
+ Build condition extractable from the given one depended on grouping fields
+
+ @param thd The thread handle
+ @param cond The condition from which the condition depended
+ on grouping fields is to be extracted
+ @param no_top_clones If it's true then no clones for the top MODE_ONLY_FULL_GROUP_BY
+ extractable conjuncts are built
+
+ @details
+ For the given condition cond this method finds out what condition depended
+ only on the grouping fields can be extracted from cond. If such condition C
+ exists the method builds the item for it.
+ This method uses the flags NO_EXTRACTION_FL and FULL_EXTRACTION_FL set by the
+ preliminary call of st_select_lex::check_cond_extraction_for_grouping_fields
+ to figure out whether a subformula depends only on these fields or not.
+ @note
+ The built condition C is always implied by the condition cond
+ (cond => C). The method tries to build the least restictive such
+ condition (i.e. for any other condition C' such that cond => C'
+ we have C => C').
+ @note
+ The build item is not ready for usage: substitution for the field items
+ has to be done and it has to be re-fixed.
+
+ @retval
+ the built condition depended only on grouping fields if such a condition
+ exists
+ NULL if there is no such a condition
+*/
+
+Item *Item::build_pushable_condition(THD *thd, bool no_top_clones)
+{
+ if (get_extraction_flag() == FULL_EXTRACTION_FL)
+ {
+ if (no_top_clones)
+ return this;
+ clear_extraction_flag();
+ return build_clone(thd);
+ }
+ if (type() == Item::COND_ITEM)
+ {
+ bool cond_and= false;
+ Item_cond *new_cond;
+ if (((Item_cond*) this)->functype() == Item_func::COND_AND_FUNC)
+ {
+ cond_and= true;
+ new_cond= new (thd->mem_root) Item_cond_and(thd);
+ }
+ else
+ new_cond= new (thd->mem_root) Item_cond_or(thd);
+ if (unlikely(!new_cond))
+ return 0;
+ List_iterator<Item> li(*((Item_cond*) this)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->get_extraction_flag() == NO_EXTRACTION_FL)
+ {
+ DBUG_ASSERT(cond_and);
+ item->clear_extraction_flag();
+ continue;
+ }
+ Item *fix= item->build_pushable_condition(thd, no_top_clones & cond_and);
+ if (unlikely(!fix))
+ {
+ if (cond_and)
+ continue;
+ break;
+ }
+ new_cond->argument_list()->push_back(fix, thd->mem_root);
+ }
+
+ if (!cond_and && item)
+ {
+ while((item= li++))
+ item->clear_extraction_flag();
+ return 0;
+ }
+ switch (new_cond->argument_list()->elements)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ return new_cond;
+ }
+ }
+ return 0;
+}
+
static
Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel)
@@ -7987,13 +8207,15 @@ void Item_ref::cleanup()
@retval NULL Out of memory error
*/
-Item* Item_ref::transform(THD *thd, Item_transformer transformer, uchar *arg)
+Item* Item_ref::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery,uchar *arg)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT((*ref) != NULL);
/* Transform the object we are referencing. */
- Item *new_item= (*ref)->transform(thd, transformer, arg);
+ Item *new_item= (*ref)->transform(thd, transformer,
+ transform_subquery, arg);
if (!new_item)
return NULL;
@@ -9101,10 +9323,12 @@ Item *Item_direct_view_ref::replace_equal_field(THD *thd, uchar *arg)
}
-bool Item_field::excl_dep_on_table(table_map tab_map)
+bool Item_field::excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
- return used_tables() == tab_map ||
- (item_equal && (item_equal->used_tables() & tab_map));
+ return !(used_tables() & ~tab_map) ||
+ (multi_eq_checked ?
+ FALSE:
+ (item_equal && (item_equal->used_tables() & tab_map)));
}
@@ -9115,7 +9339,8 @@ Item_field::excl_dep_on_grouping_fields(st_select_lex *sel)
}
-bool Item_direct_view_ref::excl_dep_on_table(table_map tab_map)
+bool Item_direct_view_ref::excl_dep_on_tables(table_map tab_map,
+ bool multi_eq_checked)
{
table_map used= used_tables();
if (used & OUTER_REF_TABLE_BIT)
@@ -9127,7 +9352,7 @@ bool Item_direct_view_ref::excl_dep_on_table(table_map tab_map)
DBUG_ASSERT(real_item()->type() == Item::FIELD_ITEM);
return item_equal->used_tables() & tab_map;
}
- return (*ref)->excl_dep_on_table(tab_map);
+ return (*ref)->excl_dep_on_tables(tab_map, multi_eq_checked);
}
@@ -9329,7 +9554,7 @@ table_map Item_default_value::used_tables() const
*/
Item *Item_default_value::transform(THD *thd, Item_transformer transformer,
- uchar *args)
+ bool transform_subquery, uchar *args)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
@@ -9340,7 +9565,8 @@ Item *Item_default_value::transform(THD *thd, Item_transformer transformer,
if (!arg)
return 0;
- Item *new_item= arg->transform(thd, transformer, args);
+ Item *new_item= arg->transform(thd, transformer,
+ transform_subquery, args);
if (!new_item)
return 0;
diff --git a/sql/item.h b/sql/item.h
index 022a14585c3..7fd32b134b4 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -445,6 +445,7 @@ typedef struct replace_equal_field_arg
struct st_join_table *context_tab;
} REPLACE_EQUAL_FIELD_ARG;
+
class Settable_routine_parameter
{
public:
@@ -1838,7 +1839,8 @@ public:
return (this->*processor)(arg);
}
- virtual Item* transform(THD *thd, Item_transformer transformer, uchar *arg);
+ virtual Item* transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
/*
This function performs a generic "compilation" of the Item tree.
@@ -1914,8 +1916,14 @@ public:
TRUE if the expression depends only on the table indicated by tab_map
or can be converted to such an exression using equalities.
Not to be used for AND/OR formulas.
+
+ @param multi_eq_checked set to TRUE if substitution for best field
+ item inside the multiple equality is already
+ done
*/
- virtual bool excl_dep_on_table(table_map tab_map) { return false; }
+ virtual bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
+ { return false; }
+
/*
TRUE if the expression depends only on grouping fields of sel
or can be converted to such an expression using equalities.
@@ -2178,6 +2186,8 @@ public:
{ return this; }
virtual Item *multiple_equality_transformer(THD *thd, uchar *arg)
{ return this; }
+ virtual Item *replace_with_nest_items(THD *thd, uchar *arg)
+ { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const
@@ -2373,10 +2383,32 @@ public:
marker &= ~EXTRACTION_MASK;
}
void check_pushable_cond(Pushdown_checker excl_dep_func, uchar *arg);
- bool pushable_cond_checker_for_derived(uchar *arg)
+
+ void check_pushable_cond_extraction(Pushdown_checker checker, uchar *arg);
+
+ /*
+ This function is used for the cases when we don't want to take into account
+ multiple equalities while finding dependency of items on tables.
+ */
+ bool pushable_cond_checker_for_tables(uchar *arg)
{
- return excl_dep_on_table(*((table_map *)arg));
+ return excl_dep_on_tables(*(table_map *)arg, TRUE);
}
+
+ /*
+ This function is used for the cases when we want to take into account
+ multiple equalities while finding dependency of items on tables.
+ */
+ bool pushable_cond_checker_for_tables_with_equalities(uchar *arg)
+ {
+ return excl_dep_on_tables(*(table_map *)arg, FALSE);
+ }
+
+ bool pushable_cond_checker_for_grouping_fields(uchar *arg)
+ {
+ return excl_dep_on_grouping_fields((st_select_lex*)arg);
+ }
+
bool pushable_cond_checker_for_subquery(uchar *arg)
{
return excl_dep_on_in_subq_left_part((Item_in_subselect *)arg);
@@ -2384,6 +2416,7 @@ public:
Item *build_pushable_cond(THD *thd,
Pushdown_checker checker,
uchar *arg);
+ Item *build_pushable_condition(THD *thd, bool no_top_clones);
/*
Checks if this item depends only on the arg table
*/
@@ -2512,15 +2545,16 @@ protected:
}
return false;
}
- bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
+ bool transform_args(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
- bool excl_dep_on_table(table_map tab_map)
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
for (uint i= 0; i < arg_count; i++)
{
if (args[i]->const_item())
continue;
- if (!args[i]->excl_dep_on_table(tab_map))
+ if (!args[i]->excl_dep_on_tables(tab_map, multi_eq_checked))
return false;
}
return true;
@@ -3475,7 +3509,7 @@ public:
Item *in_subq_field_transformer_for_where(THD *thd, uchar *arg);
Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg);
virtual void print(String *str, enum_query_type query_type);
- bool excl_dep_on_table(table_map tab_map);
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
bool cleanup_excluding_fields_processor(void *arg)
@@ -3491,6 +3525,7 @@ public:
return field->table->pos_in_table_list->outer_join;
}
bool check_index_dependence(void *arg);
+ Item *replace_with_nest_items(THD *thd, uchar *arg);
friend class Item_default_value;
friend class Item_insert_value;
friend class st_select_lex_unit;
@@ -5253,7 +5288,8 @@ public:
else
return FALSE;
}
- Item* transform(THD *thd, Item_transformer, uchar *arg);
+ Item* transform(THD *thd, Item_transformer,
+ bool transform_subquery, uchar *arg);
Item* compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool enumerate_field_refs_processor(void *arg)
@@ -5326,13 +5362,15 @@ public:
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_ref>(thd, this); }
- bool excl_dep_on_table(table_map tab_map)
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
table_map used= used_tables();
if (used & OUTER_REF_TABLE_BIT)
return false;
- return (used == tab_map) || (*ref)->excl_dep_on_table(tab_map);
+ return (!(used & ~tab_map) ||
+ (*ref)->excl_dep_on_tables(tab_map, multi_eq_checked));
}
+
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return (*ref)->excl_dep_on_grouping_fields(sel); }
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred)
@@ -5658,7 +5696,7 @@ public:
view_arg->view_used_tables|= (*ref)->used_tables();
return 0;
}
- bool excl_dep_on_table(table_map tab_map);
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred);
Item *derived_field_transformer_for_having(THD *thd, uchar *arg);
@@ -6248,7 +6286,8 @@ public:
(this->*processor)(args);
}
- Item *transform(THD *thd, Item_transformer transformer, uchar *args);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *args);
};
/**
@@ -6558,6 +6597,22 @@ public:
return TRUE;
return (this->*processor)(arg);
}
+ // TODO:varun need to enable this
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
+ {
+ if (transform_subquery)
+ {
+ if (example)
+ {
+ Item *new_item= example->transform(thd, transformer,
+ transform_subquery, arg);
+ if (new_item != example)
+ setup(thd, new_item);
+ }
+ }
+ return (this->*transformer)(thd, arg);
+ }
virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
void split_sum_func2_example(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &fields, uint flags)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index aaf6baf87f6..b3ea2663baa 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1703,7 +1703,7 @@ bool Item_in_optimizer::is_null()
*/
Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
- uchar *argument)
+ bool transform_subquery, uchar *argument)
{
Item *new_item;
@@ -1712,7 +1712,8 @@ Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
DBUG_ASSERT(arg_count == 2);
/* Transform the left IN operand. */
- new_item= (*args)->transform(thd, transformer, argument);
+ new_item= (*args)->transform(thd, transformer,
+ transform_subquery, argument);
if (!new_item)
return 0;
/*
@@ -1724,10 +1725,18 @@ Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
if ((*args) != new_item)
thd->change_item_tree(args, new_item);
+ /*
+ TODO varun
+ needs to transform the cached item
+ */
+ if (transform_subquery)
+ cache->setup(thd, args[0]);
+
if (invisible_mode())
{
/* MAX/MIN transformed => pass through */
- new_item= args[1]->transform(thd, transformer, argument);
+ new_item= args[1]->transform(thd, transformer,
+ transform_subquery, argument);
if (!new_item)
return 0;
if (args[1] != new_item)
@@ -1752,6 +1761,18 @@ Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
Item_in_subselect *in_arg= (Item_in_subselect*)args[1];
thd->change_item_tree(&in_arg->left_expr, args[0]);
+
+ /*
+ TODO(varun): this is needed for the sort-nest when we have dependent
+ subqyueries, for such cases we would need to introduce a new
+ parameter to transform function like transform_subquery,
+ if set to TRUE we would change the inner contents of the
+ subquery also.
+
+ new_item= args[1]->transform(thd, transformer, argument);
+ if (args[1] != new_item)
+ thd->change_item_tree(args + 1, new_item);
+ */
}
return (this->*transformer)(thd, argument);
}
@@ -5108,7 +5129,8 @@ bool Item_cond::walk(Item_processor processor, bool walk_subquery, void *arg)
Item returned as the result of transformation of the root node
*/
-Item *Item_cond::transform(THD *thd, Item_transformer transformer, uchar *arg)
+Item *Item_cond::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
@@ -5116,7 +5138,8 @@ Item *Item_cond::transform(THD *thd, Item_transformer transformer, uchar *arg)
Item *item;
while ((item= li++))
{
- Item *new_item= item->transform(thd, transformer, arg);
+ Item *new_item= item->transform(thd, transformer,
+ transform_subquery, arg);
if (!new_item)
return 0;
@@ -5129,7 +5152,7 @@ Item *Item_cond::transform(THD *thd, Item_transformer transformer, uchar *arg)
if (new_item != item)
thd->change_item_tree(li.ref(), new_item);
}
- return Item_func::transform(thd, transformer, arg);
+ return Item_func::transform(thd, transformer, transform_subquery, arg);
}
@@ -5176,7 +5199,7 @@ Item *Item_cond::compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
if (new_item && new_item != item)
thd->change_item_tree(li.ref(), new_item);
}
- return Item_func::transform(thd, transformer, arg_t);
+ return Item_func::transform(thd, transformer, FALSE, arg_t);
}
@@ -5328,7 +5351,7 @@ Item *Item_cond::build_clone(THD *thd)
}
-bool Item_cond::excl_dep_on_table(table_map tab_map)
+bool Item_cond::excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
if (used_tables() & OUTER_REF_TABLE_BIT)
return false;
@@ -5338,7 +5361,7 @@ bool Item_cond::excl_dep_on_table(table_map tab_map)
Item *item;
while ((item= li++))
{
- if (!item->excl_dep_on_table(tab_map))
+ if (!item->excl_dep_on_tables(tab_map, multi_eq_checked))
return false;
}
return true;
@@ -7215,7 +7238,8 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, void *arg)
}
-Item *Item_equal::transform(THD *thd, Item_transformer transformer, uchar *arg)
+Item *Item_equal::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
@@ -7223,7 +7247,8 @@ Item *Item_equal::transform(THD *thd, Item_transformer transformer, uchar *arg)
Item_equal_fields_iterator it(*this);
while ((item= it++))
{
- Item *new_item= item->transform(thd, transformer, arg);
+ Item *new_item= item->transform(thd, transformer,
+ transform_subquery, arg);
if (!new_item)
return 0;
@@ -7236,7 +7261,7 @@ Item *Item_equal::transform(THD *thd, Item_transformer transformer, uchar *arg)
if (new_item != item)
thd->change_item_tree((Item **) it.ref(), new_item);
}
- return Item_func::transform(thd, transformer, arg);
+ return Item_func::transform(thd, transformer, transform_subquery, arg);
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index ffe17f8a0bc..dda4c29dba3 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -379,7 +379,8 @@ public:
const char *func_name() const { return "<in_optimizer>"; }
Item_cache **get_cache() { return &cache; }
void keep_top_level_cache();
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
virtual Item *expr_cache_insert_transformer(THD *thd, uchar *unused);
bool is_expensive_processor(void *arg);
bool is_expensive();
@@ -3016,7 +3017,8 @@ public:
bool top_level() { return abort_on_null; }
void copy_andor_arguments(THD *thd, Item_cond *item);
bool walk(Item_processor processor, bool walk_subquery, void *arg);
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
void traverse_cond(Cond_traverser, void *arg, traverse_order order);
void neg_arguments(THD *thd);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
@@ -3025,7 +3027,7 @@ public:
bool eval_not_null_tables(void *opt_arg);
bool find_not_null_fields(table_map allowed);
Item *build_clone(THD *thd);
- bool excl_dep_on_table(table_map tab_map);
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
};
@@ -3198,7 +3200,8 @@ public:
SARGABLE_PARAM **sargables);
SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
bool walk(Item_processor processor, bool walk_subquery, void *arg);
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
virtual void print(String *str, enum_query_type query_type);
const Type_handler *compare_type_handler() const { return m_compare_handler; }
CHARSET_INFO *compare_collation() const { return m_compare_collation; }
@@ -3210,7 +3213,7 @@ public:
This does not comply with the specification of the virtual method,
but Item_equal items are processed distinguishly anyway
*/
- bool excl_dep_on_table(table_map tab_map)
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
return used_tables() & tab_map;
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 2e4a813990f..de8b519c898 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -473,11 +473,13 @@ void Item_func::traverse_cond(Cond_traverser traverser,
}
-bool Item_args::transform_args(THD *thd, Item_transformer transformer, uchar *arg)
+bool Item_args::transform_args(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
{
for (uint i= 0; i < arg_count; i++)
{
- Item *new_item= args[i]->transform(thd, transformer, arg);
+ Item *new_item= args[i]->transform(thd, transformer,
+ transform_subquery, arg);
if (!new_item)
return true;
/*
@@ -510,10 +512,11 @@ bool Item_args::transform_args(THD *thd, Item_transformer transformer, uchar *ar
Item returned as the result of transformation of the root node
*/
-Item *Item_func::transform(THD *thd, Item_transformer transformer, uchar *argument)
+Item *Item_func::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *argument)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
- if (transform_args(thd, transformer, argument))
+ if (transform_args(thd, transformer, transform_subquery, argument))
return 0;
return (this->*transformer)(thd, argument);
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 6226b741e91..c8b14ae2a21 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -204,7 +204,8 @@ public:
else
max_length= (uint32) max_result_length;
}
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
Item* compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
void traverse_cond(Cond_traverser traverser,
@@ -331,12 +332,12 @@ public:
return used_tables() & RAND_TABLE_BIT;
}
- bool excl_dep_on_table(table_map tab_map)
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
if (used_tables() & OUTER_REF_TABLE_BIT)
return false;
return !(used_tables() & ~tab_map) ||
- Item_args::excl_dep_on_table(tab_map);
+ Item_args::excl_dep_on_tables(tab_map, multi_eq_checked);
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
@@ -2818,7 +2819,8 @@ public:
void cleanup();
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_set_user_var>(thd, this); }
- bool excl_dep_on_table(table_map tab_map) { return false; }
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
+ { return false; }
};
diff --git a/sql/item_row.cc b/sql/item_row.cc
index def1458df1b..d6fd2938d15 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -166,11 +166,12 @@ void Item_row::print(String *str, enum_query_type query_type)
}
-Item *Item_row::transform(THD *thd, Item_transformer transformer, uchar *arg)
+Item *Item_row::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
- if (transform_args(thd, transformer, arg))
+ if (transform_args(thd, transformer, transform_subquery, arg))
return 0;
return (this->*transformer)(thd, arg);
}
diff --git a/sql/item_row.h b/sql/item_row.h
index 2872a498d55..75ec99499f3 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -119,7 +119,8 @@ public:
return true;
return (this->*processor)(arg);
}
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
+ Item *transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
bool eval_not_null_tables(void *opt_arg);
bool find_not_null_fields(table_map allowed);
@@ -136,9 +137,9 @@ public:
return this;
}
- bool excl_dep_on_table(table_map tab_map)
+ bool excl_dep_on_tables(table_map tab_map, bool multi_eq_checked)
{
- return Item_args::excl_dep_on_table(tab_map);
+ return Item_args::excl_dep_on_tables(tab_map, multi_eq_checked);
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 96344c0968b..7cd501e76b8 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -715,6 +715,80 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
}
+Item* Item_subselect::transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg)
+{
+ if (!(unit->uncacheable & ~UNCACHEABLE_DEPENDENT) && engine->is_executed() &&
+ !unit->describe)
+ {
+ /*
+ The subquery has already been executed (for real, it wasn't EXPLAIN's
+ fake execution) so it should not matter what it has inside.
+
+ The actual reason for not walking inside is that parts of the subquery
+ (e.g. JTBM join nests and their IN-equality conditions may have been
+ invalidated by irreversible cleanups (those happen after an uncorrelated
+ subquery has been executed).
+ */
+ return (this->*transformer)(thd, arg);
+ }
+
+ /*
+ TODO(varun): this is needed for the sort-nest when we have dependent
+ subqueries, for such cases we would need to introduce a new
+ parameter to transform function like transform_subquery,
+ if set to TRUE we would change the inner contents of the
+ subquery also.
+ */
+ if (transform_subquery)
+ {
+ for (SELECT_LEX *lex= unit->first_select(); lex; lex= lex->next_select())
+ {
+ List_iterator<Item> it(lex->item_list);
+ Item *item, *new_item;
+ ORDER *order;
+
+ if (lex->where)
+ {
+ lex->where= (lex->where)->transform(thd, transformer, TRUE, arg);
+ lex->where->update_used_tables();
+ }
+ if (lex->having)
+ {
+ lex->having= (lex->having)->transform(thd, transformer, TRUE, arg);
+ lex->having->update_used_tables();
+ }
+
+ while ((item=it++))
+ {
+ if ((new_item= item->transform(thd, transformer, TRUE, arg)) != item)
+ {
+ new_item->name= item->name;
+ thd->change_item_tree(it.ref(), new_item);
+ it.replace(new_item);
+ }
+ new_item->update_used_tables();
+ }
+
+ for (order= lex->order_list.first ; order; order= order->next)
+ {
+ *order->item= (*order->item)->transform(thd, transformer,
+ TRUE, arg);
+ (*order->item)->update_used_tables();
+ }
+
+ for (order= lex->group_list.first ; order; order= order->next)
+ {
+ *order->item= (*order->item)->transform(thd, transformer, TRUE, arg);
+ (*order->item)->update_used_tables();
+ }
+ }
+ }
+
+ return (this->*transformer)(thd, arg);
+}
+
+
bool Item_subselect::exec()
{
subselect_engine *org_engine= engine;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index dc8417495c5..bf0f7617b36 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -234,6 +234,9 @@ public:
virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; }
bool walk(Item_processor processor, bool walk_subquery, void *arg);
+ Item* transform(THD *thd, Item_transformer transformer,
+ bool transform_subquery, uchar *arg);
+
bool mark_as_eliminated_processor(void *arg);
bool eliminate_subselect_processor(void *arg);
bool set_fake_select_as_master_processor(void *arg);
diff --git a/sql/opt_split.cc b/sql/opt_split.cc
index cfac0c93544..1bdaa1e16ed 100644
--- a/sql/opt_split.cc
+++ b/sql/opt_split.cc
@@ -653,7 +653,7 @@ add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses,
*/
static
-double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len)
+double spl_postjoin_oper_cost(THD *thd, double join_record_count, ulong rec_len)
{
double cost;
cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) *
@@ -696,7 +696,7 @@ void JOIN::add_keyuses_for_splitting()
KEYUSE_EXT *keyuse_ext;
KEYUSE_EXT keyuse_ext_end;
double oper_cost;
- uint rec_len;
+ ulong rec_len;
uint added_keyuse_count;
TABLE *table= select_lex->master_unit()->derived->table;
List_iterator_fast<KEY_FIELD> li(spl_opt_info->added_key_fields);
@@ -971,7 +971,7 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(double record_count,
(spl_opt_info->unsplit_card ?
spl_opt_info->unsplit_card : 1);
- uint rec_len= table->s->rec_buff_length;
+ ulong rec_len= table->s->rec_buff_length;
double split_card= spl_opt_info->unsplit_card * spl_plan->split_sel;
double oper_cost= split_card *
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 44cb524d1b8..6df06c508b4 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -448,7 +448,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred);
static bool convert_subq_to_jtbm(JOIN *parent_join,
Item_in_subselect *subq_pred, bool *remove);
static TABLE_LIST *alloc_join_nest(THD *thd);
-static uint get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements);
+static ulong get_tmp_table_rec_length(Ref_ptr_array p_list, uint elements);
bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables);
static SJ_MATERIALIZATION_INFO *
at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab,
@@ -2504,8 +2504,8 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
/*
Calculate temporary table parameters and usage costs
*/
- uint rowlen= get_tmp_table_rec_length(subq_select->ref_pointer_array,
- subq_select->item_list.elements);
+ ulong rowlen= get_tmp_table_rec_length(subq_select->ref_pointer_array,
+ subq_select->item_list.elements);
double lookup_cost= get_tmp_table_lookup_cost(join->thd,
subjoin_out_rows, rowlen);
double write_cost= get_tmp_table_write_cost(join->thd,
@@ -2552,9 +2552,9 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
Length of the temptable record, in bytes
*/
-static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements)
+static ulong get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements)
{
- uint len= 0;
+ ulong len= 0;
Item *item;
//List_iterator<Item> it(items);
for (uint i= 0; i < elements ; i++)
@@ -2605,7 +2605,7 @@ static uint get_tmp_table_rec_length(Ref_ptr_array p_items, uint elements)
*/
double
-get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size)
+get_tmp_table_lookup_cost(THD *thd, double row_count, ulong row_size)
{
if (row_count > thd->variables.max_heap_table_size / (double) row_size)
return (double) DISK_TEMPTABLE_LOOKUP_COST;
@@ -2625,7 +2625,7 @@ get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size)
*/
double
-get_tmp_table_write_cost(THD *thd, double row_count, uint row_size)
+get_tmp_table_write_cost(THD *thd, double row_count, ulong row_size)
{
double lookup_cost= get_tmp_table_lookup_cost(thd, row_count, row_size);
/*
@@ -3126,7 +3126,8 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
{
best_access_path(join, join->positions[i].table, rem_tables,
join->positions, i,
- disable_jbuf, prefix_rec_count, &curpos, &dummy);
+ disable_jbuf, prefix_rec_count, &curpos, &dummy,
+ 0, FALSE);
prefix_rec_count= COST_MULT(prefix_rec_count, curpos.records_read);
prefix_cost= COST_ADD(prefix_cost, curpos.read_time);
prefix_cost= COST_ADD(prefix_cost,
@@ -3728,6 +3729,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
SJ_MATERIALIZATION_INFO *sjm= s->emb_sj_nest->sj_mat_info;
sjm->is_used= TRUE;
sjm->is_sj_scan= FALSE;
+ bool save_sort_nest_op= pos->sort_nest_operation_here;
memcpy((uchar*) (pos - sjm->tables + 1), (uchar*) sjm->positions,
sizeof(POSITION) * sjm->tables);
recalculate_prefix_record_count(join, tablenr - sjm->tables + 1,
@@ -3735,6 +3737,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
first= tablenr - sjm->tables + 1;
join->best_positions[first].n_sj_tables= sjm->tables;
join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE;
+ join->best_positions[first].sort_nest_operation_here= save_sort_nest_op;
Json_writer_object semijoin_strategy(thd);
semijoin_strategy.add("semi_join_strategy","SJ-Materialization");
Json_writer_array semijoin_plan(thd, "join_order");
@@ -3755,11 +3758,14 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
sjm->is_used= TRUE;
sjm->is_sj_scan= TRUE;
first= pos->sjmat_picker.sjm_scan_last_inner - sjm->tables + 1;
+ POSITION *last_inner= join->best_positions+ first + sjm->tables - 1;
+ bool save_sort_nest_op= last_inner->sort_nest_operation_here;
memcpy((uchar*) (join->best_positions + first),
(uchar*) sjm->positions, sizeof(POSITION) * sjm->tables);
recalculate_prefix_record_count(join, first, first + sjm->tables);
join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE_SCAN;
join->best_positions[first].n_sj_tables= sjm->tables;
+ join->best_positions[first].sort_nest_operation_here= save_sort_nest_op;
/*
Do what advance_sj_state did: re-run best_access_path for every table
in the [last_inner_table + 1; pos..) range
@@ -3794,15 +3800,17 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
Json_writer_object trace_one_table(thd);
trace_one_table.add_table_name(join->best_positions[i].table);
}
+ save_sort_nest_op= join->best_positions[i].sort_nest_operation_here;
best_access_path(join, join->best_positions[i].table, rem_tables,
join->best_positions, i,
FALSE, prefix_rec_count,
- join->best_positions + i, &dummy);
+ join->best_positions + i, &dummy, 0, FALSE);
+ join->best_positions[i].sort_nest_operation_here= save_sort_nest_op;
prefix_rec_count *= join->best_positions[i].records_read;
rem_tables &= ~join->best_positions[i].table->table->map;
}
}
-
+
if (pos->sj_strategy == SJ_OPT_FIRST_MATCH)
{
first= pos->firstmatch_picker.first_firstmatch_table;
@@ -3838,7 +3846,8 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
best_access_path(join, join->best_positions[idx].table,
rem_tables, join->best_positions, idx,
TRUE /* no jbuf */,
- record_count, join->best_positions + idx, &dummy);
+ record_count, join->best_positions + idx, &dummy,
+ 0, FALSE);
}
record_count *= join->best_positions[idx].records_read;
rem_tables &= ~join->best_positions[idx].table->table->map;
@@ -3878,7 +3887,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
rem_tables, join->best_positions, idx,
TRUE /* no jbuf */,
record_count, join->best_positions + idx,
- &loose_scan_pos);
+ &loose_scan_pos, 0, FALSE);
if (idx==first)
{
join->best_positions[idx]= loose_scan_pos;
@@ -3921,7 +3930,10 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
for (uint i= first; i < i_end; i++)
{
if (i != first)
+ {
join->best_positions[i].sj_strategy= SJ_OPT_NONE;
+ DBUG_ASSERT(!join->best_positions[i].sort_nest_operation_here);
+ }
handled_tabs |= join->best_positions[i].table->table->map;
}
@@ -3997,7 +4009,14 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
DBUG_ENTER("setup_sj_materialization");
- /* Walk out of outer join nests until we reach the semi-join nest we're in */
+ /*
+ Walk out of outer join nests until we reach the semi-join nest we're in.
+ There can be a case that the first [in join order ordering] table
+ inside semi-join-materialization nest is also an inner table wrt an
+ outer join (that is embedded in the semi-join).
+ This can happen when all of the tables that are inside the semi-join
+ but not inside the outer join are constant
+ */
while (!emb_sj_nest->sj_mat_info)
emb_sj_nest= emb_sj_nest->embedding;
@@ -4883,6 +4902,11 @@ int setup_semijoin_loosescan(JOIN *join)
for (i= join->const_tables ; i < join->top_join_tab_count; )
{
JOIN_TAB *tab=join->join_tab + i;
+ if (tab->is_sort_nest)
+ {
+ i++;
+ continue;
+ }
switch (pos->sj_strategy) {
case SJ_OPT_MATERIALIZE:
case SJ_OPT_MATERIALIZE_SCAN:
@@ -5030,11 +5054,20 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
DBUG_ENTER("setup_semijoin_dups_elimination");
join->complex_firstmatch_tables= table_map(0);
+ Sort_nest_info *sort_nest_info= join->sort_nest_info;
+
+ if (sort_nest_info)
+ no_jbuf_after= join->const_tables+ sort_nest_info->number_of_tables();
POSITION *pos= join->best_positions + join->const_tables;
for (i= join->const_tables ; i < join->top_join_tab_count; )
{
JOIN_TAB *tab=join->join_tab + i;
+ if (tab->is_sort_nest)
+ {
+ i++;
+ continue;
+ }
switch (pos->sj_strategy) {
case SJ_OPT_MATERIALIZE:
case SJ_OPT_MATERIALIZE_SCAN:
@@ -5623,6 +5656,36 @@ enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab)
sjm->materialized= TRUE;
}
}
+ else if (tab->is_sort_nest)
+ {
+ /*
+ This is where the sort-nest gets filled by the partial join.
+ This would compute the partial join and write the records
+ in the temporary table.
+ */
+
+ /*
+ TODO(varun): this can be move to the SJM nest when the handling
+ of sort-nest is done with a bush
+ */
+ enum_nested_loop_state rc;
+ JOIN *join= tab->join;
+ Sort_nest_info *nest_info= join->sort_nest_info;
+
+ if (!nest_info->is_materialized())
+ {
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;
+ JOIN_TAB *save_return_tab= join->return_tab;
+ if ((rc= sub_select(join, join_tab, FALSE)) < 0 ||
+ (rc= sub_select(join, join_tab, TRUE)) < 0)
+ {
+ join->return_tab= save_return_tab;
+ DBUG_RETURN(rc);
+ }
+ join->return_tab= save_return_tab;
+ nest_info->set_materialized();
+ }
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -6492,7 +6555,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
*/
/* C.1 Compute the cost of the materialization strategy. */
//uint rowlen= get_tmp_table_rec_length(unit->first_select()->item_list);
- uint rowlen= get_tmp_table_rec_length(ref_ptrs,
+ ulong rowlen= get_tmp_table_rec_length(ref_ptrs,
select_lex->item_list.elements);
/* The cost of writing one row into the temporary table. */
double write_cost= get_tmp_table_write_cost(thd, inner_record_count_1,
@@ -7091,7 +7154,7 @@ bool Item_in_subselect::pushdown_cond_for_in_subquery(THD *thd, Item *cond)
remaining_cond=
remaining_cond->transform(thd,
&Item::in_subq_field_transformer_for_having,
- (uchar *)this);
+ FALSE, (uchar *)this);
if (!remaining_cond ||
remaining_cond->walk(&Item::cleanup_excluding_const_fields_processor,
0, 0))
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index abd37f1e98e..6ade22b3a65 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -302,6 +302,8 @@ public:
pos->loosescan_picker.loosescan_key= best_loose_scan_key;
pos->loosescan_picker.loosescan_parts= best_max_loose_keypart + 1;
pos->use_join_buffer= FALSE;
+ pos->sort_nest_operation_here= FALSE;
+ pos->index_no= -1;
pos->table= tab;
pos->range_rowid_filter_info= tab->range_rowid_filter_info;
pos->ref_depend_map= best_ref_depend_map;
diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc
index d95f0795542..a6e2740ac17 100644
--- a/sql/opt_trace.cc
+++ b/sql/opt_trace.cc
@@ -615,6 +615,13 @@ void Json_writer::add_table_name(const JOIN_TAB *tab)
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
add_str(table_name_buffer, len);
}
+ else if (tab->is_sort_nest)
+ {
+ size_t len= my_snprintf(table_name_buffer,
+ sizeof(table_name_buffer)-1,
+ "<sort-nest>");
+ add_str(table_name_buffer, len);
+ }
else
{
TABLE_LIST *real_table= tab->table->pos_in_table_list;
@@ -707,6 +714,42 @@ void print_best_access_for_table(THD *thd, POSITION *pos,
/*
+ Add the tables that are inside the sort-nest
+ in the optimizer trace
+*/
+void add_sort_nest_tables_to_trace(JOIN *join,
+ Mat_join_tab_nest_info* nest_info)
+{
+ JOIN_TAB *end_tab, *tab;
+ THD *thd= join->thd;
+ end_tab= nest_info->nest_tab;
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_array sort_nest(thd, "sort-nest");
+ for (tab= join->join_tab + join->const_tables; tab < end_tab; tab++)
+ sort_nest.add_table_name(tab);
+}
+
+
+/*
+ This function is used during best_access_path to print the sort-nest
+ that were considered doing the cost based analysis of the various
+ join orders.
+*/
+
+void trace_sort_nest(JOIN *join, uint idx, table_map remaining_tables)
+{
+ THD *const thd= join->thd;
+ Json_writer_array plan_prefix(thd, "sort-nest");
+ for (uint i= 0; i < idx; i++)
+ {
+ TABLE *tr= join->positions[i].table->table;
+ if (tr->map & remaining_tables)
+ plan_prefix.add_table_name(join->positions[i].table);
+ }
+}
+
+
+/*
Introduce enum_query_type flags parameter, maybe also allow
EXPLAIN also use this function.
*/
diff --git a/sql/opt_trace.h b/sql/opt_trace.h
index 46adbec2c3c..d2330420a2e 100644
--- a/sql/opt_trace.h
+++ b/sql/opt_trace.h
@@ -109,6 +109,8 @@ void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables);
void print_final_join_order(JOIN *join);
void print_best_access_for_table(THD *thd, POSITION *pos,
enum join_type type);
+void add_sort_nest_tables_to_trace(JOIN *join,
+ Mat_join_tab_nest_info* nest_info);
/*
Security related (need to add a proper comment here)
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 3cae7d460e0..7f8f9e8dbba 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -604,6 +604,7 @@ typedef struct system_variables
ulonglong group_concat_max_len;
ulonglong default_regex_flags;
ulonglong max_mem_used;
+ my_bool use_sort_nest;
/**
Place holders to store Multi-source variables in sys_var.cc during
diff --git a/sql/sql_const.h b/sql/sql_const.h
index f7c820c727b..de8d5f9b596 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -296,6 +296,14 @@
*/
#define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1)
+
+/**
+ Average record length is the number of bytes for the record.
+ It is just a rough estimates which is needed to calculate
+ the cost of filling, reading and sorting the temporary table.
+*/
+#define AVG_REC_LEN 50
+
#if defined(__WIN__)
#define INTERRUPT_PRIOR -2
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 06e82263524..12900cca22c 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1405,8 +1405,9 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
/* 1. Check what pushable formula can be extracted from cond */
Item *extracted_cond;
- cond->check_pushable_cond(&Item::pushable_cond_checker_for_derived,
- (uchar *)(&derived->table->map));
+ cond->check_pushable_cond(
+ &Item::pushable_cond_checker_for_tables_with_equalities,
+ (uchar *)&derived->table->map);
/* 2. Build a clone PC of the formula that can be extracted */
extracted_cond=
cond->build_pushable_cond(thd,
@@ -1462,7 +1463,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
remaining_cond=
remaining_cond->transform(thd,
&Item::derived_field_transformer_for_having,
- (uchar *) sl);
+ FALSE, (uchar *) sl);
if (!remaining_cond)
continue;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 4180089a501..c9b42fbae82 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3720,7 +3720,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
while ((item= li++))
{
- item->transform(thd, &Item::update_value_transformer,
+ item->transform(thd, &Item::update_value_transformer, FALSE,
(uchar*)lex->current_select);
}
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index caccf27446b..d2e4d0fe103 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -7923,178 +7923,6 @@ bool st_select_lex::collect_grouping_fields(THD *thd)
}
-/**
- @brief
- For a condition check possibility of exraction a formula over grouping fields
-
- @param thd The thread handle
- @param cond The condition whose subformulas are to be analyzed
- @param checker The checker callback function to be applied to the nodes
- of the tree of the object
-
- @details
- This method traverses the AND-OR condition cond and for each subformula of
- the condition it checks whether it can be usable for the extraction of a
- condition over the grouping fields of this select. The method uses
- the call-back parameter checker to check whether a primary formula
- depends only on grouping fields.
- The subformulas that are not usable are marked with the flag NO_EXTRACTION_FL.
- The subformulas that can be entierly extracted are marked with the flag
- FULL_EXTRACTION_FL.
- @note
- This method is called before any call of extract_cond_for_grouping_fields.
- The flag NO_EXTRACTION_FL set in a subformula allows to avoid building clone
- for the subformula when extracting the pushable condition.
- The flag FULL_EXTRACTION_FL allows to delete later all top level conjuncts
- from cond.
-*/
-
-void
-st_select_lex::check_cond_extraction_for_grouping_fields(THD *thd, Item *cond)
-{
- if (cond->get_extraction_flag() == NO_EXTRACTION_FL)
- return;
- cond->clear_extraction_flag();
- if (cond->type() == Item::COND_ITEM)
- {
- Item_cond_and *and_cond=
- (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) ?
- ((Item_cond_and*) cond) : 0;
-
- List<Item> *arg_list= ((Item_cond*) cond)->argument_list();
- List_iterator<Item> li(*arg_list);
- uint count= 0; // to count items not containing NO_EXTRACTION_FL
- uint count_full= 0; // to count items with FULL_EXTRACTION_FL
- Item *item;
- while ((item=li++))
- {
- check_cond_extraction_for_grouping_fields(thd, item);
- if (item->get_extraction_flag() != NO_EXTRACTION_FL)
- {
- count++;
- if (item->get_extraction_flag() == FULL_EXTRACTION_FL)
- count_full++;
- }
- else if (!and_cond)
- break;
- }
- if ((and_cond && count == 0) || item)
- cond->set_extraction_flag(NO_EXTRACTION_FL);
- if (count_full == arg_list->elements)
- {
- cond->set_extraction_flag(FULL_EXTRACTION_FL);
- }
- if (cond->get_extraction_flag() != 0)
- {
- li.rewind();
- while ((item=li++))
- item->clear_extraction_flag();
- }
- }
- else
- {
- int fl= cond->excl_dep_on_grouping_fields(this) ?
- FULL_EXTRACTION_FL : NO_EXTRACTION_FL;
- cond->set_extraction_flag(fl);
- }
-}
-
-
-/**
- @brief
- Build condition extractable from the given one depended on grouping fields
-
- @param thd The thread handle
- @param cond The condition from which the condition depended
- on grouping fields is to be extracted
- @param no_top_clones If it's true then no clones for the top fully
- extractable conjuncts are built
-
- @details
- For the given condition cond this method finds out what condition depended
- only on the grouping fields can be extracted from cond. If such condition C
- exists the method builds the item for it.
- This method uses the flags NO_EXTRACTION_FL and FULL_EXTRACTION_FL set by the
- preliminary call of st_select_lex::check_cond_extraction_for_grouping_fields
- to figure out whether a subformula depends only on these fields or not.
- @note
- The built condition C is always implied by the condition cond
- (cond => C). The method tries to build the least restictive such
- condition (i.e. for any other condition C' such that cond => C'
- we have C => C').
- @note
- The build item is not ready for usage: substitution for the field items
- has to be done and it has to be re-fixed.
-
- @retval
- the built condition depended only on grouping fields if such a condition exists
- NULL if there is no such a condition
-*/
-
-Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond,
- bool no_top_clones)
-{
- if (cond->get_extraction_flag() == FULL_EXTRACTION_FL)
- {
- if (no_top_clones)
- return cond;
- cond->clear_extraction_flag();
- return cond->build_clone(thd);
- }
- if (cond->type() == Item::COND_ITEM)
- {
- bool cond_and= false;
- Item_cond *new_cond;
- if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
- {
- cond_and= true;
- new_cond= new (thd->mem_root) Item_cond_and(thd);
- }
- else
- new_cond= new (thd->mem_root) Item_cond_or(thd);
- if (unlikely(!new_cond))
- return 0;
- List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
- Item *item;
- while ((item=li++))
- {
- if (item->get_extraction_flag() == NO_EXTRACTION_FL)
- {
- DBUG_ASSERT(cond_and);
- item->clear_extraction_flag();
- continue;
- }
- Item *fix= build_cond_for_grouping_fields(thd, item,
- no_top_clones & cond_and);
- if (unlikely(!fix))
- {
- if (cond_and)
- continue;
- break;
- }
- new_cond->argument_list()->push_back(fix, thd->mem_root);
- }
-
- if (!cond_and && item)
- {
- while((item= li++))
- item->clear_extraction_flag();
- return 0;
- }
- switch (new_cond->argument_list()->elements)
- {
- case 0:
- return 0;
- case 1:
- return new_cond->argument_list()->head();
- default:
- return new_cond;
- }
- }
- return 0;
-}
-
-
bool st_select_lex::set_nest_level(int new_nest_level)
{
DBUG_ENTER("st_select_lex::set_nest_level");
@@ -9616,12 +9444,13 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
if (have_window_funcs())
{
Item *cond_over_partition_fields;
- check_cond_extraction_for_grouping_fields(thd, cond);
- cond_over_partition_fields=
- build_cond_for_grouping_fields(thd, cond, true);
+ cond->check_pushable_cond_extraction(&Item::pushable_cond_checker_for_grouping_fields,
+ (uchar*)this);
+ cond_over_partition_fields= cond->build_pushable_condition(thd, true);
if (cond_over_partition_fields)
cond_over_partition_fields= cond_over_partition_fields->transform(thd,
&Item::grouping_field_transformer_for_where,
+ FALSE,
(uchar*) this);
if (cond_over_partition_fields)
{
@@ -9636,7 +9465,7 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
if (!join->group_list && !with_sum_func)
{
cond=
- cond->transform(thd, transformer, arg);
+ cond->transform(thd, transformer, FALSE, arg);
if (cond)
{
cond->walk(
@@ -9652,9 +9481,10 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
the WHERE clause of this select.
*/
Item *cond_over_grouping_fields;
- check_cond_extraction_for_grouping_fields(thd, cond);
- cond_over_grouping_fields=
- build_cond_for_grouping_fields(thd, cond, true);
+ cond->check_pushable_cond_extraction(&Item::pushable_cond_checker_for_grouping_fields,
+ (uchar*)this);
+
+ cond_over_grouping_fields= cond->build_pushable_condition(thd, true);
/*
Transform references to the columns of condition that can be pushed
@@ -9663,6 +9493,7 @@ void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond,
if (cond_over_grouping_fields)
cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
&Item::grouping_field_transformer_for_where,
+ FALSE,
(uchar*) this);
if (cond_over_grouping_fields)
@@ -9830,7 +9661,7 @@ st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd, Item *cond)
{
Item *result= cond->transform(thd,
&Item::multiple_equality_transformer,
- (uchar *)this);
+ FALSE, (uchar *)this);
if (!result)
return true;
if (result->type() == Item::COND_ITEM &&
@@ -9878,7 +9709,7 @@ st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd, Item *cond)
{
Item *result= item->transform(thd,
&Item::multiple_equality_transformer,
- (uchar *)item);
+ FALSE, (uchar *)item);
if (!result)
return true;
@@ -10145,7 +9976,8 @@ Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having)
*/
List_iterator_fast<Item> it(attach_to_conds);
Item *item;
- check_cond_extraction_for_grouping_fields(thd, having);
+ having->check_pushable_cond_extraction(&Item::pushable_cond_checker_for_grouping_fields,
+ (uchar*)this);
if (build_pushable_cond_for_having_pushdown(thd, having))
{
attach_to_conds.empty();
@@ -10205,7 +10037,7 @@ Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having)
{
item= item->transform(thd,
&Item::field_transformer_for_having_pushdown,
- (uchar *)this);
+ FALSE, (uchar *)this);
if (item->walk(&Item:: cleanup_processor, 0, STOP_PTR) ||
item->fix_fields(thd, NULL))
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 98bec985a64..22ffe6dabe5 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1496,9 +1496,6 @@ public:
void collect_grouping_fields_for_derived(THD *thd, ORDER *grouping_list);
bool collect_grouping_fields(THD *thd);
bool collect_fields_equal_to_grouping(THD *thd);
- void check_cond_extraction_for_grouping_fields(THD *thd, Item *cond);
- Item *build_cond_for_grouping_fields(THD *thd, Item *cond,
- bool no_to_clones);
List<Window_spec> window_specs;
void prepare_add_window_spec(THD *thd);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 6cc87ddcbb5..61ae4b16413 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -116,7 +116,10 @@ static bool best_extension_by_limited_search(JOIN *join,
uint idx, double record_count,
double read_time, uint depth,
uint prune_level,
- uint use_cond_selectivity);
+ uint use_cond_selectivity,
+ table_map sort_nest_tables,
+ bool nest_created,
+ bool limit_applied_to_nest);
static uint determine_search_depth(JOIN* join);
C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
@@ -124,6 +127,8 @@ static int join_tab_cmp_straight(const void *dummy, const void* ptr1, const void
static int join_tab_cmp_embedded_first(const void *emb, const void* ptr1, const void *ptr2);
C_MODE_END
static uint cache_record_length(JOIN *join,uint index);
+static ulong cache_record_length_for_nest(JOIN *join,uint index);
+
static store_key *get_store_key(THD *thd,
KEYUSE *keyuse, table_map used_tables,
KEY_PART_INFO *key_part, uchar *key_buff,
@@ -151,11 +156,11 @@ static COND *build_equal_items(JOIN *join, COND *cond,
bool ignore_on_conds,
COND_EQUAL **cond_equal_ref,
bool link_equal_fields= FALSE);
-static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
- COND *cond,
- COND_EQUAL *cond_equal,
- void *table_join_idx,
- bool do_substitution);
+COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
+ COND *cond,
+ COND_EQUAL *cond_equal,
+ void *table_join_idx,
+ bool do_substitution);
static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
COND *conds, bool top, bool in_sj);
static bool check_interleaving_with_nj(JOIN_TAB *next);
@@ -184,6 +189,8 @@ static enum_nested_loop_state
end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+enum_nested_loop_state
+end_nest_materialization(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static int join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos);
static int join_read_system(JOIN_TAB *tab);
@@ -231,9 +238,9 @@ static bool test_if_cheaper_ordering(const JOIN_TAB *tab,
ha_rows *new_select_limit,
uint *new_used_key_parts= NULL,
uint *saved_best_key_parts= NULL);
-static int test_if_order_by_key(JOIN *join,
- ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts= NULL);
+int test_if_order_by_key(JOIN *join,
+ ORDER *order, TABLE *table, uint idx,
+ uint *used_key_parts= NULL);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes,
const key_map *map);
@@ -298,7 +305,13 @@ static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
table_map rem_tables);
void set_postjoin_aggr_write_func(JOIN_TAB *tab);
-static Item **get_sargable_cond(JOIN *join, TABLE *table);
+Item **get_sargable_cond(JOIN *join, TABLE *table);
+void find_cost_of_index_with_ordering(THD *thd, const JOIN_TAB *tab,
+ TABLE *table,
+ ha_rows *select_limit_arg,
+ double fanout, double est_best_records,
+ uint nr, double *index_scan_time,
+ Json_writer_object *trace_possible_key);
static
bool build_notnull_conds_for_range_scans(JOIN *join, COND *cond,
@@ -2467,6 +2480,14 @@ int JOIN::optimize_stage2()
if (setup_semijoin_loosescan(this))
DBUG_RETURN(1);
+ if (sort_nest_needed())
+ {
+ if (make_sort_nest(sort_nest_info))
+ DBUG_RETURN(1);
+ substitute_best_fields_for_order_by_items();
+ substitute_base_with_nest_field_items(sort_nest_info);
+ }
+
if (make_join_select(this, select, conds))
{
zero_result_cause=
@@ -2478,22 +2499,9 @@ int JOIN::optimize_stage2()
error= -1; /* if goto err */
/* Optimize distinct away if possible */
- {
- ORDER *org_order= order;
- order=remove_const(this, order,conds,1, &simple_order);
- if (unlikely(thd->is_error()))
- {
- error= 1;
- DBUG_RETURN(1);
- }
+ if (remove_const_from_order_by())
+ DBUG_RETURN(TRUE);
- /*
- If we are using ORDER BY NULL or ORDER BY const_expression,
- return result in any order (even if we are using a GROUP BY)
- */
- if (!order && org_order)
- skip_sort_order= 1;
- }
/*
Check if we can optimize away GROUP BY/DISTINCT.
We can do that if there are no aggregate functions, the
@@ -2714,7 +2722,8 @@ int JOIN::optimize_stage2()
Yet the current implementation of FORCE INDEX hints does not
allow us to do it in a clean manner.
*/
- no_jbuf_after= 1 ? table_count : make_join_orderinfo(this);
+ no_jbuf_after= 1 ? table_count
+ : make_join_orderinfo(this);
// Don't use join buffering when we use MATCH
select_opts_for_readinfo=
@@ -2787,19 +2796,10 @@ int JOIN::optimize_stage2()
if (order && !need_tmp)
{
- /*
- Force using of tmp table if sorting by a SP or UDF function due to
- their expensive and probably non-deterministic nature.
- */
- for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
+ if (is_order_by_expensive())
{
- Item *item= *tmp_order->item;
- if (item->is_expensive())
- {
- /* Force tmp table without sort */
- need_tmp=1; simple_order=simple_group=0;
- break;
- }
+ need_tmp=1;
+ simple_order=simple_group=0;
}
}
@@ -2885,10 +2885,25 @@ int JOIN::optimize_stage2()
else if (order && // ORDER BY wo/ preceding GROUP BY
(simple_order || skip_sort_order)) // which is possibly skippable
{
- if (test_if_skip_sort_order(tab, order, select_limit, false,
- &tab->table->keys_in_use_for_order_by))
+ if (!sort_nest_info)
{
- ordered_index_usage= ordered_index_order_by;
+ if (test_if_skip_sort_order(tab, order, select_limit, false,
+ &tab->table->keys_in_use_for_order_by))
+ {
+ ordered_index_usage= ordered_index_order_by;
+ }
+ }
+ else
+ {
+ JOIN_TAB *first_tab= sort_nest_info->nest_tab;
+ int idx= first_tab->get_index_on_table();
+
+ if (first_tab == join_tab + const_tables &&
+ first_tab->check_if_index_satisfies_ordering(idx))
+ {
+ resetup_access_for_ordering(first_tab, idx);
+ ordered_index_usage= ordered_index_order_by;
+ }
}
}
}
@@ -3587,7 +3602,9 @@ bool JOIN::make_aggr_tables_info()
curr_tab->type != JT_EQ_REF) // Don't sort 1 row
{
// Sort either first non-const table or the last tmp table
- JOIN_TAB *sort_tab= curr_tab;
+ JOIN_TAB *sort_tab= sort_nest_info ?
+ sort_nest_info->nest_tab :
+ curr_tab;
if (add_sorting_to_table(sort_tab, order_arg))
DBUG_RETURN(true);
@@ -3845,7 +3862,7 @@ bool JOIN::setup_subquery_caches()
JOIN_TAB *tab;
if (conds &&
!(conds= conds->transform(thd, &Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
@@ -3854,20 +3871,20 @@ bool JOIN::setup_subquery_caches()
!(tab->select_cond=
tab->select_cond->transform(thd,
&Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
if (tab->cache_select && tab->cache_select->cond)
if (!(tab->cache_select->cond=
tab->cache_select->
cond->transform(thd, &Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
}
if (having &&
!(having= having->transform(thd,
&Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
if (tmp_having)
@@ -3876,7 +3893,7 @@ bool JOIN::setup_subquery_caches()
if (!(tmp_having=
tmp_having->transform(thd,
&Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
}
}
@@ -3891,7 +3908,7 @@ bool JOIN::setup_subquery_caches()
Item *new_item;
if (!(new_item=
item->transform(thd, &Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
if (new_item != item)
{
@@ -3903,7 +3920,7 @@ bool JOIN::setup_subquery_caches()
if (!(*tmp_group->item=
(*tmp_group->item)->transform(thd,
&Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
}
}
@@ -3914,7 +3931,7 @@ bool JOIN::setup_subquery_caches()
if (!(*ord->item=
(*ord->item)->transform(thd,
&Item::expr_cache_insert_transformer,
- NULL)))
+ FALSE, NULL)))
DBUG_RETURN(TRUE);
}
}
@@ -4425,7 +4442,7 @@ JOIN::destroy()
WITH_CONST_TABLES);
tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
- if (tab->aggr)
+ if (tab->aggr || tab->is_sort_nest)
{
free_tmp_table(thd, tab->table);
delete tab->tmp_table_param;
@@ -4731,7 +4748,7 @@ void mark_join_nest_as_const(JOIN *join,
- "t1 LEFT JOIN (...) ON ..." uses the join nest's ON expression.
*/
-static Item **get_sargable_cond(JOIN *join, TABLE *table)
+Item **get_sargable_cond(JOIN *join, TABLE *table)
{
Item **retval;
if (table->pos_in_table_list->on_expr)
@@ -5286,6 +5303,19 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
join->join_tab= stat;
join->make_notnull_conds_for_range_scans();
+ /*
+ Here a call is made to remove the constant from the order by clause,
+ this call would only remove the basic constants. This is done primarily
+ for the ORDER BY LIMIT optimization with the sort-nest.
+ */
+
+ if (join->remove_const_from_order_by())
+ DBUG_RETURN(TRUE);
+
+ if (join->sort_nest_allowed() && !join->is_order_by_expensive())
+ join->sort_nest_possible= TRUE;
+
+ join->propagate_equal_field_for_orderby();
/* Calc how many (possible) matched records in each table */
@@ -5345,6 +5375,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
all select distinct fields participate in one index.
*/
add_group_and_distinct_keys(join, s);
+ s->find_keys_that_can_achieve_ordering();
+ s->get_estimated_record_length();
s->table->cond_selectivity= 1.0;
@@ -5451,13 +5483,14 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (pull_out_semijoin_tables(join))
DBUG_RETURN(TRUE);
- join->join_tab=stat;
join->top_join_tab_count= table_count;
- join->map2table=stat_ref;
- join->table= table_vector;
join->const_tables=const_count;
join->found_const_table_map=found_const_table_map;
+ join->join_tab=stat;
+ join->map2table=stat_ref;
+ join->table= table_vector;
+
if (join->const_tables != join->table_count)
optimize_keyuse(join, keyuse_array);
@@ -7092,6 +7125,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].sj_strategy= SJ_OPT_NONE;
join->positions[idx].use_join_buffer= FALSE;
join->positions[idx].range_rowid_filter_info= 0;
+ join->positions[idx].sort_nest_operation_here= FALSE;
/* Move the const table as down as possible in best_ref */
JOIN_TAB **pos=join->best_ref+idx+1;
@@ -7187,7 +7221,10 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
@param pos OUT Table access plan
@param loose_scan_pos OUT Table plan that uses loosescan, or set cost to
DBL_MAX if not possible.
-
+ @param cardinality contains the estimate of records for the best
+ join order (if the join optimizer is run once
+ to get the best cardinality else it is set to
+ DBL_MAX)
@return
None
*/
@@ -7201,7 +7238,8 @@ best_access_path(JOIN *join,
bool disable_jbuf,
double record_count,
POSITION *pos,
- POSITION *loose_scan_pos)
+ POSITION *loose_scan_pos,
+ table_map sort_nest_tables, bool nest_created)
{
THD *thd= join->thd;
uint use_cond_selectivity= thd->variables.optimizer_use_condition_selectivity;
@@ -7222,6 +7260,8 @@ best_access_path(JOIN *join,
Range_rowid_filter_cost_info *filter= 0;
const char* cause= NULL;
enum join_type best_type= JT_UNKNOWN, type= JT_UNKNOWN;
+ int index_picked= -1;
+ int index_used_for_access= -1;
disable_jbuf= disable_jbuf || idx == join->const_tables;
@@ -7318,7 +7358,11 @@ best_access_path(JOIN *join,
notnull_part|=keyuse->keypart_map;
double tmp2= prev_record_reads(join_positions, idx,
- (found_ref | keyuse->used_tables));
+ (found_ref | keyuse->used_tables),
+ sort_nest_tables,
+ nest_created ?
+ join->fraction_output_for_nest :
+ 1.0);
if (tmp2 < best_prev_record_reads)
{
best_part_found_ref= keyuse->used_tables & ~join->const_table_map;
@@ -7359,7 +7403,10 @@ best_access_path(JOIN *join,
Really, there should be records=0.0 (yes!)
but 1.0 would be probably safer
*/
- tmp= prev_record_reads(join_positions, idx, found_ref);
+ tmp= prev_record_reads(join_positions, idx, found_ref,
+ sort_nest_tables,
+ nest_created ?
+ join->fraction_output_for_nest : 1.0);
records= 1.0;
type= JT_FT;
trace_access_idx.add("access_type", join_type_str[type])
@@ -7388,7 +7435,10 @@ best_access_path(JOIN *join,
type= JT_EQ_REF;
trace_access_idx.add("access_type", join_type_str[type])
.add("index", keyinfo->name);
- tmp = prev_record_reads(join_positions, idx, found_ref);
+ tmp = prev_record_reads(join_positions, idx, found_ref,
+ sort_nest_tables,
+ nest_created ?
+ join->fraction_output_for_nest : 1.0);
records=1.0;
}
else
@@ -7694,12 +7744,75 @@ best_access_path(JOIN *join,
DBUG_ASSERT(tmp >= 0);
}
}
+
+ /*
+ Cost for sorting is added for the cases when ref access does
+ not satisfy the ORDER BY clause
+ An example would be
+ query
+ select * from t1 where t1.a=5 and c=2 order by b;
+
+ we have 2 keys idx1(a,b) and idx2(a,c)
+ so cost of sorting needs to be added for idx2 but not for idx1
+ as it can resolve the ORDER BY clause
+ */
+ double cost_of_sorting= 0;
+ if (join->is_index_with_ordering_allowed(idx))
+ {
+ if (!s->check_if_index_satisfies_ordering(start_key->key))
+ {
+ double sort_cost;
+ sort_cost= join->sort_nest_oper_cost(records, idx,
+ s->get_estimated_record_length());
+ cost_of_sorting= sort_cost;
+ if (unlikely(thd->trace_started()))
+ {
+ trace_access_idx.add("cost_of_sorting", cost_of_sorting);
+ trace_access_idx.add("satisfies_ordering", false);
+ }
+ }
+ else
+ {
+ /*
+ Apply limit here if possible, as ref access here achieves the
+ key that would satisfy the ordering.
+ */
+ double records_before_limit_applied= records;
+ records= COST_MULT(records, join->fraction_output_for_nest);
+ if (unlikely(thd->trace_started()))
+ {
+ trace_access_idx.add("records_before_limit_considered",
+ records_before_limit_applied);
+ trace_access_idx.add("records_after_limit_considered",
+ floor(records));
+ trace_access_idx.add("satisfies_ordering", true);
+ }
+ tmp= records;
+ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
+ if (table->covering_keys.is_set(key))
+ tmp= table->file->keyread_time(key, 1, (ha_rows) tmp);
+ else
+ tmp= table->file->read_time(key, 1,
+ (ha_rows) MY_MIN(tmp,s->worst_seeks));
+ tmp= COST_MULT(tmp, record_count);
+ }
+ }
+
trace_access_idx.add("rows", records).add("cost", tmp);
- if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE)
+ if (tmp + cost_of_sorting + 0.0001 < (best_time -
+ records/(double) TIME_FOR_COMPARE))
{
trace_access_idx.add("chosen", true);
- best_time= COST_ADD(tmp, records/(double) TIME_FOR_COMPARE);
+ best_time= COST_ADD(COST_ADD(tmp, cost_of_sorting),
+ records/(double) TIME_FOR_COMPARE);
+ /*
+ best here is assigned the cost of reading the rows via the index
+ and not the cost of evalutation the part of the where clause
+ satisfied by this ref access. This is done so because we add the
+ cost of evaluating the where clause in
+ best_extension_by_limited_search.
+ */
best= tmp;
best_records= records;
best_key= start_key;
@@ -7768,6 +7881,8 @@ best_access_path(JOIN *join,
trace_access_hash.add("chosen", true);
}
+ index_used_for_access= best_key ? best_key->key : -1;
+ index_picked= index_used_for_access;
/*
Don't test table scan if it can't be better.
Prefer key lookup if we would use the same key for scanning.
@@ -7842,6 +7957,7 @@ best_access_path(JOIN *join,
double rows= record_count * s->found_records;
double access_cost_factor= MY_MIN(tmp / rows, 1.0);
uint key_no= s->quick->index;
+ index_used_for_access= key_no;
filter=
s->table->best_range_rowid_filter_for_partial_join(key_no, rows,
access_cost_factor);
@@ -7866,11 +7982,13 @@ best_access_path(JOIN *join,
{
type= JT_NEXT;
tmp= s->table->file->read_time(s->ref.key, 1, s->records);
+ index_used_for_access= s->ref.key;
}
else // table scan
{
tmp= s->scan_time();
type= JT_ALL;
+ index_used_for_access= -1;
}
if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache
@@ -7944,6 +8062,7 @@ best_access_path(JOIN *join,
join->outer_join)));
spl_plan= 0;
best_type= type;
+ index_picked= index_used_for_access;
}
trace_access_scan.add("chosen", best_key == NULL);
}
@@ -7954,18 +8073,14 @@ best_access_path(JOIN *join,
trace_access_scan.add("cause", "cost");
}
- /* Update the cost information for the current partial plan */
- pos->records_read= records;
- pos->read_time= best;
- pos->key= best_key;
- pos->table= s;
- pos->ref_depend_map= best_ref_depends_map;
- pos->loosescan_picker.loosescan_key= MAX_KEY;
- pos->use_join_buffer= best_uses_jbuf;
- pos->spl_plan= spl_plan;
- pos->range_rowid_filter_info= best_filter;
-
- loose_scan_opt.save_to_position(s, loose_scan_pos);
+ if (!best_key &&
+ idx == join->const_tables &&
+ s->table == join->sort_by_table &&
+ join->unit->select_limit_cnt >= records)
+ {
+ trace_access_scan.add("use_tmp_table", true);
+ join->sort_by_table= (TABLE*) 1; // Must use temporary table
+ }
if (!best_key &&
idx == join->const_tables &&
@@ -7978,6 +8093,76 @@ best_access_path(JOIN *join,
trace_access_scan.end();
trace_paths.end();
+ /*
+ Use the estimate of rows read for a table for range/table scan
+ from TABLE::quick_condition_rows. This is due to the reason
+ records already take into account condition selectivity for the table
+ for range/table scan.
+ */
+
+ double idx_records= best_key ? records : s->table->quick_condition_rows;
+ double idx_time= best;
+ if (join->is_index_with_ordering_allowed(idx))
+ {
+ if (s->check_if_index_satisfies_ordering(index_picked))
+ {
+ /*
+ Removing the selectivity of limit taken into account for an access
+ which could resolve the ORDER BY clause by using an index.
+ This is done because we apply the selectivity again in the function
+ get_best_index_for_order_by_limit, so we make sure that the
+ selectivity is not applied twice.
+ */
+ idx_records= MY_MIN(s->table->stat_records(),
+ COST_MULT(idx_records,
+ 1 / join->fraction_output_for_nest));
+ }
+ else
+ {
+ /*
+ Also adding here the cost of sorting for best access method that cannot
+ resolve the ordering. This is done so that the cost comparison is more
+ accurate when we try to pick an index that can resolve the ordering
+ in the function get_best_index_for_order_by_limit().
+ */
+ double sort_cost;
+ sort_cost= join->sort_nest_oper_cost(idx_records, idx,
+ s->get_estimated_record_length());
+ idx_time+= sort_cost;
+ }
+ }
+ int idx_no= get_best_index_for_order_by_limit(s, join->select_limit,
+ &idx_time,
+ &idx_records,
+ index_picked, idx);
+
+ if (idx_no >= 0)
+ {
+ best= idx_time;
+ best_key= 0;
+ best_ref_depends_map= 0;
+ best_filter= 0;
+ spl_plan= 0;
+ records= idx_records;
+ index_picked= idx_no;
+ }
+
+
+ /* Update the cost information for the current partial plan */
+ pos->records_read= records;
+ pos->read_time= best;
+ pos->key= best_key;
+ pos->table= s;
+ pos->ref_depend_map= best_ref_depends_map;
+ pos->loosescan_picker.loosescan_key= MAX_KEY;
+ pos->use_join_buffer= best_uses_jbuf;
+ pos->spl_plan= spl_plan;
+ pos->range_rowid_filter_info= best_filter;
+ pos->sort_nest_operation_here= FALSE;
+ pos->index_no= index_picked;
+
+ loose_scan_opt.save_to_position(s, loose_scan_pos);
+
if (unlikely(thd->trace_started()))
print_best_access_for_table(thd, pos, best_type);
@@ -8178,10 +8363,16 @@ choose_plan(JOIN *join, table_map join_tables)
if (search_depth == 0)
/* Automatically determine a reasonable value for 'search_depth' */
search_depth= determine_search_depth(join);
+
+ if (join->sort_nest_possible &&
+ join->estimate_cardinality_for_join(join_tables))
+ DBUG_RETURN(TRUE);
+
if (greedy_search(join, join_tables, search_depth, prune_level,
use_cond_selectivity))
DBUG_RETURN(TRUE);
}
+ trace_plan.end();
/*
Store the cost of this query into a user variable
@@ -8469,7 +8660,8 @@ optimize_straight_join(JOIN *join, table_map join_tables)
/* Find the best access method from 's' to the current partial plan */
best_access_path(join, s, join_tables, join->positions, idx,
disable_jbuf, record_count,
- position, &loose_scan_pos);
+ position, &loose_scan_pos,
+ 0, FALSE);
/* compute the cost of the new plan extended with 's' */
record_count= COST_MULT(record_count, position->records_read);
@@ -8488,6 +8680,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
join_tables);
position->cond_selectivity= pushdown_cond_selectivity;
+ record_count= record_count*pushdown_cond_selectivity;
++idx;
}
@@ -8600,6 +8793,9 @@ greedy_search(JOIN *join,
JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
// ==join->tables or # tables in the sj-mat nest we're optimizing
uint n_tables __attribute__((unused));
+
+ join->set_fraction_output_for_nest();
+
DBUG_ENTER("greedy_search");
/* number of tables that remain to be optimized */
@@ -8613,9 +8809,15 @@ greedy_search(JOIN *join,
do {
/* Find the extension of the current QEP with the lowest cost */
join->best_read= DBL_MAX;
- if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
- read_time, search_depth, prune_level,
- use_cond_selectivity))
+ table_map sort_nest_tables= 0;
+ if (best_extension_by_limited_search(join, remaining_tables, idx,
+ record_count, read_time,
+ search_depth,
+ (join->sort_nest_possible ? 0 :
+ prune_level),
+ use_cond_selectivity,
+ sort_nest_tables, FALSE,
+ FALSE))
DBUG_RETURN(TRUE);
/*
'best_read < DBL_MAX' means that optimizer managed to find
@@ -9338,6 +9540,15 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
(values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
@param use_cond_selectivity specifies how the selectivity of the conditions
pushed to a table should be taken into account
+ @param sort_nest_tables set of tables that satify the ORDER BY clause
+ If ORDER BY clause is not satisfield it contains
+ set of all tables in the prefix
+ @param nest_created set to TRUE if a prefix join order satisfied
+ the ORDER BY clause and we can add a sort-nest
+ on the found prefix
+ @param limit_applied_to_nest TRUE if limit is applied for the sort-nest
+ cost calculation
+ FALSE otherwise
@retval
FALSE ok
@@ -9353,7 +9564,10 @@ best_extension_by_limited_search(JOIN *join,
double read_time,
uint search_depth,
uint prune_level,
- uint use_cond_selectivity)
+ uint use_cond_selectivity,
+ table_map sort_nest_tables,
+ bool nest_created,
+ bool limit_applied_to_nest)
{
DBUG_ENTER("best_extension_by_limited_search");
@@ -9379,7 +9593,24 @@ best_extension_by_limited_search(JOIN *join,
JOIN_TAB *s;
double best_record_count= DBL_MAX;
double best_read_time= DBL_MAX;
- bool disable_jbuf= join->thd->variables.join_cache_level == 0;
+ bool disable_jbuf= (join->thd->variables.join_cache_level == 0) ||
+ nest_created;
+ bool save_prefix_resolves_ordering= join->prefix_resolves_ordering;
+
+ if (nest_created && !limit_applied_to_nest)
+ {
+ double original_record_count= record_count;
+ record_count= COST_MULT(record_count, join->fraction_output_for_nest);
+ limit_applied_to_nest= TRUE;
+
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object apply_limit(thd);
+ apply_limit.add("original_record_count", original_record_count);
+ apply_limit.add("record_count_after_limit_applied", record_count);
+ }
+
+ }
DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time,
"part_plan"););
@@ -9392,6 +9623,7 @@ best_extension_by_limited_search(JOIN *join,
if (join->emb_sjm_nest)
allowed_tables= join->emb_sjm_nest->sj_inner_tables & ~join->const_table_map;
+ int index_used;
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{
table_map real_table_bit= s->table->map;
@@ -9408,12 +9640,31 @@ best_extension_by_limited_search(JOIN *join,
{
trace_plan_prefix(join, idx, remaining_tables);
trace_one_table.add_table_name(s);
+ if (nest_created)
+ trace_sort_nest(join, idx, sort_nest_tables);
}
/* Find the best access method from 's' to the current partial plan */
POSITION loose_scan_pos;
best_access_path(join, s, remaining_tables, join->positions, idx,
- disable_jbuf, record_count, position, &loose_scan_pos);
+ disable_jbuf, record_count, position, &loose_scan_pos,
+ sort_nest_tables, nest_created);
+
+ /*
+ sort_nest_operation_here is set to TRUE here in the special case
+ when we only have one table in the join. Generally
+ sort_nest_operation_here is set when we check if ORDER BY clause is
+ satisfied by the partial join order, in the sort-nest branch of
+ best_extension_by_limited_search.
+ */
+ index_used= position->index_no;
+ if ((join->table_count - join->const_tables == 1) &&
+ join->is_index_with_ordering_allowed(idx) &&
+ s->check_if_index_satisfies_ordering(index_used))
+ {
+ position->sort_nest_operation_here= TRUE;
+ }
+>>>>>>> ORDER BY LIMIT
/* Compute the cost of extending the plan with 's' */
current_record_count= COST_MULT(record_count, position->records_read);
@@ -9421,10 +9672,10 @@ best_extension_by_limited_search(JOIN *join,
? position->range_rowid_filter_info->get_cmp_gain(current_record_count)
: 0;
current_read_time=COST_ADD(read_time,
- COST_ADD(position->read_time -
- filter_cmp_gain,
- current_record_count /
- (double) TIME_FOR_COMPARE));
+ COST_ADD(position->read_time -
+ filter_cmp_gain,
+ current_record_count /
+ (double) TIME_FOR_COMPARE));
if (unlikely(thd->trace_started()))
{
@@ -9496,8 +9747,51 @@ best_extension_by_limited_search(JOIN *join,
double partial_join_cardinality= current_record_count *
pushdown_cond_selectivity;
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
- { /* Recursively expand the current partial plan */
+ {
+ /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
+
+ if (join->is_index_with_ordering_allowed(idx) &&
+ s->check_if_index_satisfies_ordering(index_used))
+ limit_applied_to_nest= TRUE;
+
+ /*
+ TODO varun:
+ 1) get an optimizer switch to enable or disable the sort
+ nest instead of a system variable
+ */
+ if (!nest_created &&
+ (join->prefix_resolves_ordering ||
+ join->consider_adding_sort_nest(sort_nest_tables |
+ real_table_bit)))
+ {
+ // SORT_NEST branch
+ join->positions[idx].sort_nest_operation_here= TRUE;
+ join->prefix_resolves_ordering= TRUE;
+ double sorting_cost= 0;
+ if (s->needs_filesort(idx, index_used))
+ {
+ ulong rec_len= cache_record_length_for_nest(join, idx);
+ sorting_cost= join->sort_nest_oper_cost(partial_join_cardinality,
+ idx, rec_len);
+ trace_one_table.add("cost_of_sorting", sorting_cost);
+ }
+ Json_writer_array trace_rest(thd, "rest_of_plan");
+ if (best_extension_by_limited_search(join,
+ remaining_tables & ~real_table_bit,
+ idx + 1,
+ partial_join_cardinality,
+ COST_ADD(current_read_time,
+ sorting_cost),
+ search_depth - 1,
+ prune_level,
+ use_cond_selectivity,
+ sort_nest_tables | real_table_bit,
+ TRUE, limit_applied_to_nest))
+ DBUG_RETURN(TRUE);
+ join->positions[idx].sort_nest_operation_here= FALSE;
+ trace_rest.end();
+ }
Json_writer_array trace_rest(thd, "rest_of_plan");
if (best_extension_by_limited_search(join,
remaining_tables & ~real_table_bit,
@@ -9506,26 +9800,53 @@ best_extension_by_limited_search(JOIN *join,
current_read_time,
search_depth - 1,
prune_level,
- use_cond_selectivity))
+ use_cond_selectivity,
+ nest_created ? sort_nest_tables :
+ sort_nest_tables | real_table_bit,
+ nest_created,
+ limit_applied_to_nest))
DBUG_RETURN(TRUE);
+ trace_rest.end();
+
+ if (!idx)
+ limit_applied_to_nest= FALSE;
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
else
- { /*
+ {
+ /*
'join' is either the best partial QEP with 'search_depth' relations,
or the best complete QEP so far, whichever is smaller.
*/
- if (join->sort_by_table &&
- join->sort_by_table !=
- join->positions[join->const_tables].table->table)
- /*
- We may have to make a temp table, note that this is only a
- heuristic since we cannot know for sure at this point.
- Hence it may be wrong.
- */
- current_read_time= COST_ADD(current_read_time, current_record_count);
+ if (!nest_created &&
+ ((join->sort_by_table &&
+ join->sort_by_table !=
+ join->positions[join->const_tables].table->table) ||
+ join->sort_nest_possible))
+ {
+ double cost;
+ if (join->sort_nest_possible)
+ {
+ ulong rec_len= cache_record_length_for_nest(join, idx);
+ cost= join->sort_nest_oper_cost(partial_join_cardinality, idx,
+ rec_len);
+ trace_one_table.add("cost_of_sorting", cost);
+ }
+ else
+ {
+ /*
+ We may have to make a temp table, note that this is only a
+ heuristic since we cannot know for sure at this point.
+ Hence it may be wrong.
+ */
+ cost= current_record_count;
+ }
+
+ current_read_time= COST_ADD(current_read_time, cost);
+ }
trace_one_table.add("estimated_join_cardinality",
partial_join_cardinality);
+
if (current_read_time < join->best_read)
{
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
@@ -9539,6 +9860,7 @@ best_extension_by_limited_search(JOIN *join,
current_read_time,
"full_plan"););
}
+ join->prefix_resolves_ordering= save_prefix_resolves_ordering;
restore_prev_nj_state(s);
restore_prev_sj_state(remaining_tables, s, idx);
}
@@ -9547,6 +9869,24 @@ best_extension_by_limited_search(JOIN *join,
}
+ulong JOIN_TAB::get_estimated_record_length()
+{
+ if (used_rec_length_for_nest)
+ return used_rec_length_for_nest;
+
+ Field **f_ptr, *field;
+ MY_BITMAP *read_set= table->read_set;
+ ulong rec_length= 0;
+ for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ if (bitmap_is_set(read_set, field->field_index))
+ rec_length+=field->pack_length();
+ }
+ used_rec_length_for_nest= rec_length;
+ return used_rec_length_for_nest;
+}
+
+
/**
Find how much space the prevous read not const tables takes in cache.
*/
@@ -9766,6 +10106,27 @@ cache_record_length(JOIN *join,uint idx)
/*
+ @brief
+ Get an estimate of record length for a materialized nest.
+*/
+
+static ulong
+cache_record_length_for_nest(JOIN *join,uint idx)
+{
+ uint length=0;
+ JOIN_TAB **pos,**end;
+
+ for (pos=join->best_ref+join->const_tables,end=join->best_ref+idx ;
+ pos != end ;
+ pos++)
+ {
+ JOIN_TAB *join_tab= *pos;
+ length+= join_tab->get_estimated_record_length();
+ }
+ return length;
+}
+
+/*
Get the number of different row combinations for subset of partial join
SYNOPSIS
@@ -9817,15 +10178,20 @@ cache_record_length(JOIN *join,uint idx)
*/
double
-prev_record_reads(const POSITION *positions, uint idx, table_map found_ref)
+prev_record_reads(const POSITION *positions, uint idx, table_map found_ref,
+ table_map sort_nest_tables,
+ double fraction_output_for_nest)
{
double found=1.0;
const POSITION *pos_end= positions - 1;
+ bool apply_limit= FALSE;
for (const POSITION *pos= positions + idx - 1; pos != pos_end; pos--)
{
if (pos->table->table->map & found_ref)
{
found_ref|= pos->ref_depend_map;
+ if (pos->table->table->map & sort_nest_tables)
+ apply_limit= TRUE;
/*
For the case of "t1 LEFT JOIN t2 ON ..." where t2 is a const table
with no matching row we will get position[t2].records_read==0.
@@ -9846,6 +10212,8 @@ prev_record_reads(const POSITION *positions, uint idx, table_map found_ref)
found= COST_MULT(found, pos->records_read);
}
}
+ if (apply_limit)
+ found= COST_MULT(found, fraction_output_for_nest);
return found;
}
@@ -10177,6 +10545,8 @@ bool JOIN::get_best_combination()
table_map used_tables;
JOIN_TAB *j;
KEYUSE *keyuse;
+ uint n_tables;
+ table_map nest_tables_map= 0;
DBUG_ENTER("get_best_combination");
/*
@@ -10208,17 +10578,34 @@ bool JOIN::get_best_combination()
full_join=0;
hash_join= FALSE;
+ int index_no= best_positions[const_tables].index_no;
+
fix_semijoin_strategies_for_picked_join_order(this);
top_join_tab_count= get_number_of_tables_at_top_level(this);
if (!(join_tab= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*
(top_join_tab_count + aggr_tables))))
DBUG_RETURN(TRUE);
-
+
+ for (j=join_tab, tablenr=0 ; tablenr < top_join_tab_count + aggr_tables;
+ tablenr++,j++)
+ bzero((void*)j, sizeof(JOIN_TAB));
+
+ if (check_if_sort_nest_present(&n_tables, &nest_tables_map))
+ {
+ if (create_sort_nest_info(n_tables, nest_tables_map))
+ DBUG_RETURN(TRUE);
+
+ if (sort_nest_needed())
+ join_tab[const_tables + n_tables].is_sort_nest= TRUE;
+ else
+ setup_index_use_for_ordering(index_no);
+ }
+
JOIN_TAB_RANGE *root_range;
if (!(root_range= new (thd->mem_root) JOIN_TAB_RANGE))
DBUG_RETURN(TRUE);
- root_range->start= join_tab;
+ root_range->start= join_tab;
/* root_range->end will be set later */
join_tab_ranges.empty();
@@ -10232,6 +10619,23 @@ bool JOIN::get_best_combination()
{
TABLE *form;
POSITION *cur_pos= &best_positions[tablenr];
+
+ if (j->is_sort_nest)
+ {
+ uint tables= n_tables;
+ j->join= this;
+ j->table= NULL;
+ j->ref.key = -1;
+ j->on_expr_ref= (Item**) &null_ptr;
+ j->is_sort_nest= TRUE;
+ j->records_read= calculate_record_count_for_sort_nest(tables);
+ j->records= (ha_rows) j->records_read;
+ j->cond_selectivity= 1.0;
+ sort_nest_info->nest_tab= j;
+ tablenr--;
+ continue;
+ }
+
if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE ||
cur_pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
{
@@ -10263,6 +10667,10 @@ bool JOIN::get_best_combination()
DBUG_RETURN(TRUE);
jt_range->start= jt;
jt_range->end= jt + sjm->tables;
+ JOIN_TAB *tab;
+ for (tab= jt; tab < jt_range->end; tab++)
+ bzero((void*)tab, sizeof(JOIN_TAB));
+
join_tab_ranges.push_back(jt_range, thd->mem_root);
j->bush_children= jt_range;
sjm_nest_end= jt + sjm->tables;
@@ -10293,7 +10701,7 @@ bool JOIN::get_best_combination()
j->type=JT_ALL;
if (best_positions[tablenr].use_join_buffer &&
tablenr != const_tables)
- full_join= 1;
+ full_join= 1;
}
/*if (best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN)
@@ -10332,6 +10740,8 @@ bool JOIN::get_best_combination()
used_tables= OUTER_REF_TABLE_BIT; // Outer row is already read
for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++)
{
+ if (j->is_sort_nest)
+ j++;
if (j->bush_children)
j= j->bush_children->start;
@@ -11036,6 +11446,8 @@ make_outerjoin_info(JOIN *join)
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
+ if (tab->is_sort_nest)
+ continue;
TABLE *table= tab->table;
TABLE_LIST *tbl= table->pos_in_table_list;
TABLE_LIST *embedding= tbl->embedding;
@@ -11211,10 +11623,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB *tab;
table_map current_map;
i= join->const_tables;
+ Item *saved_cond= cond;
+ Sort_nest_info *sort_nest_info= join->sort_nest_info;
+ if (join->sort_nest_needed())
+ cond= sort_nest_info->get_nest_cond();
+
for (tab= first_depth_first_tab(join); tab;
tab= next_depth_first_tab(join, tab))
{
bool is_hj;
+ if (tab->is_sort_nest)
+ {
+ cond= saved_cond;
+ continue;
+ }
/*
first_inner is the X in queries like:
@@ -11437,7 +11859,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tab->table->reginfo.impossible_range)
DBUG_RETURN(1);
}
- else if (tab->type == JT_ALL && ! use_quick_range)
+ else if (tab->type == JT_ALL && ! use_quick_range &&
+ !join->sort_nest_info)
{
if (!tab->const_keys.is_clear_all() &&
tab->table->reginfo.impossible_range)
@@ -12243,6 +12666,35 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
}
+enum_nested_loop_state
+end_nest_materialization(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
+{
+ int error;
+ THD *thd= join->thd;
+ Sort_nest_info *nest_info= join->sort_nest_info;
+ DBUG_ENTER("end_sj_materialize");
+ if (!end_of_records)
+ {
+ TABLE *table= nest_info->table;
+ fill_record(thd, table, table->field,
+ nest_info->nest_base_table_cols, TRUE, FALSE);
+
+ if (unlikely(thd->is_error()))
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
+ if (unlikely((error= table->file->ha_write_tmp_row(table->record[0]))))
+ {
+ /* create_myisam_from_heap will generate error if needed */
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP) &&
+ create_internal_tmp_table_from_heap(thd, table,
+ nest_info->tmp_table_param.start_recinfo,
+ &nest_info->tmp_table_param.recinfo,
+ error, 1, NULL))
+ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
+ }
+ }
+ DBUG_RETURN(NESTED_LOOP_OK);
+}
+
/*
Check whether a join buffer can be used to join the specified table
@@ -12410,6 +12862,10 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if (cache_level == 0 || !prev_tab)
return 0;
+
+ if (!join->is_join_buffering_allowed(tab))
+ return 0;
+
if (force_unlinked_cache && (cache_level%2 == 0))
cache_level--;
@@ -12666,7 +13122,9 @@ restart:
*/
prev_tab= tab - 1;
if (tab == join->join_tab + join->const_tables ||
- (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab))
+ (tab->bush_root_tab &&
+ tab->bush_root_tab->bush_children->start == tab) ||
+ tab->is_sort_nest)
prev_tab= NULL;
switch (tab->type) {
@@ -12695,7 +13153,7 @@ restart:
default:
tab->used_join_cache_level= 0;
}
- if (!tab->bush_children)
+ if (!tab->bush_children && !tab->is_sort_nest)
idx++;
}
}
@@ -12838,17 +13296,20 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
Later it should be improved.
*/
- if (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab)
+ if ((tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab) ||
+ tab->is_sort_nest)
prev_tab= NULL;
- DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table);
+ DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table
+ || tab->is_sort_nest);
tab->partial_join_cardinality= join->best_positions[i].records_read *
(prev_tab? prev_tab->partial_join_cardinality : 1);
- if (!tab->bush_children)
+ if (!tab->bush_children && !tab->is_sort_nest)
i++;
}
check_join_cache_usage_for_tables(join, options, no_jbuf_after);
+ Sort_nest_info *sort_nest_info= join->sort_nest_info;
JOIN_TAB *first_tab;
for (tab= first_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
@@ -12875,7 +13336,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
end_sj_materialize.
*/
if (!(tab->bush_root_tab &&
- tab->bush_root_tab->bush_children->end == tab + 1))
+ tab->bush_root_tab->bush_children->end == tab + 1) &&
+ !(sort_nest_info && tab+1 == sort_nest_info->nest_tab))
{
tab->next_select=sub_select; /* normal select */
}
@@ -12990,8 +13452,11 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
tab->select->quick->index != MAX_KEY && //not index_merge
table->covering_keys.is_set(tab->select->quick->index))
table->file->ha_start_keyread(tab->select->quick->index);
- else if (!table->covering_keys.is_clear_all() &&
- !(tab->select && tab->select->quick))
+ else if ((!table->covering_keys.is_clear_all() &&
+ !(tab->select && tab->select->quick)) ||
+ (sort_nest_info && sort_nest_info->nest_tab == tab &&
+ sort_nest_info->number_of_tables() == 1 &&
+ sort_nest_info->index_used >= 0))
{ // Only read index tree
if (tab->loosescan_match_tab)
tab->index= tab->loosescan_key;
@@ -13010,9 +13475,17 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
tab->index= table->s->primary_key;
else
#endif
- tab->index=find_shortest_key(table, & table->covering_keys);
+ if (sort_nest_info && sort_nest_info->number_of_tables() == 1 &&
+ sort_nest_info->nest_tab == tab &&
+ sort_nest_info->index_used >= 0)
+ {
+ tab->index= sort_nest_info->index_used;
+ tab->limit= tab->records_read;
+ }
+ else
+ tab->index=find_shortest_key(table, &table->covering_keys);
}
- tab->read_first_record= join_read_first;
+ tab->read_first_record= join_read_first;
/* Read with index_first / index_next */
tab->type= tab->type == JT_ALL ? JT_NEXT : JT_HASH_NEXT;
}
@@ -13065,7 +13538,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
It could be that sort_by_tab==NULL, and the plan is to use filesort()
on the first table.
*/
- if (join->order)
+ if (join->order && !join->sort_nest_info)
{
join->simple_order= 0;
join->need_tmp= 1;
@@ -13823,6 +14296,8 @@ static void update_depend_map(JOIN *join)
join_tab;
join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS))
{
+ if (join_tab->is_sort_nest)
+ continue;
TABLE_REF *ref= &join_tab->ref;
table_map depend_map=0;
Item **item=ref->items;
@@ -14113,6 +14588,22 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
}
+bool JOIN::remove_const_from_order_by()
+{
+ ORDER *org_order= order;
+ order=remove_const(this, order,conds,1, &simple_order);
+ if (unlikely(thd->is_error()))
+ return TRUE;
+ /*
+ If we are using ORDER BY NULL or ORDER BY const_expression,
+ return result in any order (even if we are using a GROUP BY)
+ */
+ if (!order && org_order)
+ skip_sort_order= 1;
+ return FALSE;
+}
+
+
/**
Filter out ORDER items those are equal to constants in WHERE
@@ -15594,11 +16085,11 @@ Item *eliminate_item_equal(THD *thd, COND *cond, COND_EQUAL *upper_levels,
The transformed condition, or NULL in case of error
*/
-static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
- COND *cond,
- COND_EQUAL *cond_equal,
- void *table_join_idx,
- bool do_substitution)
+COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
+ COND *cond,
+ COND_EQUAL *cond_equal,
+ void *table_join_idx,
+ bool do_substitution)
{
Item_equal *item_equal;
COND *org_cond= cond; // Return this in case of fatal error
@@ -15719,7 +16210,7 @@ static COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
{
REPLACE_EQUAL_FIELD_ARG arg= {item_equal, context_tab};
if (!(cond= cond->transform(thd, &Item::replace_equal_field,
- (uchar *) &arg)))
+ FALSE, (uchar *) &arg)))
return 0;
}
cond_equal= cond_equal->upper_levels;
@@ -16716,7 +17207,8 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
best_access_path(join, rs, reopt_remaining_tables,
join->positions, i,
TRUE, rec_count,
- &pos, &loose_scan_pos);
+ &pos, &loose_scan_pos,
+ 0, FALSE);
}
else
pos= join->positions[i];
@@ -17553,6 +18045,16 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
}
}
}
+ else
+ {
+ if (!comp_item)
+ return 0;
+ Item_equal *item_eq= comp_item->get_item_equal();
+ if (!item_eq || !item_eq->get_const())
+ return 0;
+ *const_item= item_eq->get_const();
+ return 1;
+ }
return 0;
}
@@ -19847,6 +20349,10 @@ do_select(JOIN *join, Procedure *procedure)
JOIN_TAB *join_tab= join->join_tab +
(join->tables_list ? join->const_tables : 0);
+ Sort_nest_info *sort_nest_info= join->sort_nest_info;
+ join_tab= sort_nest_info ? sort_nest_info->nest_tab
+ : join_tab;
+
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
error= NESTED_LOOP_NO_MORE_ROWS;
else
@@ -22521,9 +23027,9 @@ part_of_refkey(TABLE *table,Field *field)
-1 Reverse key can be used
*/
-static int test_if_order_by_key(JOIN *join,
- ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts)
+int test_if_order_by_key(JOIN *join,
+ ORDER *order, TABLE *table, uint idx,
+ uint *used_key_parts)
{
KEY_PART_INFO *key_part,*key_part_end;
key_part=table->key_info[idx].key_part;
@@ -22545,6 +23051,8 @@ static int test_if_order_by_key(JOIN *join,
for (; order ; order=order->next, const_key_parts>>=1)
{
+ if (order->item[0]->real_item()->type() != Item::FIELD_ITEM)
+ DBUG_RETURN(0);
Item_field *item_field= ((Item_field*) (*order->item)->real_item());
Field *field= item_field->field;
int flag;
@@ -26109,6 +26617,13 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
eta->table_name.copy(table_name_buffer, len, cs);
}
+ else if (is_sort_nest)
+ {
+ size_t len= my_snprintf(table_name_buffer,
+ sizeof(table_name_buffer)-1,
+ "<sort-nest>");
+ eta->table_name.copy(table_name_buffer, len, cs);
+ }
else
{
TABLE_LIST *real_table= table->pos_in_table_list;
@@ -26481,6 +26996,14 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
prev_table->derived_select_number);
eta->firstmatch_table_name.append(namebuf, len);
}
+ else if(do_firstmatch->is_sort_nest)
+ {
+ char namebuf[NAME_LEN];
+ /* Derived table name generation */
+ size_t len= my_snprintf(namebuf, sizeof(namebuf)-1,
+ "<sort-nest>");
+ eta->firstmatch_table_name.append(namebuf, len);
+ }
else
eta->firstmatch_table_name.append(&prev_table->pos_in_table_list->alias);
}
@@ -27744,12 +28267,9 @@ void JOIN::cache_const_exprs()
to use a full index scan on this index).
*/
-static bool get_range_limit_read_cost(const JOIN_TAB *tab,
- const TABLE *table,
- ha_rows table_records,
- uint keynr,
- ha_rows rows_limit,
- double *read_time)
+bool get_range_limit_read_cost(const JOIN_TAB *tab, const TABLE *table,
+ ha_rows table_records, uint keynr,
+ ha_rows rows_limit, double *read_time)
{
bool res= false;
/*
@@ -28103,78 +28623,11 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
select_limit= (ha_rows) (select_limit*rec_per_key);
} /* group */
- /*
- If tab=tk is not the last joined table tn then to get first
- L records from the result set we can expect to retrieve
- only L/fanout(tk,tn) where fanout(tk,tn) says how many
- rows in the record set on average will match each row tk.
- Usually our estimates for fanouts are too pessimistic.
- So the estimate for L/fanout(tk,tn) will be too optimistic
- and as result we'll choose an index scan when using ref/range
- access + filesort will be cheaper.
- */
- select_limit= (ha_rows) (select_limit < fanout ?
- 1 : select_limit/fanout);
-
- /*
- refkey_rows_estimate is E(#rows) produced by the table access
- strategy that was picked without regard to ORDER BY ... LIMIT.
-
- It will be used as the source of selectivity data.
- Use table->cond_selectivity as a better estimate which includes
- condition selectivity too.
- */
- {
- // we use MIN(...), because "Using LooseScan" queries have
- // cond_selectivity=1 while refkey_rows_estimate has a better
- // estimate.
- refkey_rows_estimate= MY_MIN(refkey_rows_estimate,
- ha_rows(table_records *
- table->cond_selectivity));
- }
+ find_cost_of_index_with_ordering(thd, tab, table, &select_limit,
+ fanout, refkey_rows_estimate,
+ nr, &index_scan_time,
+ &possible_key);
- /*
- We assume that each of the tested indexes is not correlated
- with ref_key. Thus, to select first N records we have to scan
- N/selectivity(ref_key) index entries.
- selectivity(ref_key) = #scanned_records/#table_records =
- refkey_rows_estimate/table_records.
- In any case we can't select more than #table_records.
- N/(refkey_rows_estimate/table_records) > table_records
- <=> N > refkey_rows_estimate.
- */
-
- if (select_limit > refkey_rows_estimate)
- select_limit= table_records;
- else
- select_limit= (ha_rows) (select_limit *
- (double) table_records /
- refkey_rows_estimate);
- possible_key.add("updated_limit", select_limit);
- rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1);
- set_if_bigger(rec_per_key, 1);
- /*
- Here we take into account the fact that rows are
- accessed in sequences rec_per_key records in each.
- Rows in such a sequence are supposed to be ordered
- by rowid/primary key. When reading the data
- in a sequence we'll touch not more pages than the
- table file contains.
- TODO. Use the formula for a disk sweep sequential access
- to calculate the cost of accessing data rows for one
- index entry.
- */
- index_scan_time= select_limit/rec_per_key *
- MY_MIN(rec_per_key, table->file->scan_time());
- double range_scan_time;
- if (get_range_limit_read_cost(tab, table, table_records, nr,
- select_limit, &range_scan_time))
- {
- possible_key.add("range_scan_time", range_scan_time);
- if (range_scan_time < index_scan_time)
- index_scan_time= range_scan_time;
- }
- possible_key.add("index_scan_time", index_scan_time);
if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
index_scan_time < read_time)
@@ -28708,6 +29161,151 @@ select_handler *SELECT_LEX::find_select_handler(THD *thd)
}
+/*
+ @brief
+ Check if the items in the ORDER BY clause are expensive or not.
+
+ @details
+ In this function we walk through the ORDER BY list and check if
+ the elements in the list are expensive to calculate or not.
+ This is done so that we can force computing the join first, store
+ the result in a temporary table and then sort the temporary table.
+
+ @retval
+ TRUE ORDER BY clause is expensive
+ FALSE Otherwise
+*/
+
+bool JOIN::is_order_by_expensive()
+{
+ /*
+ Force using of temp table if sorting by a SP or UDF function due to
+ their expensive and probably non-deterministic nature.
+ */
+ for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
+ {
+ Item *item= *tmp_order->item;
+ if (item->is_expensive())
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+/*
+ @brief
+ Estimate the cardinality for a join
+
+ @param
+ joined_tables map of all the non-const tables of the join
+
+ @details
+ Run the join planner to get an estimate of cardinality for a join.
+
+ @note
+ This is currently used by the ORDER BY LIMIT optimization with the
+ sort-nest. This is a very expensive way to compute cardinality.
+
+ The cardinality of the join remains the same irrespective of the way the
+ tables are joined theoretically but that is not the case with our join
+ planner.
+
+ Still we would like to speed this process of getting the estimates of
+ cardinality for a join by reducing the number of permutations of
+ join orders to be considered. We can do this by running the join planner
+ with a small value for system variable optimizer_search_depth.
+
+ @retval
+ TRUE error
+ FALSE otherwise
+*/
+
+bool JOIN::estimate_cardinality_for_join(table_map joined_tables)
+{
+ Json_writer_temp_disable disable_tracing(thd);
+ uint use_cond_selectivity;
+ uint search_depth= thd->variables.optimizer_search_depth;
+ if (search_depth == 0)
+ search_depth= determine_search_depth(this);
+ uint prune_level= thd->variables.optimizer_prune_level;
+ use_cond_selectivity= thd->variables.optimizer_use_condition_selectivity;
+
+ TABLE *save_sort_by_table= sort_by_table;
+ JOIN_TAB *save_best_ref[MAX_TABLES];
+ for (uint i= 0; i < table_count; i++)
+ save_best_ref[i]= best_ref[i];
+
+ get_cardinality_estimate= TRUE;
+ cardinality_estimate= DBL_MAX;
+
+ if (greedy_search(this, joined_tables, search_depth, prune_level,
+ use_cond_selectivity))
+ return TRUE;
+
+ set_if_bigger(join_record_count, 1);
+ cardinality_estimate= join_record_count;
+
+ cur_embedding_map= 0;
+ reset_nj_counters(this, join_list);
+ cur_sj_inner_tables= 0;
+ get_cardinality_estimate= FALSE;
+ sort_by_table= save_sort_by_table;
+
+ for (uint i= 0; i < table_count; i++)
+ best_ref[i]= save_best_ref[i];
+
+ return FALSE;
+}
+
+
+/*
+ @brief
+ Re-setup accesses that use index on the first non-const table for ordering
+
+ @param
+ tab table whose access needs to be re-setup
+ idx index used to access first non-const table
+
+ @details
+ Re-setup the way to access index when the ordering is in DESCENDING order.
+ Also cancel the Index Condition Pushdown for the indexes that need to
+ do the ordering in reverse order.
+*/
+
+void resetup_access_for_ordering(JOIN_TAB* tab, int idx)
+{
+ JOIN *join= tab->join;
+ int direction= test_if_order_by_key(join, join->order, tab->table, idx);
+ if (direction == -1)
+ {
+ if (tab->type == JT_REF || tab->type == JT_EQ_REF)
+ {
+ tab->read_first_record= join_read_last_key;
+ tab->read_record.read_record_func= join_read_prev_same;
+ /*
+ Cancel Pushed Index Condition, as it doesn't work for reverse scans.
+ */
+ if (tab->select && tab->select->pre_idx_push_select_cond)
+ {
+ tab->set_cond(tab->select->pre_idx_push_select_cond);
+ tab->table->file->cancel_pushed_idx_cond();
+ }
+ }
+ else if (tab->type == JT_NEXT)
+ tab->read_first_record= join_read_last;
+ else if (tab->type == JT_ALL && tab->select && tab->select->quick)
+ {
+ if (tab->select && tab->select->pre_idx_push_select_cond)
+ {
+ tab->set_cond(tab->select->pre_idx_push_select_cond);
+ tab->table->file->cancel_pushed_idx_cond();
+ }
+ }
+ }
+}
+
+
/**
@brief
Construct not null conditions for provingly not nullable fields
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 4f7bf49f635..f8deea0036e 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -366,6 +366,12 @@ typedef struct st_join_table {
uint used_fields;
ulong used_fieldlength;
ulong max_used_fieldlength;
+ /*
+ Estimate for the length of a record that would be stored in the nest.
+ The estimate contains length of all the fields that have bitmap read_set
+ set for them.
+ */
+ ulong used_rec_length_for_nest;
uint used_blobs;
uint used_null_fields;
uint used_uneven_bit_fields;
@@ -524,6 +530,12 @@ typedef struct st_join_table {
/* Becomes true just after the used range filter has been built / filled */
bool is_rowid_filter_built;
+ /*
+ Set to TRUE if we picked a join order that would use a sort-nest on
+ a prefix that satisfies the ORDER BY clause.
+ */
+ bool is_sort_nest;
+
void build_range_rowid_filter_if_needed();
void cleanup();
@@ -611,6 +623,7 @@ typedef struct st_join_table {
return tmp_select_cond;
}
void calc_used_field_length(bool max_fl);
+ ulong get_estimated_record_length();
ulong get_used_fieldlength()
{
if (!used_fieldlength)
@@ -676,6 +689,10 @@ typedef struct st_join_table {
table_map remaining_tables);
bool fix_splitting(SplM_plan_info *spl_plan, table_map remaining_tables,
bool is_const_table);
+ int get_index_on_table();
+ void find_keys_that_can_achieve_ordering();
+ bool check_if_index_satisfies_ordering(int index_used);
+ bool needs_filesort(uint idx, int index_used);
} JOIN_TAB;
@@ -859,7 +876,9 @@ public:
bool disable_jbuf,
double record_count,
struct st_position *pos,
- struct st_position *loose_scan_pos);
+ struct st_position *loose_scan_pos,
+ table_map sort_nest_tables,
+ bool nest_created);
friend bool get_best_combination(JOIN *join);
friend int setup_semijoin_loosescan(JOIN *join);
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
@@ -992,6 +1011,20 @@ typedef struct st_position
/* Cost info for the range filter used at this position */
Range_rowid_filter_cost_info *range_rowid_filter_info;
+ /*
+ Flag to be set to TRUE if prefix of a join satisfies the ORDER BY CLAUSE
+ This flag is only set at the join planner stage.
+ */
+ bool sort_nest_operation_here;
+ /*
+ set to the index number for the first non-const table only if it
+ satisfies the ORDER BY CLAUSE and has the lowest cost of accessing
+ the table.
+ -1 otherwise
+ This is set only at the join planner stage.
+ */
+ int index_no;
+
} POSITION;
typedef Bounds_checked_array<Item_null_result*> Item_null_array;
@@ -1065,6 +1098,96 @@ private:
};
+/**
+ Structure which consists of an item of the base table and the corresponding
+ item to the base item in the nest.
+*/
+
+class Item_pair :public Sql_alloc
+{
+public:
+ Item *base_item;
+ Item *nest_item;
+ Item_pair(Item *a, Item *b)
+ :base_item(a), nest_item(b) {}
+};
+
+
+/*
+ A subset of joined tables that are joined together is called a join table
+ nest. If the result of the join these tables is materialized in a temporary
+ table then the nest is called a materialized join table nest.
+ The class declared below is used for the objects created to handle
+ materialized join tables nests. These objects are supposed to be used at the
+ optimization an execution phases.
+*/
+
+class Mat_join_tab_nest_info : public Sql_alloc
+{
+protected:
+ /* The join which the joined tables from the nest belongs to */
+ JOIN *join;
+ /* Number of joined tables in the nest */
+ uint n_tables;
+ /* The bitmap of joined tables from the nest */
+ table_map nest_tables_map;
+ /* Condition extracted from the WHERE condition and pushed into the nest */
+ Item *nest_cond;
+ /* TRUE <=> materialization already performed */
+ bool materialized;
+
+public:
+ Mat_join_tab_nest_info(JOIN *join_arg, uint tables, table_map tables_map)
+ {
+ join= join_arg;
+ table= NULL;
+ nest_tab= NULL;
+ n_tables= tables;
+ nest_tables_map= tables_map;
+ nest_cond= NULL;
+ materialized= FALSE;
+ }
+
+ TMP_TABLE_PARAM tmp_table_param;
+ List<Item> nest_base_table_cols;
+
+ /*
+ List containing the mapping of items of the base tables with the
+ corresponding items in the nest
+ */
+ List<Item_pair> mapping_of_items;
+ st_join_table *nest_tab;
+ TABLE *table;
+
+ uint number_of_tables() { return n_tables; }
+ table_map get_tables_map() { return nest_tables_map; }
+ void set_nest_cond(Item *cond) { nest_cond= cond; }
+ Item *get_nest_cond() { return nest_cond; }
+ void set_materialized() { materialized= TRUE; }
+ bool is_materialized() { return materialized; }
+};
+
+/*
+ A derived class for the sort-nest.
+*/
+
+class Sort_nest_info : public Mat_join_tab_nest_info
+{
+public:
+ Sort_nest_info(JOIN *join_arg, uint tables, table_map tables_map)
+ :Mat_join_tab_nest_info(join_arg, tables, tables_map)
+ {
+ index_used= -1;
+ }
+ /*
+ >=0 set to the index that satisfies the ORDER BY clause and does an index
+ scan on the first non-const table.
+ -1 otherwise
+ */
+ int index_used;
+};
+
+
class JOIN :public Sql_alloc
{
private:
@@ -1508,6 +1631,45 @@ public:
*/
bool is_orig_degenerated;
+ /*
+ NOT NULL sort nest present
+ NULL Otherwise
+ */
+ Sort_nest_info *sort_nest_info;
+
+ /*
+ Set to TRUE if the query can use ORDER BY LIMIT optimization with
+ sort-nest. It caches the value that tells if the join optimizer
+ should consider using a sort-nest or not.
+ @see sort_nest_allowed()
+ */
+ bool sort_nest_possible;
+
+ /*
+ SET to TRUE when one wants to get an estimate of the cardinality for a
+ join.
+ */
+ bool get_cardinality_estimate;
+
+ /*
+ Set to the estimate of the rows that the join planner expects to be in the
+ output of the join.
+ */
+ double cardinality_estimate;
+
+ /*
+ The fraction of records we would read of the sort-nest or the first table
+ that satisfies the ORDER BY clause, after the sorting is done.
+ */
+ double fraction_output_for_nest;
+
+ /*
+ Caches that a prefix resolved the ORDER BY clause. This is done so that
+ we don't walk through the order by list everytime when we extend the prefix
+ to check if the extended prefix satifies the ordering or not.
+ */
+ bool prefix_resolves_ordering;
+
JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_arg)
:fields_list(fields_arg)
@@ -1603,6 +1765,12 @@ public:
sjm_lookup_tables= 0;
sjm_scan_tables= 0;
is_orig_degenerated= false;
+ sort_nest_info= NULL;
+ sort_nest_possible= FALSE;
+ fraction_output_for_nest= 1;
+ prefix_resolves_ordering= FALSE;
+ get_cardinality_estimate= FALSE;
+ cardinality_estimate= DBL_MAX;
}
/* True if the plan guarantees that it will be returned zero or one row */
@@ -1722,7 +1890,8 @@ public:
JOIN_TAB *get_sort_by_join_tab()
{
return (need_tmp || !sort_by_table || skip_sort_order ||
- ((group || tmp_table_param.sum_func_count) && !group_list)) ?
+ ((group || tmp_table_param.sum_func_count) && !group_list) ||
+ sort_nest_info) ?
NULL : join_tab+const_tables;
}
bool setup_subquery_caches();
@@ -1741,6 +1910,7 @@ public:
and one of the following conditions holds:
- We are using DISTINCT (simple distinct's are already optimized away)
- We are using an ORDER BY or GROUP BY on fields not in the first table
+ - We are not using the sort nest for ORDER BY with LIMIT
- We are using different ORDER BY and GROUP BY orders
- The user wants us to buffer the result.
When the WITH ROLLUP modifier is present, we cannot skip temporary table
@@ -1749,11 +1919,49 @@ public:
bool test_if_need_tmp_table()
{
return ((const_tables != table_count &&
- ((select_distinct || !simple_order || !simple_group) ||
+ ((select_distinct || (!simple_order && !sort_nest_info) || !simple_group) ||
(group_list && order) ||
MY_TEST(select_options & OPTION_BUFFER_RESULT))) ||
(rollup.state != ROLLUP::STATE_NONE && select_distinct));
}
+
+ /*
+ TRUE if the sort-nest contains more than one table
+ FALSE otherwise
+ */
+ bool sort_nest_needed()
+ {
+ if (!sort_nest_info)
+ return FALSE;
+ return sort_nest_info->number_of_tables() == 1 ? FALSE : TRUE;
+ }
+
+ bool sort_nest_allowed();
+ bool is_order_by_expensive();
+ bool estimate_cardinality_for_join(table_map joined_tables);
+ bool check_if_sort_nest_present(uint* n_tables, table_map *tables_map);
+ bool create_sort_nest_info(uint n_tables, table_map nest_tables_map);
+ bool remove_const_from_order_by();
+ bool make_sort_nest(Mat_join_tab_nest_info *nest_info);
+ double calculate_record_count_for_sort_nest(uint n_tables);
+ void
+ substitute_base_with_nest_field_items(Mat_join_tab_nest_info* nest_info);
+ void substitute_best_fields_for_order_by_items();
+ void substitute_ref_items(JOIN_TAB *tab, Mat_join_tab_nest_info* nest_info);
+ void substitutions_for_sjm_lookup(JOIN_TAB *sjm_tab,
+ Mat_join_tab_nest_info* nest_info);
+ void extract_condition_for_the_nest(Mat_join_tab_nest_info* nest_info);
+ void propagate_equal_field_for_orderby();
+ void setup_index_use_for_ordering(int index_no);
+ void setup_range_scan(JOIN_TAB *tab, uint idx, double records);
+ bool is_join_buffering_allowed(JOIN_TAB *tab);
+ bool check_join_prefix_resolves_ordering(table_map previous_tables);
+ bool consider_adding_sort_nest(table_map previous_tables);
+ void set_fraction_output_for_nest();
+ double sort_nest_oper_cost(double join_record_count, uint idx,
+ ulong rec_len);
+ bool is_index_with_ordering_allowed(uint idx);
+
bool choose_subquery_plan(table_map join_tables);
void get_partial_cost_and_fanout(int end_tab_idx,
table_map filter_map,
@@ -2105,6 +2313,14 @@ bool mysql_select(THD *thd,
void free_underlaid_joins(THD *thd, SELECT_LEX *select);
bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
select_result *result);
+double calculate_record_count_for_sort_nest(JOIN *join, uint n_tables);
+void check_cond_extraction_for_nest(THD *thd, Item *cond,
+ Pushdown_checker checker, uchar* arg);
+void resetup_access_for_ordering(JOIN_TAB* tab, int idx);
+int get_best_index_for_order_by_limit(JOIN_TAB *tab,
+ ha_rows select_limit_arg,
+ double *read_time, double *records,
+ int index_used, uint idx);
/*
General routine to change field->ptr of a NULL-terminated array of Field
@@ -2449,10 +2665,13 @@ bool instantiate_tmp_table(TABLE *table, KEY *keyinfo,
ulonglong options);
bool open_tmp_table(TABLE *table);
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps);
-double prev_record_reads(const POSITION *positions, uint idx, table_map found_ref);
+double prev_record_reads(const POSITION *positions, uint idx, table_map found_ref,
+ table_map sort_nest_tables,
+ double fraction_output_for_nest);
+
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist);
-double get_tmp_table_lookup_cost(THD *thd, double row_count, uint row_size);
-double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size);
+double get_tmp_table_lookup_cost(THD *thd, double row_count, ulong row_size);
+double get_tmp_table_write_cost(THD *thd, double row_count, ulong row_size);
void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array);
bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
bool skip_unprefixed_keyparts);
diff --git a/sql/sql_sort_nest.cc b/sql/sql_sort_nest.cc
new file mode 100644
index 00000000000..87773414342
--- /dev/null
+++ b/sql/sql_sort_nest.cc
@@ -0,0 +1,1596 @@
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2017, MariaDB Corporation.
+
+ 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-1335 USA */
+
+
+#include "mariadb.h"
+#include "sql_select.h"
+#include "opt_trace.h"
+
+/**
+
+INTRODUCTION
+
+This file contains the functions to support the cost based ORDER BY with LIMIT
+optimization.
+
+The motivation behind this optimization is to shortcut the join execution
+for queries having ORDER BY with LIMIT clause. In other words we would like to
+avoid computing the entire join for queries having ORDER BY with LIMIT.
+
+The main idea behind this optimization is to push the LIMIT to a partial join.
+For pushing the LIMIT there is one pre-requisite and that is the partial
+join MUST resolve the ORDER BY clause.
+
+What does PUSHING THE LIMIT mean?
+
+Pushing the limit to a partial join means that one would only read a fraction
+of records of the prefix that are sorted in accordance with the ORDER BY
+clause.
+
+
+Let's say we have tables
+ t1, t2, t3, t4 .............tk,tk+1.........................tn
+ |<---------prefix------------>|<-------suffix--------------->
+
+and lets assume the prefix can resolve the ORDER BY clause and we can push
+the LIMIT.
+
+Mathematically speaking the fanout of the suffix in the join wrt prefix would
+help us to estimate the fraction of records of the prefix(that are sorted)
+that would be read:
+
+ +-------------------------------------------------------+
+ | |
+fanout(tk+1....tn)= | cardinality(t1,t2....tn) / cardinality(t1,t2....tk) |
+ | |
+ +-------------------------------------------------------+
+
+fanout is always >= 1
+
+So number of records that one would read for the prefix after the LIMIT is
+pushed is
+
+ +----------------------------+
+ | |
+records_read= | LIMIT * fanout(tk+1....tn) |
+ | |
+ +----------------------------+
+
+ +--------------------------------------------------------------+
+ | |
+ = | LIMIT * (cardinality(t1,t2....tk) / cardinality(t1,t2....tn))|
+ | |
+ +--------------------------------------------------------------+
+
+
+So the LIMIT is pushed for all partial join orders enumerated by the join
+planner that can resolve the ORDER BY clause.
+This is how we achieve a complete cost based solution for
+ORDER BY with LIMIT optimization.
+
+
+IMPLEMENTATION DETAILS
+
+Let us divide the implementation details in 3 stages:
+
+OPTIMIZATION STAGE
+
+- We invoke the join planner to get an estimate of the cardinality of the
+ join. This is needed for pushing the LIMIT in different partial plans
+ which can resolve the ORDER BY clause.
+
+- Join planner is invoked again to find the best join order for the tables
+ inside the join. The join planner enumerated various join orders.
+ For each partial plan we try to find out if it can resolve the ORDER BY
+ clause or not.
+ To resolve the ORDER BY clause, equalities from the WHERE clause are also
+ considered.
+
+- After a partial plan that can resolve ORDER BY clause is found, we push
+ the LIMIT to the partial plan.
+
+- Access methods that ensure pre-existing ordering are also taken into account
+ inside the join planner. There can be indexes on the first non-const table
+ that can resolve the ORDER BY clause. So we push the LIMIT to the first
+ non-const table also.
+
+- For each partial plan that can resolve the ORDER BY clause,
+ we consider 2 cases
+ 1) Push the LIMIT at the current partial plan
+ 2) Push the LIMIT later
+
+ This helps us to enumerate all plans where on can push LIMIT at different
+ partial plans. Finally the plan with the lowest cost is picked by the join
+ planner
+
+
+COMPILATION STAGE
+
+Preparation of Sort Nest
+
+Let's say we have the best join order as:
+
+ t1, t2, t3, t4 .............tk,tk+1.........................tn
+ |<---------prefix------------>|<-------suffix--------------->
+
+
+The array of join_tab structures would look like
+
+ t1, t2, t3, t4 .............tk, <sort nest>, tk+1.........................tn
+
+After the best execution plan is picked by the join planner which requires
+a nest for a prefix of tables that can resolve the ORDER BY clause, we want
+to prepare the temporary table that would hold the result of materialization
+of the tables in the prefix.
+
+t1, t2, t3, t4..............tk ======> inner tables of the nest
+
+To create the temporary table we need a list of Items which we want to store
+inside the temporary table of the nest. Currently this list contains all
+fields of the inner tables of the nest that have their bitmap read_set set.
+With this list of Items we create the temporary table for the nest.
+Also we create a list of Items for all the fields of the temporary table.
+This list is needed for substitution of items that will be evaluated in the
+POST ORDER BY context.
+
+
+After the nest for the prefix is prepared, we extract a sub-condition which is
+dependent on the inner tables of the nest from the WHERE clause. This
+condition is then attached to the inner tables of the nest. This condition
+would be evaluated before the ORDER BY clause is applied to the temporary
+table of the nest.
+
+We need to make substitution for items belonging to the inner tables of the
+nest which will be evaluated in the POST ORDER BY context. These items need
+to be substituted with the corresponding items of the temporary table
+of the nest.
+
+
+EXECUTION STAGE
+
+Let's say we have the best join order as:
+
+ t1, t2, t3, t4 .............tk,tk+1.........................tn
+ |<---------prefix------------>|<-------suffix--------------->
+
+ The prefix are the inner table of the sort nest while the suffix are the
+ tables outside the sort nest.
+
+ As soon as the join execution starts, we compute the partial join for the
+ tables in the prefix and store the result inside the temporary table
+ for the sort nest.
+ Then we sort the temporary table in accordance with the ORDER BY clause.
+ After the sort is performed we read the records from the temporary
+ table of the sort nest one by one and continue the join with the
+ tables in the suffix.
+
+ The join execution for this optimization can be split in 3 parts
+
+ a) Materialize the prefix
+ materialize
+ t1, t2, t3, t4 .............tk ============> <sort nest>
+ |<---------prefix------------>|
+
+ b) Sort the <sort nest> in accordance with the ORDER BY clause
+
+ c) Read records from the Filesort buffer one by one and continue join
+ execution with the tables in the suffix
+
+ <sort nest>, tk+1.........................tn
+ <-------suffix---------------->
+
+ The execution stops as soon as we get LIMIT records in the output.
+
+*/
+
+int test_if_order_by_key(JOIN *join, ORDER *order, TABLE *table, uint idx,
+ uint *used_key_parts= NULL);
+COND* substitute_for_best_equal_field(THD *thd, JOIN_TAB *context_tab,
+ COND *cond,
+ COND_EQUAL *cond_equal,
+ void *table_join_idx,
+ bool do_substitution);
+enum_nested_loop_state
+end_nest_materialization(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+bool get_range_limit_read_cost(const JOIN_TAB *tab, const TABLE *table,
+ ha_rows table_records, uint keynr,
+ ha_rows rows_limit, double *read_time);
+Item **get_sargable_cond(JOIN *join, TABLE *table);
+void find_cost_of_index_with_ordering(THD *thd, const JOIN_TAB *tab,
+ TABLE *table,
+ ha_rows *select_limit_arg,
+ double fanout, double est_best_records,
+ uint nr, double *index_scan_time,
+ Json_writer_object *trace_possible_key);
+
+
+/*
+ @brief
+ Substitute field items of tables inside the nest with nest's field items.
+
+ @param nest_info Structure holding information about the nest
+
+ @details
+ Substitute field items of tables inside the sort-nest with sort-nest's
+ field items. This is needed for expressions which would be
+ evaluated in the post ORDER BY context.
+
+ Example:
+
+ SELECT * FROM t1, t2, t3
+ WHERE t1.a = t2.a AND t2.b = t3.b AND t1.c > t3.c
+ ORDER BY t1.a,t2.c
+ LIMIT 5;
+
+ Let's say in this case the join order is t1,t2,t3 and there is a sort-nest
+ on the prefix t1,t2.
+
+ Now looking at the WHERE clause, splitting it into 2 parts:
+ (1) t2.b = t3.b AND t1.c > t3.c ---> condition external to the nest
+ (2) t1.a = t2.a ---> condition internal to the nest
+
+ Now look at the condition in (1), this would be evaluated in the post
+ ORDER BY context.
+
+ So t2.b and t1.c should actually refer to the sort-nest's field items
+ instead of field items of the tables inside the sort-nest.
+ This is why we need to substitute field items of the tables inside the
+ sort-nest with sort-nest's field items.
+
+ For the condition in (2) there is no need for substitution as this
+ condition is internal to the nest and would be evaluated before we
+ do the sorting for the sort-nest.
+
+ This function does the substitution for
+ - WHERE clause
+ - SELECT LIST
+ - ORDER BY clause
+ - ON expression
+ - REF access items
+*/
+
+void
+JOIN::substitute_base_with_nest_field_items(Mat_join_tab_nest_info *nest_info)
+{
+ List_iterator<Item> it(fields_list);
+ Item *item, *new_item;
+
+ /* Substituting SELECT list field items with sort-nest's field items */
+ while ((item= it++))
+ {
+ if ((new_item= item->transform(thd,
+ &Item::replace_with_nest_items, TRUE,
+ (uchar *) nest_info)) != item)
+ {
+ new_item->name= item->name;
+ thd->change_item_tree(it.ref(), new_item);
+ }
+ new_item->update_used_tables();
+ }
+
+ /* Substituting ORDER BY field items with sort-nest's field items */
+ ORDER *ord;
+ for (ord= order; ord ; ord=ord->next)
+ {
+ (*ord->item)= (*ord->item)->transform(thd,
+ &Item::replace_with_nest_items,
+ TRUE, (uchar *) nest_info);
+ (*ord->item)->update_used_tables();
+ }
+
+ JOIN_TAB *end_tab= nest_info->nest_tab;
+ uint i, j;
+ for (i= const_tables + nest_info->number_of_tables(), j=0;
+ i < top_join_tab_count; i++, j++)
+ {
+ JOIN_TAB *tab= end_tab + j;
+
+ if (tab->type == JT_REF || tab->type == JT_EQ_REF ||
+ tab->type == JT_REF_OR_NULL)
+ substitute_ref_items(tab, nest_info);
+
+ /* Substituting ON-EXPR field items with sort-nest's field items */
+ if (*tab->on_expr_ref)
+ {
+ item= (*tab->on_expr_ref)->transform(thd,
+ &Item::replace_with_nest_items,
+ TRUE, (uchar *) nest_info);
+ *tab->on_expr_ref= item;
+ (*tab->on_expr_ref)->update_used_tables();
+ }
+
+ /*
+ Substituting REF field items for SJM lookup with sort-nest's field items
+ */
+ if (tab->bush_children)
+ substitutions_for_sjm_lookup(tab, nest_info);
+ }
+
+ /*
+ This needs a pointer to the nest, so that this could be used in general
+ */
+ extract_condition_for_the_nest(nest_info);
+
+ /* Substituting WHERE clause's field items with sort-nest's field items */
+ if (conds)
+ {
+ conds= conds->transform(thd, &Item::replace_with_nest_items, TRUE,
+ (uchar *) nest_info);
+ conds->update_used_tables();
+ }
+}
+
+
+/*
+ @brief
+ Substitute ref access field items with nest's field items.
+
+ @param tab join tab structure having ref access
+ @param nest_info Structure holding information about the nest
+
+*/
+
+void JOIN::substitute_ref_items(JOIN_TAB *tab,
+ Mat_join_tab_nest_info* nest_info)
+{
+ Item *item;
+ /* Substituting REF field items with sort-nest's field items */
+ for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++)
+ {
+ item= tab->ref.items[keypart]->transform(thd,
+ &Item::replace_with_nest_items,
+ TRUE, (uchar *) nest_info);
+ if (item != tab->ref.items[keypart])
+ {
+ tab->ref.items[keypart]= item;
+ Item *real_item= item->real_item();
+ store_key *key_copy= tab->ref.key_copy[keypart];
+ if (key_copy->type() == store_key::FIELD_STORE_KEY)
+ {
+ store_key_field *field_copy= ((store_key_field *)key_copy);
+ DBUG_ASSERT(real_item->type() == Item::FIELD_ITEM);
+ field_copy->change_source_field((Item_field *) real_item);
+ }
+ }
+ }
+}
+
+
+/*
+ @brief
+ Substitute the left expression of the IN subquery with nest's field items.
+
+ @param sjm_tab SJM lookup join tab
+ @param nest_info Structure holding information about the nest
+
+ @details
+ This substitution is needed for SJM lookup when the SJM materialized
+ table is outside the nest.
+
+ For example:
+ SELECT t1.a, t2.a
+ FROM t1, t2
+ WHERE ot1.a in (SELECT it.b FROM it) AND ot1.b = t1.b
+ ORDER BY t1.a desc, ot1.a desc
+ LIMIT 5;
+
+ Lets consider the join order here is t1, t2, <subquery2> and there is a
+ nest on t1, t2. For <subquery2> we do SJM lookup.
+ So for the SJM table there would be a ref access created on the condition
+ t2.a=it.b. But as one can see table t2 is inside the nest and the
+ condition t2.a=it.b can only be evaluated in the post nest creation
+ context, so we need to substitute t2.a with the corresponding field item
+ of the nest.
+
+ For example:
+ If we had a sort nest on t1,t2 the the condition t2.a = it.b will be
+ evaluated in the POST ORDER BY context, so t2.a should refer to the
+ field item of the sort nest.
+
+*/
+
+void JOIN::substitutions_for_sjm_lookup(JOIN_TAB *sjm_tab,
+ Mat_join_tab_nest_info* nest_info)
+{
+ JOIN_TAB *tab= sjm_tab->bush_children->start;
+ TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding;
+
+ /*
+ @see setup_sj_materialization_part1
+ */
+ while (!emb_sj_nest->sj_mat_info)
+ emb_sj_nest= emb_sj_nest->embedding;
+ SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info;
+
+ if (!sjm->is_sj_scan)
+ {
+ Item *left_expr= emb_sj_nest->sj_subq_pred->left_expr;
+ left_expr= left_expr->transform(thd, &Item::replace_with_nest_items,
+ TRUE, (uchar *) nest_info);
+ left_expr->update_used_tables();
+ emb_sj_nest->sj_subq_pred->left_expr= left_expr;
+ }
+}
+
+
+/*
+ @brief
+ Extract from the WHERE clause the sub-condition for tables inside the nest.
+
+ @param nest_info Structure holding information about the nest
+
+ @details
+ Extract the sub-condition from the WHERE clause that can be added to the
+ tables inside the nest.
+
+ Example
+ SELECT * from t1,t2,t3
+ WHERE t1.a > t2.a (1)
+ AND t2.b = t3.b (2)
+ ORDER BY t1.a,t2.a
+ LIMIT 5;
+
+ let's say in this case the join order is t1,t2,t3 and there is a nest
+ on t1,t2
+
+ From the WHERE clause we would like to extract the condition that depends
+ only on the inner tables of the nest. The condition (1) here satisfies
+ this criteria so it would be extracted from the WHERE clause.
+ The extracted condition here would be t1.a > t2.a.
+
+ The extracted condition is stored inside the Mat_join_tab_nest_info
+ structure.
+
+ Also we remove the top level conjuncts of the WHERE clause that were
+ present in the extracted condition.
+
+ So after removal the final results would be:
+ WHERE clause: t2.b = t3.b ----> condition external to the nest
+ extracted cond: t1.a > t2.a ----> condition internal to the nest
+
+ @note
+ For the sort nest the sub-condition will be evaluated before the
+ ORDER BY clause is applied.
+*/
+
+void JOIN::extract_condition_for_the_nest(Mat_join_tab_nest_info* nest_info)
+{
+ DBUG_ASSERT(nest_info);
+ Item *orig_cond= conds;
+ Item *extracted_cond;
+
+ /*
+ check_pushable_cond_extraction would set the flag NO_EXTRACTION_FL for
+ all the predicates that cannot be added to the inner tables of the nest.
+ */
+ table_map nest_tables_map= nest_info->get_tables_map();
+ conds->check_pushable_cond_extraction(
+ &Item::pushable_cond_checker_for_tables,
+ (uchar*)&nest_tables_map);
+
+ /*
+ build_pushable_condition would create a sub-condition that would be
+ added to the inner tables of the nest. This may clone some predicates too.
+ */
+ extracted_cond= orig_cond->build_pushable_condition(thd, TRUE);
+
+ if (extracted_cond)
+ {
+ if (extracted_cond->fix_fields_if_needed(thd, 0))
+ return;
+ extracted_cond->update_used_tables();
+ /*
+ Remove from the WHERE clause the top level conjuncts that were
+ extracted for the inner tables of the nest
+ */
+ orig_cond= remove_pushed_top_conjuncts(thd, orig_cond);
+ nest_info->set_nest_cond(extracted_cond);
+ }
+ conds= orig_cond;
+}
+
+
+/*
+ @brief
+ Propagate the Multiple Equalities for all the ORDER BY items.
+
+ @details
+ Propagate the multiple equalities for the ORDER BY items.
+ This is needed so that we can generate different join orders
+ that would satisfy ordering after taking equality propagation
+ into consideration.
+
+ Example
+ SELECT * FROM t1, t2, t3
+ WHERE t1.a = t2.a AND t2.b = t3.a
+ ORDER BY t2.a, t3.a
+ LIMIT 10;
+
+ Possible join orders which satisfy the ORDER BY clause and
+ which we can get after equality propagation are:
+ - t2, sort(t2), t3, t1 ---> substitute t3.a with t2.b
+ - t2, sort(t2), t1, t3 ---> substitute t3.a with t2.b
+ - t1, t3, sort(t1,t3), t2 ---> substitute t2.a with t1.a
+ - t1, t2, sort(t1,t2), t3 ---> substitute t3.a with t2.b
+
+ So with equality propagation for ORDER BY items, we can get more
+ join orders that could satisfy the ORDER BY clause.
+*/
+
+void JOIN::propagate_equal_field_for_orderby()
+{
+ if (!sort_nest_possible)
+ return;
+ ORDER *ord;
+ for (ord= order; ord; ord= ord->next)
+ {
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_ORDERBY_EQ_PROP) && cond_equal)
+ {
+ Item *item= ord->item[0];
+ /*
+ TODO: equality substitution in the context of ORDER BY is
+ sometimes allowed when it is not allowed in the general case.
+ We make the below call for its side effect: it will locate the
+ multiple equality the item belongs to and set item->item_equal
+ accordingly.
+ */
+ (void)item->propagate_equal_fields(thd,
+ Value_source::
+ Context_identity(),
+ cond_equal);
+ }
+ }
+}
+
+
+/*
+ @brief
+ Check whether ORDER BY items can be evaluated for a given prefix
+
+ @param previous_tables table_map of all the tables in the prefix
+ of the current partial plan
+
+ @details
+ Here we walk through the ORDER BY items and check if the prefix of the
+ join resolves the ordering.
+ Also we look at the multiple equalities for each item in the ORDER BY list
+ to see if the ORDER BY items can be resolved by the given prefix.
+
+ Example
+ SELECT * FROM t1, t2, t3
+ WHERE t1.a = t2.a AND t2.b = t3.a
+ ORDER BY t2.a, t3.a
+ LIMIT 10;
+
+ Let's say the given prefix is table {t1,t3}, then this function would
+ return TRUE because there is an equality condition t2.a=t1.a ,
+ so t2.a can be resolved with t1.a. Hence the given prefix {t1,t3} would
+ resolve the ORDER BY clause.
+
+ @retval
+ TRUE ordering can be evaluated by the given prefix
+ FALSE otherwise
+
+*/
+
+bool JOIN::check_join_prefix_resolves_ordering(table_map previous_tables)
+{
+ DBUG_ASSERT(order);
+ ORDER *ord;
+ for (ord= order; ord; ord= ord->next)
+ {
+ Item *order_item= ord->item[0];
+ table_map order_tables=order_item->used_tables();
+ if (!(order_tables & ~previous_tables) ||
+ (order_item->excl_dep_on_tables(previous_tables, FALSE)))
+ continue;
+ else
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ @brief
+ Check if the best plan has a sort-nest or not.
+
+ @param
+ n_tables[out] set to the number of tables inside the sort-nest
+ nest_tables_map[out] map of tables inside the sort-nest
+ @details
+ This function walks through the JOIN::best_positions array
+ which holds the best plan and checks if there is prefix for
+ which the join planner had picked a sort-nest.
+
+ Also this function computes a table map for tables that are inside the
+ sort-nest
+
+ @retval
+ TRUE sort-nest present
+ FALSE no sort-nest present
+*/
+
+bool JOIN::check_if_sort_nest_present(uint* n_tables,
+ table_map *nest_tables_map)
+{
+ if (!sort_nest_possible)
+ return FALSE;
+
+ uint tablenr;
+ table_map nest_tables= 0;
+ uint tables= 0;
+ for (tablenr=const_tables ; tablenr < table_count ; tablenr++)
+ {
+ tables++;
+ POSITION *pos= &best_positions[tablenr];
+ if (pos->sj_strategy == SJ_OPT_MATERIALIZE ||
+ pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
+ {
+ SJ_MATERIALIZATION_INFO *sjm= pos->table->emb_sj_nest->sj_mat_info;
+ for (uint j= 0; j < sjm->tables; j++)
+ {
+ JOIN_TAB *tab= (pos+j)->table;
+ nest_tables|= tab->table->map;
+ }
+ tablenr+= (sjm->tables-1);
+ }
+ else
+ nest_tables|= pos->table->table->map;
+
+ if (pos->sort_nest_operation_here)
+ {
+ *n_tables= tables;
+ *nest_tables_map= nest_tables;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ @brief
+ Create a sort nest info structure
+
+ @param n_tables number of tables inside the sort-nest
+ @param nest_tables_map map of top level tables inside the sort-nest
+
+ @details
+ This sort-nest structure would hold all the information about the
+ sort-nest.
+
+ @retval
+ FALSE successful in creating the sort-nest info structure
+ TRUE error
+*/
+
+bool JOIN::create_sort_nest_info(uint n_tables, table_map nest_tables_map)
+{
+ if (!(sort_nest_info= new Sort_nest_info(this, n_tables, nest_tables_map)))
+ return TRUE;
+ return FALSE;
+}
+
+
+void JOIN::substitute_best_fields_for_order_by_items()
+{
+ ORDER *ord;
+ /*
+ Substitute the ORDER by items with the best field so that equality
+ propagation considered during best_access_path can be used.
+ */
+ for (ord= order; ord; ord=ord->next)
+ {
+ Item *item= ord->item[0];
+ item= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB, item,
+ cond_equal,
+ map2table, true);
+ item->update_used_tables();
+ ord->item[0]= item;
+ }
+}
+
+
+/*
+ @brief
+ Make the sort-nest.
+
+ @param
+ nest_info Structure holding information about the nest
+
+ @details
+ Setup execution structures for sort-nest materialization:
+ - Create the list of Items of the inner tables of the sort-nest
+ that are needed for the post ORDER BY computations
+ - Create the materialization temporary table for the sort-nest
+
+ This function fills up the Sort_nest_info structure
+
+ @retval
+ TRUE : In case of error
+ FALSE : Nest creation successful
+*/
+
+bool JOIN::make_sort_nest(Mat_join_tab_nest_info *nest_info)
+{
+ Field_iterator_table field_iterator;
+
+ JOIN_TAB *j;
+ JOIN_TAB *tab;
+
+ if (unlikely(thd->trace_started()))
+ add_sort_nest_tables_to_trace(this, nest_info);
+
+ /*
+ List of field items of the tables inside the sort-nest is created for
+ the field items that are needed to be stored inside the temporary table
+ of the sort-nest. Currently Item_field objects are created for the tables
+ inside the sort-nest for all the fields which have bitmap read_set
+ set for them.
+
+ TODO varun:
+ An improvement would be if to remove the fields from this
+ list that are completely internal to the nest because such
+ fields would not be used in computing expression in the post
+ ORDER BY context
+ */
+
+ for (j= join_tab + const_tables; j < nest_info->nest_tab; j++)
+ {
+ if (!j->bush_children)
+ {
+ TABLE *table= j->table;
+ field_iterator.set_table(table);
+ for (; !field_iterator.end_of_fields(); field_iterator.next())
+ {
+ Field *field= field_iterator.field();
+ if (!bitmap_is_set(table->read_set, field->field_index))
+ continue;
+ Item *item;
+ if (!(item= field_iterator.create_item(thd)))
+ return TRUE;
+ nest_info->nest_base_table_cols.push_back(item, thd->mem_root);
+ }
+ }
+ else
+ {
+ TABLE_LIST *emb_sj_nest;
+ JOIN_TAB *child_tab= j->bush_children->start;
+ emb_sj_nest= child_tab->table->pos_in_table_list->embedding;
+ /*
+ @see setup_sj_materialization_part1
+ */
+ while (!emb_sj_nest->sj_mat_info)
+ emb_sj_nest= emb_sj_nest->embedding;
+ Item_in_subselect *item_sub= emb_sj_nest->sj_subq_pred;
+ SELECT_LEX *subq_select= item_sub->unit->first_select();
+ List_iterator_fast<Item> li(subq_select->item_list);
+ Item *item;
+ while((item= li++))
+ nest_info->nest_base_table_cols.push_back(item, thd->mem_root);
+ }
+ }
+
+ tab= nest_info->nest_tab;
+ DBUG_ASSERT(!tab->table);
+
+ uint sort_nest_elements= nest_info->nest_base_table_cols.elements;
+ nest_info->tmp_table_param.init();
+ nest_info->tmp_table_param.bit_fields_as_long= TRUE;
+ nest_info->tmp_table_param.field_count= sort_nest_elements;
+ nest_info->tmp_table_param.force_not_null_cols= FALSE;
+
+ const LEX_CSTRING order_nest_name= { STRING_WITH_LEN("sort-nest") };
+ if (!(tab->table= create_tmp_table(thd, &nest_info->tmp_table_param,
+ nest_info->nest_base_table_cols,
+ (ORDER*) 0,
+ FALSE /* distinct */,
+ 0, /*save_sum_fields*/
+ thd->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS,
+ HA_POS_ERROR /*rows_limit */,
+ &order_nest_name)))
+ return TRUE; /* purecov: inspected */
+
+ tab->table->map= nest_info->get_tables_map();
+ nest_info->table= tab->table;
+ tab->type= JT_ALL;
+ tab->table->reginfo.join_tab= tab;
+
+ /*
+ The list of temp table items created here, these are needed for the
+ substitution for items that would be evaluated in POST SORT NEST context
+ */
+ field_iterator.set_table(tab->table);
+ List_iterator_fast<Item> li(nest_info->nest_base_table_cols);
+ Item *item;
+ for (; !field_iterator.end_of_fields() && (item= li++);
+ field_iterator.next())
+ {
+ Field *field= field_iterator.field();
+ Item *nest_item;
+ if (!(nest_item= new (thd->mem_root)Item_temptable_field(thd, field)))
+ return TRUE;
+ Item_pair *tmp_field= new Item_pair(item, nest_item);
+ nest_info->mapping_of_items.push_back(tmp_field, thd->mem_root);
+ }
+
+ /* Setting up the scan on the temp table */
+ tab->read_first_record= join_init_read_record;
+ tab->read_record.read_record_func= rr_sequential;
+ tab[-1].next_select= end_nest_materialization;
+ DBUG_ASSERT(!nest_info->is_materialized());
+
+ return FALSE;
+}
+
+
+/*
+ @brief
+ Calculate the cost of adding a sort-nest.
+
+ @param
+ join_record_count the cardinality of the partial join
+ idx position of the joined table in the partial plan
+ rec_len estimate of length of the record in the sort-nest table
+
+ @details
+ The calculation for the cost of the sort-nest is done here, the cost
+ includes three components
+ 1) Filling the sort-nest table
+ 2) Sorting the sort-nest table
+ 3) Reading from the sort-nest table
+*/
+
+double JOIN::sort_nest_oper_cost(double join_record_count, uint idx,
+ ulong rec_len)
+{
+ double cost= 0;
+ set_if_bigger(join_record_count, 1);
+ /*
+ The sort-nest table is not created for sorting when one does sorting
+ on the first non-const table. So for this case we don't need to add
+ the cost of filling the table.
+ */
+ if (idx != const_tables)
+ cost= get_tmp_table_write_cost(thd, join_record_count, rec_len) *
+ join_record_count; // cost to fill temp table
+
+ // cost to perform sorting
+ cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) +
+ (join_record_count == 0 ? 0 :
+ join_record_count * log2 (join_record_count)) *
+ SORT_INDEX_CMP_COST;
+
+ /*
+ cost for scanning the temp table.
+ Picked this cost from get_delayed_table_estimates()
+ */
+ double data_size= COST_MULT(join_record_count * fraction_output_for_nest,
+ rec_len);
+ cost+= data_size/IO_SIZE + 2;
+
+ return cost;
+}
+
+
+/*
+ @brief
+ Calculate the number of records that would be read from the sort-nest.
+
+ @param n_tables number of tables in the sort-nest
+
+ @details
+ The number of records read from the sort-nest would be:
+
+ cardinality(join of inner table of nest) * selectivity_of_limit;
+
+ Here selectivity of limit is how many records we would expect in the
+ output.
+ selectivity_of_limit= limit / cardinality(join of all tables)
+
+ This number of records is what we would also see in the EXPLAIN output
+ for the sort-nest in the columns "rows".
+
+ @retval Number of records that the optimizer expects to be read from the
+ sort-nest
+*/
+
+double JOIN::calculate_record_count_for_sort_nest(uint n_tables)
+{
+ double sort_nest_records= 1, record_count;
+ JOIN_TAB *tab= join_tab + const_tables;
+ for (uint j= 0; j < n_tables ; j++, tab++)
+ {
+ record_count= tab->records_read * tab->cond_selectivity;
+ sort_nest_records= COST_MULT(sort_nest_records, record_count);
+ }
+ sort_nest_records= sort_nest_records * fraction_output_for_nest;
+ set_if_bigger(sort_nest_records, 1);
+ return sort_nest_records;
+}
+
+
+/*
+ @brief
+ Find all keys that can resolve the ORDER BY clause for a table
+
+ @details
+ This function sets the flag TABLE::keys_with_ordering with all the
+ indexes of a table that can resolve the ORDER BY clause.
+*/
+
+void JOIN_TAB::find_keys_that_can_achieve_ordering()
+{
+ if (!join->sort_nest_possible)
+ return;
+
+ table->keys_with_ordering.clear_all();
+ for (uint index= 0; index < table->s->keys; index++)
+ {
+ if (table->keys_in_use_for_query.is_set(index) &&
+ test_if_order_by_key(join, join->order, table, index))
+ table->keys_with_ordering.set_bit(index);
+ }
+ table->keys_with_ordering.intersect(table->keys_in_use_for_order_by);
+}
+
+
+/*
+ @brief
+ Checks if the given prefix needs Filesort for ordering.
+
+ @param
+ idx position of the joined table in the partial plan
+ index_used >=0 number of the index that is picked as best access
+ -1 no index access chosen
+
+ @details
+ Here we check if a given prefix requires Filesort or index on the
+ first non-const table to resolve the ORDER BY clause.
+
+ @retval
+ TRUE Filesort is needed
+ FALSE index present that satisfies the ordering
+*/
+
+bool JOIN_TAB::needs_filesort(uint idx, int index_used)
+{
+ if (idx != join->const_tables)
+ return TRUE;
+
+ return !check_if_index_satisfies_ordering(index_used);
+}
+
+
+/*
+ @brief
+ Find a cheaper index that resolves ordering on the first non-const table.
+
+ @param
+ tab joined table
+ read_time [out] cost for the best index picked if cheaper
+ records [out] estimate of records going to be accessed by the
+ index
+ index_used >=0 number of index used for best access
+ -1 no index used for best access
+ idx position of the joined table in the partial plan
+
+ @details
+ Here we try to walk through all the indexes for the first non-const table
+ of a given prefix.
+ From these indexes we are only interested in the indexes that can resolve
+ the ORDER BY clause as we want to shortcut the join execution for ORDER BY
+ LIMIT optimization.
+
+ For each index we are interested in we try to estimate the records we
+ have to read to ensure #limit records in the join output.
+
+ Then with this estimate of records we calculate the cost of using an index
+ and try to find the best index for access.
+ If the best index found from here has a lower cost than the best access
+ found in best_access_path, we switch the access to use the index found
+ here.
+
+ @retval
+ -1 no cheaper index found for ordering
+ >=0 cheaper index found for ordering
+*/
+
+int get_best_index_for_order_by_limit(JOIN_TAB *tab,
+ ha_rows select_limit_arg,
+ double *read_time,
+ double *records,
+ int index_used,
+ uint idx)
+{
+ double cardinality;
+ JOIN *join= tab->join;
+ cardinality= join->cardinality_estimate;
+ /**
+ Cases when there is no need to consider indexes that can resolve the
+ ORDER BY clause
+
+ 1) Table in consideration should be the first non-const table.
+ 2) Query does not use the ORDER BY LIMIT optimization with sort_nest
+ @see sort_nest_allowed
+ 3) Join planner is run to get an estimate of cardinality for a join
+ 4) No index present that can resolve the ORDER BY clause
+ */
+
+ if (idx != join->const_tables || // (1)
+ !join->sort_nest_possible || // (2)
+ join->get_cardinality_estimate || // (3)
+ tab->table->keys_with_ordering.is_clear_all()) // (4)
+ return -1;
+
+ THD *thd= join->thd;
+ Json_writer_object trace_index_for_ordering(thd);
+ TABLE *table= tab->table;
+ double save_read_time= *read_time;
+ double save_records= *records;
+ double est_records= *records;
+ double fanout= MY_MAX(1.0, cardinality / est_records);
+ int best_index=-1;
+ trace_index_for_ordering.add("rows_estimation", est_records);
+ Json_writer_array considered_indexes(thd, "considered_indexes");
+
+ for (uint idx= 0 ; idx < table->s->keys; idx++)
+ {
+ ha_rows select_limit= select_limit_arg;
+ if (!table->keys_with_ordering.is_set(idx))
+ continue;
+ Json_writer_object possible_key(thd);
+ double index_scan_time;
+ possible_key.add("index", table->key_info[idx].name);
+ find_cost_of_index_with_ordering(thd, tab, table, &select_limit,
+ fanout, est_records,
+ idx, &index_scan_time,
+ &possible_key);
+
+ if (index_scan_time < *read_time)
+ {
+ best_index= idx;
+ *read_time= index_scan_time;
+ *records= select_limit;
+ }
+ }
+ considered_indexes.end();
+
+ if (unlikely(thd->trace_started()))
+ {
+ trace_index_for_ordering.add("best_index",
+ static_cast<ulonglong>(best_index));
+ trace_index_for_ordering.add("records", *records);
+ trace_index_for_ordering.add("best_cost", *read_time);
+ }
+
+ /*
+ If an index already found satisfied the ordering and we picked an index
+ for which we choose to do index scan then revert the cost and stick
+ with the access picked first.
+ Index scan would not help in comparison with ref access.
+ */
+ if (tab->check_if_index_satisfies_ordering(index_used))
+ {
+ if (!table->quick_keys.is_set(static_cast<uint>(index_used)))
+ {
+ best_index= -1;
+ *records= save_records;
+ *read_time= save_read_time;
+ }
+ }
+ return best_index;
+}
+
+
+/*
+ @brief
+ Disallow join buffering for tables that are read after sorting is done.
+
+ @param
+ tab table to check if join buffering is allowed or not
+
+ @details
+ Disallow join buffering for all the tables at the top level that are read
+ after sorting is done.
+ There are 2 cases
+ 1) Sorting on the first non-const table
+ For all the tables join buffering is not allowed
+ 2) Sorting on a prefix of the join with a sort-nest
+ For the tables inside the sort-nest join buffering is allowed but
+ for tables outside the sort-nest join buffering is not allowed
+
+ Also for SJM table that come after the sort-nest, join buffering is allowed
+ for the inner tables of the SJM.
+
+ @retval
+ TRUE Join buffering is allowed
+ FALSE Otherwise
+*/
+
+bool JOIN::is_join_buffering_allowed(JOIN_TAB *tab)
+{
+ if (!sort_nest_info)
+ return TRUE;
+
+ // no need to disable join buffering for the inner tables of SJM
+ if (tab->bush_root_tab)
+ return TRUE;
+
+ if (tab->table->map & sort_nest_info->get_tables_map())
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ @brief
+ Check if an index on a table resolves the ORDER BY clause.
+
+ @param
+ index_used index to be checked
+
+ @retval
+ TRUE index resolves the ORDER BY clause
+ FALSE otherwise
+*/
+
+bool JOIN_TAB::check_if_index_satisfies_ordering(int index_used)
+{
+ /*
+ index_used is set to
+ -1 for Table Scan
+ MAX_KEY for HASH JOIN
+ >=0 for ref/range/index access
+ */
+ if (index_used < 0 || index_used == MAX_KEY)
+ return FALSE;
+
+ if (table->keys_with_ordering.is_set(static_cast<uint>(index_used)))
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ @brief
+ Set up range scan for the table.
+
+ @param
+ tab table for which range scan needs to be setup
+ idx index for which range scan needs to created
+ records estimate of records to be read with range scan
+
+ @details
+ Range scan is setup here for an index that can resolve the ORDER BY clause.
+ There are 2 cases here:
+ 1) If the range scan is on the same index for which we created
+ QUICK_SELECT when we ran the range optimizer earlier, then we try
+ to reuse it.
+ 2) The range scan is on a different index then we need to create
+ QUICK_SELECT for the new key. This is done by running the range
+ optimizer again.
+
+ Also here we take into account if the ordering is in reverse direction.
+ For DESCENDING we try to reverse the QUICK_SELECT.
+
+ @note
+ This is done for the ORDER BY LIMIT optimization. We try to force creation
+ of range scan for an index that the join planner picked for us. Also here
+ we reverse the range scan if the ordering is in reverse direction.
+*/
+
+void JOIN::setup_range_scan(JOIN_TAB *tab, uint idx, double records)
+{
+ SQL_SELECT *sel= NULL;
+ Item **sargable_cond= get_sargable_cond(this, tab->table);
+ int err, rc, direction;
+ uint used_key_parts;
+ key_map keymap_for_range;
+ Json_writer_array forcing_range(thd, "range_scan_for_order_by_limit");
+
+ sel= make_select(tab->table, const_table_map, const_table_map,
+ *sargable_cond, (SORT_INFO*) 0, 1, &err);
+ if (!sel)
+ goto use_filesort;
+
+ /*
+ If the table already had a range access, check if it is the same as the
+ one we wanted to create range scan for, if yes don't run the range
+ optimizer again.
+ */
+
+ if (!(tab->quick && tab->quick->index == idx))
+ {
+ /* Free the QUICK_SELECT that was built earlier. */
+ delete tab->quick;
+ tab->quick= NULL;
+
+ keymap_for_range.clear_all(); // Force the creation of quick select
+ keymap_for_range.set_bit(idx); // only for index using range access.
+
+ rc= sel->test_quick_select(thd, keymap_for_range,
+ (table_map) 0,
+ (ha_rows) HA_POS_ERROR,
+ true, false, true, true);
+ if (rc <= 0)
+ goto use_filesort;
+ }
+ else
+ sel->quick= tab->quick;
+
+ direction= test_if_order_by_key(this, order, tab->table, idx,
+ &used_key_parts);
+
+ if (direction == -1)
+ {
+ /*
+ QUICK structure is reversed here as the ordering is in DESC order
+ */
+ QUICK_SELECT_I *reverse_quick;
+ if (sel && sel->quick)
+ {
+ reverse_quick= sel->quick->make_reverse(used_key_parts);
+ if (!reverse_quick)
+ goto use_filesort;
+ sel->set_quick(reverse_quick);
+ }
+ }
+
+ tab->quick= sel->quick;
+
+ /*
+ Fix for explain, the records here should be set to the value
+ which was stored in the JOIN::best_positions object. This is needed
+ because the estimate of rows to be read for the first non-const table had
+ taken selectivity of limit into account.
+ */
+ if (sort_nest_possible && records < tab->quick->records)
+ tab->quick->records= records;
+
+ sel->quick= NULL;
+
+use_filesort:
+ delete sel;
+}
+
+
+/*
+ @brief
+ Setup range/index scan to resolve ordering on the first non-const table.
+
+ @param
+ index index for which index scan or range scan needs to be setup
+
+ @details
+ Here we try to prepare range scan or index scan for an index that can be
+ used to resolve the ORDER BY clause. This is used only for the first
+ non-const table of the join.
+
+ For range scan
+ There is a separate call to setup_range_scan, where the QUICK_SELECT is
+ created for range access. In case we are not able to create a range
+ access, we switch back to use Filesort on the first table.
+ see @setup_range_scan
+ For index scan
+ We just store the index in Sort_nest_info::index_used.
+*/
+
+void JOIN::setup_index_use_for_ordering(int index)
+{
+ DBUG_ASSERT(sort_nest_info->index_used == -1);
+
+ sort_nest_info->nest_tab= join_tab + const_tables;
+ POSITION *cur_pos= &best_positions[const_tables];
+ JOIN_TAB *tab= cur_pos->table;
+
+ if (cur_pos->key)
+ return;
+
+ index= (index == -1) ?
+ (cur_pos->table->quick ? cur_pos->table->quick->index : -1) :
+ index;
+
+ if (tab->check_if_index_satisfies_ordering(index))
+ {
+ if (tab->table->quick_keys.is_set(index))
+ {
+ // Range scan
+ setup_range_scan(tab, index, cur_pos->records_read);
+ sort_nest_info->index_used= -1;
+ }
+ else
+ {
+ // Index scan
+ if (tab->quick)
+ {
+ delete tab->quick;
+ tab->quick= NULL;
+ }
+ sort_nest_info->index_used= index;
+ }
+ }
+}
+
+
+/*
+ @brief
+ Get index used to access the table, if present
+
+ @retval
+ >=0 index used to access the table
+ -1 no index used to access table, probably table scan is done
+*/
+
+int JOIN_TAB::get_index_on_table()
+{
+ int idx= -1;
+
+ if (type == JT_REF || type == JT_EQ_REF || type == JT_REF_OR_NULL)
+ idx= ref.key;
+ else if (type == JT_NEXT)
+ idx= index;
+ else if (type == JT_ALL && select && select->quick)
+ idx= select->quick->index;
+ return idx;
+}
+
+
+/*
+ @brief
+ Calculate the selectivity of limit.
+
+ @details
+ The selectivity of limit is calculated as
+ selecitivity_of_limit= rows_in_limit / cardinality_of_join
+
+ @note
+ The selectivity that we get is used to make an estimate of rows
+ that we would read from the partial join of the tables inside the
+ sort-nest.
+*/
+
+void JOIN::set_fraction_output_for_nest()
+{
+ if (sort_nest_possible && !get_cardinality_estimate)
+ {
+ fraction_output_for_nest= select_limit < cardinality_estimate ?
+ select_limit / cardinality_estimate :
+ 1.0;
+ if (unlikely(thd->trace_started()))
+ {
+ Json_writer_object trace_limit(thd);
+ trace_limit.add("cardinality", cardinality_estimate);
+ trace_limit.add("selectivity_of_limit", fraction_output_for_nest*100);
+ }
+ }
+}
+
+
+/*
+ @brief
+ Sort nest is allowed when one can shortcut the join execution.
+
+ @details
+ For all the operations where one requires entire join computation to be
+ done first and then apply the operation on the join output,
+ such operations can't make use of the sort-nest.
+ So this function disables the use of sort-nest for such operations.
+
+ Sort nest is not allowed for
+ 1) No ORDER BY clause
+ 2) Only constant tables in the join
+ 3) DISTINCT CLAUSE
+ 4) GROUP BY CLAUSE
+ 5) HAVING clause
+ 6) Aggregate Functions
+ 7) Window Functions
+ 8) Using ROLLUP
+ 9) Using SQL_BUFFER_RESULT
+ 10) LIMIT is absent
+ 11) Only SELECT queries can use the sort nest
+
+ @retval
+ TRUE Sort-nest is allowed
+ FALSE Otherwise
+
+*/
+
+bool JOIN::sort_nest_allowed()
+{
+ return thd->variables.use_sort_nest && order &&
+ !(const_tables == table_count ||
+ (select_distinct || group_list) ||
+ having ||
+ MY_TEST(select_options & OPTION_BUFFER_RESULT) ||
+ (rollup.state != ROLLUP::STATE_NONE && select_distinct) ||
+ select_lex->window_specs.elements > 0 ||
+ select_lex->agg_func_used() ||
+ select_limit == HA_POS_ERROR ||
+ thd->lex->sql_command != SQLCOM_SELECT);
+}
+
+
+/*
+ @brief
+ Consider adding a sort-nest on a prefix of the join
+
+ @param prefix_tables map of all the tables in the prefix
+
+ @details
+ This function is used during the join planning stage, where the join
+ planner decides if it can add a sort-nest on a prefix of a join.
+ The join planner does not add the sort-nest in the following cases:
+ 1) Queries where adding a sort-nest is not possible.
+ see @sort_nest_allowed
+ 2) Join planner is run to get the cardinality of the join
+ 3) All inner tables of an outer join are inside the nest or outside
+ 4) All inner tables of a semi-join are inside the nest or outside
+ 5) Given prefix cannot resolve the ORDER BY clause
+
+ @retval
+ TRUE sort-nest can be added on a prefix of a join
+ FALSE otherwise
+*/
+
+bool JOIN::consider_adding_sort_nest(table_map prefix_tables)
+{
+ if (!sort_nest_possible || // (1)
+ get_cardinality_estimate || // (2)
+ cur_embedding_map || // (3)
+ cur_sj_inner_tables) // (4)
+ return FALSE;
+
+ return check_join_prefix_resolves_ordering(prefix_tables); // (5)
+}
+
+
+/*
+ @brief
+ Check if indexes on a table are allowed to resolve the ORDER BY clause
+
+ @param
+ idx position of the table in the partial plan
+
+ @retval
+ TRUE Indexes are allowed to resolve ORDER BY clause
+ FALSE Otherwise
+
+*/
+
+bool JOIN::is_index_with_ordering_allowed(uint idx)
+{
+ /*
+ An index on a table can allowed to resolve ordering in these cases:
+ 1) Table should be the first non-const table
+ 2) Query that allows the ORDER BY LIMIT optimization.
+ @see sort_nest_allowed
+ 3) Join planner is not run to get the estimate of cardinality
+ */
+ return idx == const_tables && // (1)
+ sort_nest_possible && // (2)
+ !get_cardinality_estimate; // (3)
+}
+
+/*
+ @brief
+ Find the cost to access a table with an index that can resolve ORDER BY.
+
+ @param
+ THD thread structure
+ tab join_tab structure for joined table
+ table first non-const table
+ select_limit_arg limit for the query
+ fanout fanout of the join
+ est_best_records estimate of records for best access
+ nr index number
+ index_scan_time[out] cost to access the table with the
+ the index
+*/
+
+void find_cost_of_index_with_ordering(THD *thd, const JOIN_TAB *tab,
+ TABLE *table,
+ ha_rows *select_limit_arg,
+ double fanout, double est_best_records,
+ uint nr, double *index_scan_time,
+ Json_writer_object *trace_possible_key)
+{
+ KEY *keyinfo= table->key_info + nr;
+ ha_rows select_limit= *select_limit_arg;
+ double rec_per_key;
+ double table_records= table->stat_records();
+ /*
+ If tab=tk is not the last joined table tn then to get first
+ L records from the result set we can expect to retrieve
+ only L/fanout(tk,tn) where fanout(tk,tn) says how many
+ rows in the record set on average will match each row tk.
+ Usually our estimates for fanouts are too pessimistic.
+ So the estimate for L/fanout(tk,tn) will be too optimistic
+ and as result we'll choose an index scan when using ref/range
+ access + filesort will be cheaper.
+ */
+ select_limit= (ha_rows) (select_limit < fanout ?
+ 1 : select_limit/fanout);
+
+ /*
+ refkey_rows_estimate is E(#rows) produced by the table access
+ strategy that was picked without regard to ORDER BY ... LIMIT.
+
+ It will be used as the source of selectivity data.
+ Use table->cond_selectivity as a better estimate which includes
+ condition selectivity too.
+ */
+ {
+ // we use MIN(...), because "Using LooseScan" queries have
+ // cond_selectivity=1 while refkey_rows_estimate has a better
+ // estimate.
+ est_best_records= MY_MIN(est_best_records,
+ ha_rows(table_records * table->cond_selectivity));
+ }
+
+ /*
+ We assume that each of the tested indexes is not correlated
+ with ref_key. Thus, to select first N records we have to scan
+ N/selectivity(ref_key) index entries.
+ selectivity(ref_key) = #scanned_records/#table_records =
+ refkey_rows_estimate/table_records.
+ In any case we can't select more than #table_records.
+ N/(refkey_rows_estimate/table_records) > table_records
+ <=> N > refkey_rows_estimate.
+ */
+
+ if (select_limit > est_best_records)
+ select_limit= table_records;
+ else
+ select_limit= (ha_rows) (select_limit *
+ (double) table_records /
+ est_best_records);
+
+ rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1);
+ set_if_bigger(rec_per_key, 1);
+ /*
+ Here we take into account the fact that rows are
+ accessed in sequences rec_per_key records in each.
+ Rows in such a sequence are supposed to be ordered
+ by rowid/primary key. When reading the data
+ in a sequence we'll touch not more pages than the
+ table file contains.
+ TODO. Use the formula for a disk sweep sequential access
+ to calculate the cost of accessing data rows for one
+ index entry.
+ */
+ *index_scan_time= select_limit/rec_per_key *
+ MY_MIN(rec_per_key, table->file->scan_time());
+
+ if (unlikely(thd->trace_started()))
+ {
+ trace_possible_key->add("updated_limit", select_limit);
+ trace_possible_key->add("index_scan_time", *index_scan_time);
+ }
+
+ double range_scan_time;
+ if (get_range_limit_read_cost(tab, table, table_records, nr,
+ select_limit, &range_scan_time))
+ {
+ trace_possible_key->add("range_scan_time", range_scan_time);
+ if (range_scan_time < *index_scan_time)
+ *index_scan_time= range_scan_time;
+ }
+ *select_limit_arg= select_limit;
+}
+
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index eea14d7dfc2..56396d5e0fe 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -1008,9 +1008,8 @@ bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
{
select_lex->parsing_place= IN_WHERE;
conds=
- conds->transform(thd,
- &Item::in_predicate_to_in_subs_transformer,
- (uchar*) 0);
+ conds->transform(thd, &Item::in_predicate_to_in_subs_transformer,
+ FALSE, (uchar*) 0);
if (!conds)
DBUG_RETURN(true);
select_lex->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
@@ -1029,7 +1028,7 @@ bool JOIN::transform_in_predicates_into_in_subq(THD *thd)
{
table->on_expr=
table->on_expr->transform(thd,
- &Item::in_predicate_to_in_subs_transformer,
+ &Item::in_predicate_to_in_subs_transformer, FALSE,
(uchar*) 0);
if (!table->on_expr)
DBUG_RETURN(true);
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index ff92b042ccc..173e38dc63b 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -6409,3 +6409,9 @@ static Sys_var_ulonglong Sys_max_rowid_filter_size(
SESSION_VAR(max_rowid_filter_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(128*1024),
BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_use_sort_nest(
+ "use_sort_nest",
+ "Enable the sort nest",
+ SESSION_VAR(use_sort_nest), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
diff --git a/sql/table.h b/sql/table.h
index 898c31c4aff..3f5c75df9d6 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1409,6 +1409,8 @@ public:
*/
SplM_opt_info *spl_opt_info;
key_map keys_usable_for_splitting;
+ /* Map of keys that can be used to resolve the ORDER BY clause */
+ key_map keys_with_ordering;
/*
Conjunction of the predicates of the form IS NOT NULL(f) where f refers to