From a883c38e13e8cb7f4a5c5a95f15b6d6c892fbdf5 Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Tue, 24 Feb 2026 00:24:32 +0700 Subject: [PATCH 1/3] MDEV-38045: Preparation commit 1. `TABLE_LIST::init_derived` is called twice during derived tables processing: first time from `mysql_derived_init` and the second time from `mysql_derived_prepare`. Both times there is a check for whether an optimizer hint or switch setting allows a derived table to be merged. However, it is not necessary to restrict merging during the initialization call, it is enough to apply hints/switch setting during the preparation phase. 2. `open_normal_and_derived_tables()` runs processing of all phases of derived tables handling with a single call. But for future commits it is required to separate DT initialization from other phases. This commit implements the separation. --- sql/sql_base.cc | 11 +++++++++-- sql/table.cc | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 059cf17e75bf6..eaaa6b21c6250 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5809,8 +5809,15 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, uint counter; MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_normal_and_derived_tables"); - if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) || - mysql_handle_derived(thd->lex, dt_phases)) + if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy)) + goto end; + + // Process initialization phase if it is requested in dt_phases + if ((dt_phases & DT_INIT) && mysql_handle_derived(thd->lex, DT_INIT)) + goto end; + + // Process all phases remaining after DT_INIT + if (mysql_handle_derived(thd->lex, dt_phases & ~DT_INIT)) goto end; DBUG_RETURN(0); diff --git a/sql/table.cc b/sql/table.cc index e81ca13a4db4d..b673b08386e67 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -10108,7 +10108,10 @@ static inline bool derived_table_optimization_done(TABLE_LIST *table) @brief Initialize this derived table/view - @param thd Thread handle + @param + thd Thread handle + init_view TRUE when called from mysql_derived_init (first call) + FALSE when called from mysql_derived_prepare (second call) @details This function makes initial preparations of this derived table/view for @@ -10186,9 +10189,18 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) !this->opt_hints_table) // Table hints are not adjusted yet select_lex->opt_hints_qb->fix_hints_for_derived_table(this); + /* + (1) Allow merging during initialization (first call, init_view == true), + while optimizer hints are not yet resolved. By the time of + the second call (preparation, init_view == false) optimizer hints are + already resolved, so the previous choice can be re-considered. + (2) Prefer optimizer hint setting for this particular table. + If there is no hint then fall back to the optimizer switch setting. + */ bool is_derived_merge_allowed= + init_view || // (1) hint_table_state(thd, this, MERGE_HINT_ENUM, - optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)); + optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)); // (2) if (!is_materialized_derived() && unit->can_be_merged() && /* From 33f120fa0eb3e095ae8b863bf177b86f66ee6983 Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Sun, 9 Nov 2025 02:01:07 +0700 Subject: [PATCH 2/3] MDEV-38045: Implement implicit query block names for optimizer hints This patch implements support for implicit query block (QB) names in optimizer hints, allowing hints to reference query blocks and tables within derived tables, views and CTEs without requiring explicit QB_NAME hints. Examples. -- Addressing a table inside a derived table using implicit QB name select /*+ no_index(t1@dt) */ * from (select * from t1 where a > 10) as DT; -- this is an equivalent to: select /*+ no_index(t1@dt) */ * from (select /*+ qb_name(dt)*/ * from t1 where a > 10) as DT; -- Addressing a query block corresponding to the derived table select /*+ no_bnl(@dt) */ * from (select * from t1, t2 where t.1.a > t2.a) as DT; -- View create view v1 as select * from t1 where a > 10 and b > 100; -- referencing a table inside a view by implicit QB name: select /*+ index_merge(t1@v1 idx_a, idx_b) */ * from v1, t2 where v1.a = t2.a; -- equivalent to: create view v1 as select /*+ qb_name(qb_v1) */ * from t1 where a > 10 and b > 100; select /*+ index_merge(t1@qb_v1 idx_a, idx_b) */ * from v1, t2 where v1.a = t2.a; -- CTE with aless100 as (select a from t1 where b <100) select /*+ index(t1@aless100) */ * from aless100; -- equivalent to: with aless100 as (select /*+ qb_name(aless100) */ a from t1 where b <100) select /*+ index(t1@aless100) */ * from aless100; Limitations: - Only SELECT statements support implicit QB names. DML operations (UPDATE, DELETE, INSERT) only support explicit QB names --- mysql-test/main/opt_hints.result | 48 +- mysql-test/main/opt_hints.test | 26 +- mysql-test/main/opt_hints_impl_qb_name.inc | 241 +++++ mysql-test/main/opt_hints_impl_qb_name.result | 850 ++++++++++++++++++ mysql-test/main/opt_hints_impl_qb_name.test | 9 + sql/opt_hints.cc | 159 +++- sql/opt_hints.h | 1 + sql/opt_hints_parser.cc | 26 +- sql/opt_hints_parser.h | 43 +- sql/opt_hints_structs.h | 53 ++ sql/share/errmsg-utf8.txt | 4 + sql/sql_base.cc | 2 + sql/sql_parse.cc | 3 +- sql/sql_prepare.cc | 2 +- sql/sql_select.cc | 2 +- sql/sql_show.cc | 1 + sql/sql_view.cc | 1 + 17 files changed, 1402 insertions(+), 69 deletions(-) create mode 100644 mysql-test/main/opt_hints_impl_qb_name.inc create mode 100644 mysql-test/main/opt_hints_impl_qb_name.result create mode 100644 mysql-test/main/opt_hints_impl_qb_name.test create mode 100644 sql/opt_hints_structs.h diff --git a/mysql-test/main/opt_hints.result b/mysql-test/main/opt_hints.result index fe305997302ac..53848c4e00339 100644 --- a/mysql-test/main/opt_hints.result +++ b/mysql-test/main/opt_hints.result @@ -439,6 +439,18 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 ref f1 f1 8 test.t3.f1,test.t3.f2 2 50.00 Using where; FirstMatch(t3) Warnings: Note 1003 update /*+ BKA(`t2`@`select#2`) NO_BNL(`t1`@`select#2`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) */ `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f2` = `test`.`t3`.`f2` and `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 and `test`.`t3`.`f2` between `test`.`t3`.`f1` and `test`.`t1`.`f2` and `test`.`t3`.`f2` + 1 >= `test`.`t3`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Same as above but addressing tables with QB name +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) BKA(t2@QB1) NO_BNL(t1@QB1)*/ t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT /*+ QB_NAME(QB1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 range PRIMARY,f2_idx,f3_idx f2_idx 4 NULL 2 100.00 Using index condition; Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3.33 Using where +1 PRIMARY t2 ref f1 f1 8 test.t3.f1,test.t3.f2 2 50.00 Using where; FirstMatch(t3) +Warnings: +Note 1003 update /*+ BKA(`t2`@`QB1`) NO_BNL(`t1`@`QB1`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) */ `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f2` = `test`.`t3`.`f2` and `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 and `test`.`t3`.`f2` between `test`.`t3`.`f1` and `test`.`t1`.`f2` and `test`.`t3`.`f2` + 1 >= `test`.`t3`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` # Turn off range access for all keys. EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3) */ t3 SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN @@ -450,6 +462,31 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t3 eq_ref PRIMARY,f2_idx,f3_idx PRIMARY 4 test.t2.f1 1 100.00 Using where; End temporary Warnings: Note 1003 update /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1`) */ `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f2` = `test`.`t2`.`f2` and `test`.`t2`.`f1` > 30 and `test`.`t2`.`f1` < 33 and `test`.`t2`.`f2` between `test`.`t2`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t2`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Multi-table UPDATE with a derived table +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t2@dt)*/ t3, +(SELECT /*+ qb_name(dt)*/ t1.* FROM t1, t2 WHERE t1.f1 > t2.f1) AS dt +SET t3.f3 = 'mnbv' WHERE t3.f1 = dt.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL PRIMARY,f2_idx NULL NULL NULL 56 100.00 +1 PRIMARY ref key0 key0 5 test.t3.f1 8 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00 +2 DERIVED t2 index f1 f1 8 NULL 28 100.00 Using where; Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ update /*+ NO_RANGE_OPTIMIZATION(`t2`@`dt`) */ `test`.`t3` join (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`f1` > `test`.`t2`.`f1`) `dt` set `test`.`t3`.`f3` = 'mnbv' where `dt`.`f1` = `test`.`t3`.`f1` +# Multi-table UPDATE with a derived table and both table-level and +# query block-level hints +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t2@dt) NO_BNL(@dt)*/ t3, +(SELECT /*+ qb_name(dt)*/ t1.* FROM t1, t2 WHERE t1.f1 > t2.f1) AS dt +SET t3.f3 = 'mnbv' WHERE t3.f1 = dt.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL PRIMARY,f2_idx NULL NULL NULL 56 100.00 +1 PRIMARY ref key0 key0 5 test.t3.f1 8 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00 +2 DERIVED t2 index f1 f1 8 NULL 28 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ update /*+ NO_BNL(@`dt`) NO_RANGE_OPTIMIZATION(`t2`@`dt`) */ `test`.`t3` join (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`f1` > `test`.`t2`.`f1`) `dt` set `test`.`t3`.`f3` = 'mnbv' where `dt`.`f1` = `test`.`t3`.`f1` EXPLAIN EXTENDED DELETE FROM t3 WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN (SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND @@ -500,7 +537,7 @@ Warnings: Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#1`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) # Turn off ICP for a particular table and a key EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) -(SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1 x_idx)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5 +(SELECT /*+ NO_ICP(t5@QB1 x_idx) QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 @@ -550,15 +587,14 @@ id select_type table type possible_keys key key_len ref rows filtered Extra Warnings: Warning 4219 Hint QB_NAME(`qb1`) is ignored as conflicting/duplicated Note 1003 select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t2` -# Should issue warning -EXPLAIN EXTENDED SELECT /*+ BKA(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +# QB_NAME may appear after the hint, but the hint is still resolved +EXPLAIN EXTENDED SELECT /*+ NO_BNL(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 -1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where Warnings: -Warning 4220 Query block name `qb1` is not found for BKA hint -Note 1003 select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1 +Note 1003 select /*+ QB_NAME(`qb1`) NO_BNL(@`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1 # Should not crash PREPARE stmt1 FROM "SELECT /*+ BKA(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1"; diff --git a/mysql-test/main/opt_hints.test b/mysql-test/main/opt_hints.test index c3d1b3660ef7d..35282287bae86 100644 --- a/mysql-test/main/opt_hints.test +++ b/mysql-test/main/opt_hints.test @@ -248,12 +248,32 @@ SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN (SELECT /*+ BKA(t2) NO_BNL(t1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +--echo # Same as above but addressing tables with QB name +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) BKA(t2@QB1) NO_BNL(t1@QB1)*/ t3 + SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT /*+ QB_NAME(QB1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + --echo # Turn off range access for all keys. EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3) */ t3 SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN (SELECT t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +--echo # Multi-table UPDATE with a derived table +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t2@dt)*/ t3, + (SELECT /*+ qb_name(dt)*/ t1.* FROM t1, t2 WHERE t1.f1 > t2.f1) AS dt +SET t3.f3 = 'mnbv' WHERE t3.f1 = dt.f1; + +--echo # Multi-table UPDATE with a derived table and both table-level and +--echo # query block-level hints +EXPLAIN EXTENDED +UPDATE /*+ NO_RANGE_OPTIMIZATION(t2@dt) NO_BNL(@dt)*/ t3, + (SELECT /*+ qb_name(dt)*/ t1.* FROM t1, t2 WHERE t1.f1 > t2.f1) AS dt +SET t3.f3 = 'mnbv' WHERE t3.f1 = dt.f1; + EXPLAIN EXTENDED DELETE FROM t3 WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN (SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND @@ -282,7 +302,7 @@ EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) --echo # Turn off ICP for a particular table and a key EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) - (SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1 x_idx)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5 + (SELECT /*+ NO_ICP(t5@QB1 x_idx) QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); --echo # Make sure ICP is expected to be used when there are no hints @@ -308,8 +328,8 @@ EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3) --echo # Should issue warning EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) QB_NAME(qb1 ) */ * FROM t2; ---echo # Should issue warning -EXPLAIN EXTENDED SELECT /*+ BKA(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +--echo # QB_NAME may appear after the hint, but the hint is still resolved +EXPLAIN EXTENDED SELECT /*+ NO_BNL(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1; --echo # Should not crash diff --git a/mysql-test/main/opt_hints_impl_qb_name.inc b/mysql-test/main/opt_hints_impl_qb_name.inc new file mode 100644 index 0000000000000..04b1107468d61 --- /dev/null +++ b/mysql-test/main/opt_hints_impl_qb_name.inc @@ -0,0 +1,241 @@ +--source include/have_sequence.inc +--enable_prepare_warnings + +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); + +insert into t1 select seq, seq, 'filler' from seq_1_to_100; + +create table t2 as select * from t1; + +analyze table t1,t2 persistent for all; + +--echo # Table-level hint +explain extended select /*+ no_bnl(t2@dt)*/ * from + (select t1.* from t1, t2 where t1.a > t2.a) as dt; + +--echo # More than one reference to a single QB +explain extended select /*+ no_bnl(t2@dt) no_index(t1@dt)*/ * from + (select t1.* from t1, t2 where t1.a > t2.a) as dt; + +--echo # QB-level hint +explain extended select /*+ no_bnl(@dt)*/ * from + (select t1.* from t1, t2 where t1.a > t2.a) as dt; + +--echo # Index-level hints +--echo # Without the hint 'range' index access would be chosen +explain extended select /*+ no_index(t1@`T`)*/ * from + (select * from t1 where a < 3) t; + +--echo # Without the hint 'range' index access would be chosen +explain extended select /*+ no_range_optimization(t1@t1)*/ * from + (select * from t1 where a > 100 and a < 120) as t1; + +--echo # Regular and derived tables share same name but the hint is applied correctly +explain extended select /*+ index(t1@t1 idx_ab)*/ * from + (select * from t1 where a < 3) as t1; + +explain extended select /*+ no_index(t1@t2 idx_a) index(t1@t1 idx_ab)*/ * from + (select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; + +explain extended select /*+ no_index(t1@t1 idx_a, idx_ab)*/ * from + (select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; + +--echo # Nested derived tables +explain extended select /*+ no_bnl(t1@dt2)*/ * from + (select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; + +explain extended select /*+ no_index(t1@DT2)*/ * from + (select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; + +--echo # Explicit QB name overrides the implicit one +explain extended select /*+ no_index(t1@dt2)*/ * from + (select count(*) from t1, (select /*+ qb_name(dt2)*/ * from t1 where a < 5) dt1) as dt2; + +--echo # Both hints are applied +explain extended select /*+ no_index(t1@dt1) no_bnl(t1@dt2)*/ * from + (select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; + +--disable_ps_protocol +# PS protocol is disabled because the warning is genererated only during +# PREPARE step of a statement. When the QB name cannot be resolved the hint +# is discarded, so it is not present during EXECUTE step. The same applies +# not only to implicit but also to explicit QB names. + +--echo # Nested derived tables with ambiguous names, hint is ignored +explain extended select /*+ no_index(t1@t1)*/* from + (select count(*) from t1, (select * from t1 where a < 5) t1) as t1; + +--echo # The hint cannot be applied to a derived table with UNION +explain extended select /*+ no_index(t2@t1)*/* from + (select * from t1 where a < 3 union select * from t2 where a < 9) as t1; + +explain extended select /*+ no_index(t1@t1)*/* from + (select * from t1 where a < 3 union select * from t2 where a < 9) as t1; + +--echo # Test INSERT..SELECT +explain extended insert into t2 select /*+ no_bnl(t2@dt)*/ * from + (select t1.* from t1, t2 where t1.a > t2.a) as dt; + +--echo # Test MERGE and NO_MERGE hints +explain extended select /*+ merge(@dt)*/ * from + (select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; + +explain extended select /*+ no_merge(@dt)*/ * from + (select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; + +--echo # Multiple levels of nested derived tables, all hints are applied +explain extended +select /*+ no_merge(dv) no_bnl(t2@dt) */ * from ( + select /*+ no_merge(du) */ * from ( + select /*+ no_merge(dt) */ * from ( + select t1.* from t1, t2 where t1.a > t2.a + ) dt + ) du +) dv; + +--enable_ps_protocol + +--echo # ====================================== +--echo # Test CTEs +--echo # By default BNL and index access to t1 are used. +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; + +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(t1@cte)*/ * from cte; + +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte; + +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte)*/ * from cte; + +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte) no_index(t1@dt1)*/ * from cte; + +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@dt1 idx_a) no_bnl(@cte)*/ * from cte; + + +--disable_ps_protocol +# See the comment above for why PS protocol is disabled + +--echo # Ambiguity: multiple occurencies of `cte`, the hint is ignored +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte where cnt > 10 +union +select * from cte where cnt < 100; + +--echo # However, if CTE occurencies have different aliases, the hint can be applied +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte1)*/ * from cte as cte1 where cnt > 10 +union +select * from cte where cnt < 100; + +--enable_ps_protocol + +--echo # ====================================== +--echo # Test views +create view v1 as select * from t1 where a < 100; + +--echo # Default execution plan +explain extended select * + from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; + +explain extended + select /*+ index(t1@v1 idx_ab) no_index(t1@`v2`)*/ * + from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; + +--echo # Nested views +create view v2 as select * from v1 where a < 300; + +--echo # Default execution plan +explain extended select * from v2; + +--echo # Addressing an object inside a nested view +explain extended select /*+ index(t1@`v1` idx_ab)*/ * from v2; + +create view v3 as select * from t1 union select * from t2; + +--disable_ps_protocol +# See the comment above for why PS protocol is disabled + +--echo # Unable to apply the hint to a view with UNION +explain extended select /*+ no_index(t1@v3) */ * from v3; + +--echo # Ambiguity: view `v1` appears two times - should warn and ignore hint +explain extended select /*+ index(t2@v1) */ * from v1, + (select a from v1 where b > 5) dt; + +--enable_ps_protocol + +--echo # Implicit QB names are not supported inside views +create view v4 as select /*+ no_bnl(t2@dt)*/ * from + (select t1.* from t1, t2 where t1.a > t2.a) as dt; + +show create view v4; + +--echo # However, a derived table inside a view can be addressed from outer query +create view v5 as select dt.a from + t1, (select t1.* from t1, t2 where t1.a > t2.a) as dt where t1.a=dt.a; + +--echo # Addressing a single table +explain extended select /*+ no_bnl(t2@dt) */* from v5; +--echo # Addressing the whole derived table +explain extended select /*+ no_bnl(@dt) */* from v5; + +--echo # Derived tables inside views can be addressed by their aliases +explain extended select /*+ no_bnl(t2@dt) */ * from v4; + +drop view v1, v2, v3, v4, v5; + +--echo # ====================================== +--echo # Not supported for DML, check presence of warnings + +--disable_ps_protocol +explain extended +update /*+ no_range_optimization(t1@dt)*/ t2, + (select a from t1 where a > 10) dt +set b=1 where t2.a = dt.a; + +explain extended +delete /*+ no_index(t1@dt)*/ from t2 +where t2.a in + (select * from (select a from t1 where a > 10) dt where dt.a > 20); + +--echo # ====================================== +--echo # Objects in triggers and stored functions must not be visible for hints +create table t3 (a int, b int); + +--echo # Trigger with derived table inside +delimiter |; +create trigger tr1 before insert on t3 +for each row +begin + set new.b = (select max(a) from + (select a from t1 where a < new.a) dt); +end| +delimiter ;| + +--echo # Warning expected +explain extended select /*+ no_index(t1@dt) */ max(a) from t3 where a<2; + +--echo # Stored function with derived table inside +create function get_max(p_a int) returns int +return (select max(a) from + (select a from t1 where a < p_a) dt); + +--echo # Warning expected +explain extended select /*+ no_index(t1@dt) */ a, get_max(a) from t1; + +--enable_ps_protocol +drop function get_max; +drop table t1, t2, t3; \ No newline at end of file diff --git a/mysql-test/main/opt_hints_impl_qb_name.result b/mysql-test/main/opt_hints_impl_qb_name.result new file mode 100644 index 0000000000000..3c73eb3c491fa --- /dev/null +++ b/mysql-test/main/opt_hints_impl_qb_name.result @@ -0,0 +1,850 @@ +set optimizer_switch = 'derived_merge=on'; +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); +insert into t1 select seq, seq, 'filler' from seq_1_to_100; +create table t2 as select * from t1; +analyze table t1,t2 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +# Table-level hint +explain extended select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(`t2`@`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +# More than one reference to a single QB +explain extended select /*+ no_bnl(t2@dt) no_index(t1@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(`t2`@`dt`) NO_INDEX(`t1`@`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +# QB-level hint +explain extended select /*+ no_bnl(@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(@`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +# Index-level hints +# Without the hint 'range' index access would be chosen +explain extended select /*+ no_index(t1@`T`)*/ * from +(select * from t1 where a < 3) t; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 2.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`T`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 +# Without the hint 'range' index access would be chosen +explain extended select /*+ no_range_optimization(t1@t1)*/ * from +(select * from t1 where a > 100 and a < 120) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 1.00 Using where +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`t1`@`t1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 100 and `test`.`t1`.`a` < 120 +# Regular and derived tables share same name but the hint is applied correctly +explain extended select /*+ index(t1@t1 idx_ab)*/ * from +(select * from t1 where a < 3) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 select /*+ INDEX(`t1`@`t1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 +explain extended select /*+ no_index(t1@t2 idx_a) index(t1@t1 idx_ab)*/ * from +(select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 3 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`t2` `idx_a`) INDEX(`t1`@`t1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5 and `test`.`t1`.`a` < 3 +explain extended select /*+ no_index(t1@t1 idx_a, idx_ab)*/ * from +(select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 2.00 Using where +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`t1` `idx_a`,`idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5 and `test`.`t1`.`a` < 3 +# Nested derived tables +explain extended select /*+ no_bnl(t1@dt2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`dt2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5) `dt2` +explain extended select /*+ no_index(t1@DT2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`DT2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`DT2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5) `dt2` +# Explicit QB name overrides the implicit one +explain extended select /*+ no_index(t1@dt2)*/ * from +(select count(*) from t1, (select /*+ qb_name(dt2)*/ * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5) `dt2` +# Both hints are applied +explain extended select /*+ no_index(t1@dt1) no_bnl(t1@dt2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt1`) NO_BNL(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`dt2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5) `dt2` +# Nested derived tables with ambiguous names, hint is ignored +explain extended select /*+ no_index(t1@t1)*/* from +(select count(*) from t1, (select * from t1 where a < 5) t1) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Warning 4256 Query block name `t1` is ambiguous for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`count(*)` AS `count(*)` from (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5) `t1` +# The hint cannot be applied to a derived table with UNION +explain extended select /*+ no_index(t2@t1)*/* from +(select * from t1 where a < 3 union select * from t2 where a < 9) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +3 UNION t2 ALL NULL NULL NULL NULL 100 8.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `t1` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 union /* select#3 */ select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t2` where `test`.`t2`.`a` < 9) `t1` +explain extended select /*+ no_index(t1@t1)*/* from +(select * from t1 where a < 3 union select * from t2 where a < 9) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +3 UNION t2 ALL NULL NULL NULL NULL 100 8.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `t1` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 union /* select#3 */ select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t2` where `test`.`t2`.`a` < 9) `t1` +# Test INSERT..SELECT +explain extended insert into t2 select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 Using temporary +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 insert into `test`.`t2` select /*+ NO_BNL(`t2`@`dt`) */ sql_buffer_result `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +# Test MERGE and NO_MERGE hints +explain extended select /*+ merge(@dt)*/ * from +(select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ MERGE(@`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +explain extended select /*+ no_merge(@dt)*/ * from +(select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +3 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(@`dt`) */ `dt1`.`a` AS `a`,`dt1`.`b` AS `b`,`dt1`.`c` AS `c` from (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt1` +# Multiple levels of nested derived tables, all hints are applied +explain extended +select /*+ no_merge(dv) no_bnl(t2@dt) */ * from ( +select /*+ no_merge(du) */ * from ( +select /*+ no_merge(dt) */ * from ( +select t1.* from t1, t2 where t1.a > t2.a +) dt +) du +) dv; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED ALL NULL NULL NULL NULL 10000 100.00 +3 DERIVED ALL NULL NULL NULL NULL 10000 100.00 +4 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +4 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(`dt`@`select#3`) NO_MERGE(`du`@`select#2`) NO_MERGE(`dv`@`select#1`) NO_BNL(`t2`@`dt`) */ `dv`.`a` AS `a`,`dv`.`b` AS `b`,`dv`.`c` AS `c` from (/* select#2 */ select `du`.`a` AS `a`,`du`.`b` AS `b`,`du`.`c` AS `c` from (/* select#3 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#4 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt`) `du`) `dv` +# ====================================== +# Test CTEs +# By default BNL and index access to t1 are used. +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(t1@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_BNL(`t1`@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_BNL(@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte) no_index(t1@dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte`) NO_INDEX(`t1`@`dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@dt1 idx_a) no_bnl(@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 300 100.00 +2 DERIVED t1 range idx_ab idx_ab 5 NULL 3 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`dt1` `idx_a`) NO_BNL(@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Ambiguity: multiple occurencies of `cte`, the hint is ignored +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte where cnt > 10 +union +select * from cte where cnt < 100; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using where +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +4 UNION ALL NULL NULL NULL NULL 200 100.00 Using where +5 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4256 Query block name `cte` is ambiguous for NO_BNL hint +Note 1003 with cte as (/* select#2 */ select count(0) AS `cnt` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5 having `cnt` > 10)/* select#1 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` > 10 union /* select#4 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` < 100 +# However, if CTE occurencies have different aliases, the hint can be applied +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte1)*/ * from cte as cte1 where cnt > 10 +union +select * from cte where cnt < 100; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using where +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +4 UNION ALL NULL NULL NULL NULL 200 100.00 Using where +5 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte1`) */ count(0) AS `cnt` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5 having `cnt` > 10)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte1`) */ `cte1`.`cnt` AS `cnt` from `cte` `cte1` where `cte1`.`cnt` > 10 union /* select#4 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` < 100 +# ====================================== +# Test views +create view v1 as select * from t1 where a < 100; +# Default execution plan +explain extended select * +from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +1 SIMPLE t1 ref idx_a,idx_ab idx_a 5 test.t1.a 1 100.00 +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` < 3 and `test`.`t1`.`a` < 100 and `test`.`t1`.`a` < 100 +explain extended +select /*+ index(t1@v1 idx_ab) no_index(t1@`v2`)*/ * +from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 99.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ INDEX(`t1`@`v1` `idx_ab`) NO_INDEX(`t1`@`v2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` < 3 and `test`.`t1`.`a` < 100 and `test`.`t1`.`a` < 100 +# Nested views +create view v2 as select * from v1 where a < 300; +# Default execution plan +explain extended select * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 94.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 300 and `test`.`t1`.`a` < 100 +# Addressing an object inside a nested view +explain extended select /*+ index(t1@`v1` idx_ab)*/ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 99 100.00 Using index condition +Warnings: +Note 1003 select /*+ INDEX(`t1`@`v1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 300 and `test`.`t1`.`a` < 100 +create view v3 as select * from t1 union select * from t2; +# Unable to apply the hint to a view with UNION +explain extended select /*+ no_index(t1@v3) */ * from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +3 UNION t2 ALL NULL NULL NULL NULL 100 100.00 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `v3` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Ambiguity: view `v1` appears two times - should warn and ignore hint +explain extended select /*+ index(t2@v1) */ * from v1, +(select a from v1 where b > 5) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_ab 5 NULL 99 90.20 Using where; Using index +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 94.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Warning 4256 Query block name `v1` is ambiguous for INDEX hint +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`b` > 5 and `test`.`t1`.`a` < 100 and `test`.`t1`.`a` < 100 +# Implicit QB names are not supported inside views +create view v4 as select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +Warnings: +Warning 4242 Implicit query block names are ignored for hints specified within VIEWs +show create view v4; +View Create View character_set_client collation_connection +v4 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (`t1` join `t2`) where `t1`.`a` > `t2`.`a`) `dt` latin1 latin1_swedish_ci +# However, a derived table inside a view can be addressed from outer query +create view v5 as select dt.a from +t1, (select t1.* from t1, t2 where t1.a > t2.a) as dt where t1.a=dt.a; +# Addressing a single table +explain extended select /*+ no_bnl(t2@dt) */* from v5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index idx_a,idx_ab idx_a 5 NULL 100 100.00 Using where; Using index +1 SIMPLE t1 ref idx_a,idx_ab idx_a 5 test.t1.a 1 100.00 Using index +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(`t2`@`dt`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` > `test`.`t2`.`a` +# Addressing the whole derived table +explain extended select /*+ no_bnl(@dt) */* from v5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index idx_a,idx_ab idx_a 5 NULL 100 100.00 Using where; Using index +1 SIMPLE t1 ref idx_a,idx_ab idx_a 5 test.t1.a 1 100.00 Using index +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(@`dt`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` > `test`.`t2`.`a` +# Derived tables inside views can be addressed by their aliases +explain extended select /*+ no_bnl(t2@dt) */ * from v4; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 select /*+ NO_BNL(`t2`@`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a` +drop view v1, v2, v3, v4, v5; +# ====================================== +# Not supported for DML, check presence of warnings +explain extended +update /*+ no_range_optimization(t1@dt)*/ t2, +(select a from t1 where a > 10) dt +set b=1 where t2.a = dt.a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 100 100.00 Using where +1 PRIMARY ref key0 key0 5 test.t2.a 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_ab 5 NULL 92 100.00 Using where; Using index +Warnings: +Warning 4220 Query block name `dt` is not found for NO_RANGE_OPTIMIZATION hint +Note 1003 /* select#1 */ update `test`.`t2` join (/* select#2 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 10) `dt` set `test`.`t2`.`b` = 1 where `dt`.`a` = `test`.`t2`.`a` +explain extended +delete /*+ no_index(t1@dt)*/ from t2 +where t2.a in +(select * from (select a from t1 where a > 10) dt where dt.a > 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 100 90.00 Using where +1 PRIMARY t1 ref idx_a,idx_ab idx_a 5 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 delete from `test`.`t2` using (`test`.`t1`) where `test`.`t1`.`a` = `test`.`t2`.`a` and `test`.`t2`.`a` > 20 and `test`.`t2`.`a` > 10 +# ====================================== +# Objects in triggers and stored functions must not be visible for hints +create table t3 (a int, b int); +# Trigger with derived table inside +create trigger tr1 before insert on t3 +for each row +begin +set new.b = (select max(a) from +(select a from t1 where a < new.a) dt); +end| +# Warning expected +explain extended select /*+ no_index(t1@dt) */ max(a) from t3 where a<2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 select max(NULL) AS `max(a)` from `test`.`t3` where 0 +# Stored function with derived table inside +create function get_max(p_a int) returns int +return (select max(a) from +(select a from t1 where a < p_a) dt); +# Warning expected +explain extended select /*+ no_index(t1@dt) */ a, get_max(a) from t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 select `test`.`t1`.`a` AS `a`,`get_max`(`test`.`t1`.`a`) AS `get_max(a)` from `test`.`t1` +drop function get_max; +drop table t1, t2, t3; +set optimizer_switch = 'derived_merge=off'; +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); +insert into t1 select seq, seq, 'filler' from seq_1_to_100; +create table t2 as select * from t1; +analyze table t1,t2 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +# Table-level hint +explain extended select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t2`@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +# More than one reference to a single QB +explain extended select /*+ no_bnl(t2@dt) no_index(t1@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t2`@`dt`) NO_INDEX(`t1`@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +# QB-level hint +explain extended select /*+ no_bnl(@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +# Index-level hints +# Without the hint 'range' index access would be chosen +explain extended select /*+ no_index(t1@`T`)*/ * from +(select * from t1 where a < 3) t; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 2.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`T`) */ `t`.`a` AS `a`,`t`.`b` AS `b`,`t`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`T`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3) `t` +# Without the hint 'range' index access would be chosen +explain extended select /*+ no_range_optimization(t1@t1)*/ * from +(select * from t1 where a > 100 and a < 120) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 1.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_RANGE_OPTIMIZATION(`t1`@`t1`) */ `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`t1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 100 and `test`.`t1`.`a` < 120) `t1` +# Regular and derived tables share same name but the hint is applied correctly +explain extended select /*+ index(t1@t1 idx_ab)*/ * from +(select * from t1 where a < 3) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 /* select#1 */ select /*+ INDEX(`t1`@`t1` `idx_ab`) */ `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`t1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3) `t1` +explain extended select /*+ no_index(t1@t2 idx_a) index(t1@t1 idx_ab)*/ * from +(select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_ab idx_ab 5 NULL 3 100.00 Using index condition +2 DERIVED t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`t2` `idx_a`) INDEX(`t1`@`t1` `idx_ab`) */ `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c`,`t2`.`a` AS `a`,`t2`.`b` AS `b`,`t2`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`t1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3) `t1` join (/* select#3 */ select /*+ QB_NAME(`t2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `t2` +explain extended select /*+ no_index(t1@t1 idx_a, idx_ab)*/ * from +(select * from t1 where a < 3) as t1, (select * from t1 where a < 5) as t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +2 DERIVED t1 ALL NULL NULL NULL NULL 100 2.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`t1` `idx_a`,`idx_ab`) */ `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c`,`t2`.`a` AS `a`,`t2`.`b` AS `b`,`t2`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`t1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3) `t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `t2` +# Nested derived tables +explain extended select /*+ no_bnl(t1@dt2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`dt2`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`) `dt2` +explain extended select /*+ no_index(t1@DT2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`DT2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`DT2`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`) `dt2` +# Explicit QB name overrides the implicit one +explain extended select /*+ no_index(t1@dt2)*/ * from +(select count(*) from t1, (select /*+ qb_name(dt2)*/ * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`) `dt2` +# Both hints are applied +explain extended select /*+ no_index(t1@dt1) no_bnl(t1@dt2)*/ * from +(select count(*) from t1, (select * from t1 where a < 5) dt1) as dt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +3 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt1`) NO_BNL(`t1`@`dt2`) */ `dt2`.`count(*)` AS `count(*)` from (/* select#2 */ select /*+ QB_NAME(`dt2`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`) `dt2` +# Nested derived tables with ambiguous names, hint is ignored +explain extended select /*+ no_index(t1@t1)*/* from +(select count(*) from t1, (select * from t1 where a < 5) t1) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Warning 4256 Query block name `t1` is ambiguous for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`count(*)` AS `count(*)` from (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `t1`) `t1` +# The hint cannot be applied to a derived table with UNION +explain extended select /*+ no_index(t2@t1)*/* from +(select * from t1 where a < 3 union select * from t2 where a < 9) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +3 UNION t2 ALL NULL NULL NULL NULL 100 8.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `t1` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 union /* select#3 */ select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t2` where `test`.`t2`.`a` < 9) `t1` +explain extended select /*+ no_index(t1@t1)*/* from +(select * from t1 where a < 3 union select * from t2 where a < 9) as t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +3 UNION t2 ALL NULL NULL NULL NULL 100 8.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `t1` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 3 union /* select#3 */ select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t2` where `test`.`t2`.`a` < 9) `t1` +# Test INSERT..SELECT +explain extended insert into t2 select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 insert into `test`.`t2` /* select#1 */ select /*+ NO_BNL(`t2`@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +# Test MERGE and NO_MERGE hints +explain extended select /*+ merge(@dt)*/ * from +(select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +2 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ MERGE(@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +explain extended select /*+ no_merge(@dt)*/ * from +(select * from (select t1.* from t1, t2 where t1.a > t2.a) as dt1) as dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED ALL NULL NULL NULL NULL 10000 100.00 +3 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`dt`) */ `dt1`.`a` AS `a`,`dt1`.`b` AS `b`,`dt1`.`c` AS `c` from (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt1`) `dt` +# Multiple levels of nested derived tables, all hints are applied +explain extended +select /*+ no_merge(dv) no_bnl(t2@dt) */ * from ( +select /*+ no_merge(du) */ * from ( +select /*+ no_merge(dt) */ * from ( +select t1.* from t1, t2 where t1.a > t2.a +) dt +) du +) dv; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +2 DERIVED ALL NULL NULL NULL NULL 10000 100.00 +3 DERIVED ALL NULL NULL NULL NULL 10000 100.00 +4 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +4 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(`dt`@`select#3`) NO_MERGE(`du`@`select#2`) NO_MERGE(`dv`@`select#1`) NO_BNL(`t2`@`dt`) */ `dv`.`a` AS `a`,`dv`.`b` AS `b`,`dv`.`c` AS `c` from (/* select#2 */ select `du`.`a` AS `a`,`du`.`b` AS `b`,`du`.`c` AS `c` from (/* select#3 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#4 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt`) `du`) `dv` +# ====================================== +# Test CTEs +# By default BNL and index access to t1 are used. +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(t1@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_BNL(`t1`@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_BNL(@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte) no_index(t1@dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte`) NO_INDEX(`t1`@`dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@dt1 idx_a) no_bnl(@cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 300 100.00 +2 DERIVED ALL NULL NULL NULL NULL 3 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +3 DERIVED t1 range idx_ab idx_ab 5 NULL 3 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`dt1` `idx_a`) NO_BNL(@`cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Ambiguity: multiple occurencies of `cte`, the hint is ignored +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_bnl(@cte)*/ * from cte where cnt > 10 +union +select * from cte where cnt < 100; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using where +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +4 UNION ALL NULL NULL NULL NULL 200 100.00 Using where +5 DERIVED ALL NULL NULL NULL NULL 2 100.00 +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +6 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4256 Query block name `cte` is ambiguous for NO_BNL hint +Note 1003 with cte as (/* select#2 */ select count(0) AS `cnt` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1` having `cnt` > 10)/* select#1 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` > 10 union /* select#4 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` < 100 +# However, if CTE occurencies have different aliases, the hint can be applied +explain extended +with cte as (select count(*) as cnt from t1, (select * from t1 where a < 5) dt1) +select /*+ no_index(t1@cte1)*/ * from cte as cte1 where cnt > 10 +union +select * from cte where cnt < 100; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using where +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +4 UNION ALL NULL NULL NULL NULL 200 100.00 Using where +5 DERIVED ALL NULL NULL NULL NULL 2 100.00 +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +6 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`cte1`) */ count(0) AS `cnt` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1` having `cnt` > 10)/* select#1 */ select /*+ NO_INDEX(`t1`@`cte1`) */ `cte1`.`cnt` AS `cnt` from `cte` `cte1` where `cte1`.`cnt` > 10 union /* select#4 */ select `cte`.`cnt` AS `cnt` from `cte` where `cte`.`cnt` < 100 +# ====================================== +# Test views +create view v1 as select * from t1 where a < 100; +# Default execution plan +explain extended select * +from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 1 100.00 Using index condition +1 SIMPLE t1 ref idx_a,idx_ab idx_a 5 test.t1.a 1 100.00 +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` < 3 and `test`.`t1`.`a` < 100 and `test`.`t1`.`a` < 100 +explain extended +select /*+ index(t1@v1 idx_ab) no_index(t1@`v2`)*/ * +from v1, v1 as v2 where v1.a = v2.a and v1.a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 2 100.00 Using index condition +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 99.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ INDEX(`t1`@`v1` `idx_ab`) NO_INDEX(`t1`@`v2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` = `test`.`t1`.`a` and `test`.`t1`.`a` < 3 and `test`.`t1`.`a` < 100 and `test`.`t1`.`a` < 100 +# Nested views +create view v2 as select * from v1 where a < 300; +# Default execution plan +explain extended select * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL idx_a,idx_ab NULL NULL NULL 100 94.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 300 and `test`.`t1`.`a` < 100 +# Addressing an object inside a nested view +explain extended select /*+ index(t1@`v1` idx_ab)*/ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 99 100.00 Using index condition +Warnings: +Note 1003 select /*+ INDEX(`t1`@`v1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 300 and `test`.`t1`.`a` < 100 +create view v3 as select * from t1 union select * from t2; +# Unable to apply the hint to a view with UNION +explain extended select /*+ no_index(t1@v3) */ * from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +3 UNION t2 ALL NULL NULL NULL NULL 100 100.00 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4257 Implicit query block name `v3` is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for NO_INDEX hint +Note 1003 /* select#1 */ select `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Ambiguity: view `v1` appears two times - should warn and ignore hint +explain extended select /*+ index(t2@v1) */ * from v1, +(select a from v1 where b > 5) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL idx_a,idx_ab NULL NULL NULL 100 94.00 Using where +1 PRIMARY ALL NULL NULL NULL NULL 99 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_ab 5 NULL 99 90.20 Using where; Using index +Warnings: +Warning 4256 Query block name `v1` is ambiguous for INDEX hint +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`dt`.`a` AS `a` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`b` > 5 and `test`.`t1`.`a` < 100) `dt` where `test`.`t1`.`a` < 100 +# Implicit QB names are not supported inside views +create view v4 as select /*+ no_bnl(t2@dt)*/ * from +(select t1.* from t1, t2 where t1.a > t2.a) as dt; +Warnings: +Warning 4242 Implicit query block names are ignored for hints specified within VIEWs +show create view v4; +View Create View character_set_client collation_connection +v4 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v4` AS select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c` from (`t1` join `t2`) where `t1`.`a` > `t2`.`a`) `dt` latin1 latin1_swedish_ci +# However, a derived table inside a view can be addressed from outer query +create view v5 as select dt.a from +t1, (select t1.* from t1, t2 where t1.a > t2.a) as dt where t1.a=dt.a; +# Addressing a single table +explain extended select /*+ no_bnl(t2@dt) */* from v5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index idx_a,idx_ab idx_a 5 NULL 100 100.00 Using where; Using index +1 PRIMARY ref key0 key0 5 test.t1.a 100 100.00 +3 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t2`@`dt`) */ `dt`.`a` AS `a` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` where `dt`.`a` = `test`.`t1`.`a` +# Addressing the whole derived table +explain extended select /*+ no_bnl(@dt) */* from v5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index idx_a,idx_ab idx_a 5 NULL 100 100.00 Using where; Using index +1 PRIMARY ref key0 key0 5 test.t1.a 100 100.00 +3 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(@`dt`) */ `dt`.`a` AS `a` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` where `dt`.`a` = `test`.`t1`.`a` +# Derived tables inside views can be addressed by their aliases +explain extended select /*+ no_bnl(t2@dt) */ * from v4; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 10000 100.00 +3 DERIVED t1 ALL idx_a,idx_ab NULL NULL NULL 100 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 100 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_BNL(`t2`@`dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#3 */ select /*+ QB_NAME(`dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` > `test`.`t2`.`a`) `dt` +drop view v1, v2, v3, v4, v5; +# ====================================== +# Not supported for DML, check presence of warnings +explain extended +update /*+ no_range_optimization(t1@dt)*/ t2, +(select a from t1 where a > 10) dt +set b=1 where t2.a = dt.a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 100 100.00 Using where +1 PRIMARY ref key0 key0 5 test.t2.a 9 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_ab 5 NULL 92 100.00 Using where; Using index +Warnings: +Warning 4220 Query block name `dt` is not found for NO_RANGE_OPTIMIZATION hint +Note 1003 /* select#1 */ update `test`.`t2` join (/* select#2 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 10) `dt` set `test`.`t2`.`b` = 1 where `dt`.`a` = `test`.`t2`.`a` +explain extended +delete /*+ no_index(t1@dt)*/ from t2 +where t2.a in +(select * from (select a from t1 where a > 10) dt where dt.a > 20); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 100 80.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 5 test.t2.a 1 100.00 +3 DERIVED t1 range idx_a,idx_ab idx_ab 5 NULL 84 100.00 Using where; Using index +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 /* select#1 */ delete from `test`.`t2` using (/* select#3 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 10 and `test`.`t1`.`a` > 20) `dt` where `dt`.`a` = `test`.`t2`.`a` and `test`.`t2`.`a` > 20 +# ====================================== +# Objects in triggers and stored functions must not be visible for hints +create table t3 (a int, b int); +# Trigger with derived table inside +create trigger tr1 before insert on t3 +for each row +begin +set new.b = (select max(a) from +(select a from t1 where a < new.a) dt); +end| +# Warning expected +explain extended select /*+ no_index(t1@dt) */ max(a) from t3 where a<2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 select max(NULL) AS `max(a)` from `test`.`t3` where 0 +# Stored function with derived table inside +create function get_max(p_a int) returns int +return (select max(a) from +(select a from t1 where a < p_a) dt); +# Warning expected +explain extended select /*+ no_index(t1@dt) */ a, get_max(a) from t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Warning 4220 Query block name `dt` is not found for NO_INDEX hint +Note 1003 select `test`.`t1`.`a` AS `a`,`get_max`(`test`.`t1`.`a`) AS `get_max(a)` from `test`.`t1` +drop function get_max; +drop table t1, t2, t3; +set optimizer_switch = default; diff --git a/mysql-test/main/opt_hints_impl_qb_name.test b/mysql-test/main/opt_hints_impl_qb_name.test new file mode 100644 index 0000000000000..0c4c427380300 --- /dev/null +++ b/mysql-test/main/opt_hints_impl_qb_name.test @@ -0,0 +1,9 @@ +set optimizer_switch = 'derived_merge=on'; + +--source opt_hints_impl_qb_name.inc + +set optimizer_switch = 'derived_merge=off'; + +--source opt_hints_impl_qb_name.inc + +set optimizer_switch = default; diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc index 230afd47d7d95..becd69c94ac56 100644 --- a/sql/opt_hints.cc +++ b/sql/opt_hints.cc @@ -15,10 +15,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "my_global.h" +#include "opt_hints_structs.h" #include "sql_class.h" #include "sql_lex.h" #include "sql_select.h" #include "opt_hints.h" +#include "opt_trace.h" /** Information about hints. Must be in sync with opt_hints_enum. @@ -46,15 +48,15 @@ struct st_opt_hint_info opt_hint_info[]= {{STRING_WITH_LEN("JOIN_SUFFIX")}, false, true, true}, {{STRING_WITH_LEN("JOIN_ORDER")}, false, true, true}, {{STRING_WITH_LEN("JOIN_FIXED_ORDER")}, false, true, false}, - {{STRING_WITH_LEN("DERIVED_CONDITION_PUSHDOWN")}, false, false, false}, - {{STRING_WITH_LEN("MERGE")}, true, false, false}, - {{STRING_WITH_LEN("SPLIT_MATERIALIZED")}, false, false, false}, + {{STRING_WITH_LEN("DERIVED_CONDITION_PUSHDOWN")}, false, false, false}, + {{STRING_WITH_LEN("MERGE")}, true, false, false}, + {{STRING_WITH_LEN("SPLIT_MATERIALIZED")}, false, false, false}, {{STRING_WITH_LEN("INDEX")}, false, true, false}, {{STRING_WITH_LEN("JOIN_INDEX")}, false, true, false}, {{STRING_WITH_LEN("GROUP_INDEX")}, false, true, false}, {{STRING_WITH_LEN("ORDER_INDEX")}, false, true, false}, {{STRING_WITH_LEN("ROWID_FILTER")}, false, true, false}, - {{STRING_WITH_LEN("INDEX_MERGE")}, false, false, false}, + {{STRING_WITH_LEN("INDEX_MERGE")}, false, false, false}, {null_clex_str, 0, 0, 0} }; @@ -84,7 +86,6 @@ int cmp_lex_string(const LEX_CSTRING &s, const LEX_CSTRING &t, (const uchar*)t.str, t.length); } - /* This is a version of push_warning_printf() guaranteeing no escalation of the warning to the level of error @@ -137,7 +138,9 @@ void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type, str.append(opt_hint_info[hint_type].hint_type); /* ER_WARN_UNKNOWN_QB_NAME with two arguments */ - if (err_code == ER_WARN_UNKNOWN_QB_NAME) + if (err_code == ER_WARN_UNKNOWN_QB_NAME || + err_code == ER_WARN_AMBIGUOUS_QB_NAME || + err_code == ER_WARN_IMPLICIT_QB_NAME_FOR_UNION) { String qb_name_str; append_identifier(thd, &qb_name_str, qb_name_arg->str, qb_name_arg->length); @@ -287,6 +290,95 @@ static Opt_hints_qb *find_hints_by_select_number(Parse_context *pc, return qb; } +/** + Helper function to find_qb_hints whereby it matches a qb_name to + an alias of a derived table, view or CTE used in the current query. + For example, for query + `select * from (select ... from t1 ...) as DT` + and given qb_name "DT", this function will return the query block + corresponding to the derived table DT, just like if the name was specified + explicitly using the QB_NAME hint: + `select * from (select / *+ QB_NAME(DT) * / ... from t1 ...) as DT` + + For query + `select * from v1, v1 as v2 where v1.a = v2.a and v1.a < 3` + and given qb_name "v2", this function will return the query block + corresponding to the view v2. + + For query + `with lda as (select ... from t1 ...) + select * from lda` + and given qb_name "lda", this function will return the query block + corresponding to the CTE "lda", just like if the name was specified + explicitly using the QB_NAME hint: + `with lda as (select / *+ QB_NAME(lda) * / ... from t1 ...) + select * from lda` + + Note: Implicit QB names are only supported for SELECTs. DML operations + (UPDATE, DELETE, INSERT) only support explicit QB names. This is caused by + the fact that optimizer hints are resolved before `open_tables()` + at `Sql_cmd_dml::prepare()`. + + @return the pair: - result code + - matching query block hints object, if it exists, + and NULL otherwise + */ + +enum class implicit_qb_result +{ + OK, // Found exactly one match, success + // Failure statuses: + NOT_FOUND, // No matches found + AMBIGUOUS, // More than one alias matches qb_name in the current query + UNION // DT/view/CTE has UNION/EXCEPT/INTERSECT inside + // (i.e., multiple query blocks), so the hint cannot be resolved + // unambiguously +}; + +static std::pair +find_hints_by_implicit_qb_name(Parse_context *pc, const Lex_ident_sys &qb_name) +{ + Opt_hints_qb *qb= nullptr; + + // Traverse the global table list to find all derived tables and views + for (TABLE_LIST *tbl= pc->thd->lex->query_tables; tbl; tbl= tbl->next_global) + { + // Skip if neither a derived table nor a view + if (tbl->is_non_derived()) + continue; + + // Check if the alias equals the implicit QB name + if (cmp_lex_string(tbl->alias, qb_name, system_charset_info)) + continue; // not a match, continue to next table + + /* + If `qb` was already set before, this means there are multiple tables with + same alias in the query. The name cannot be resolved unambiguously. + */ + if (qb) + return {implicit_qb_result::AMBIGUOUS, nullptr}; + + SELECT_LEX *child_select= tbl->get_unit()->first_select(); + /* + Check if the derived table/view does not contain UNION, because + implicit QB names for UNIONs are ambiguous - we do not know which SELECT + should the hint be applied to. So we only support implicit names + for single-SELECT derived tables/views. + */ + if (child_select->next_select()) + return {implicit_qb_result::UNION, nullptr}; + + Parse_context child_ctx(pc, child_select); + /* + qb should be set only this one time. Ambiguous references to + the same query block will fail on subsequent iterations (see above). + */ + qb= get_qb_hints(&child_ctx); + } + + return {(qb ? implicit_qb_result::OK : implicit_qb_result::NOT_FOUND), qb}; +} + /** Find existing Opt_hints_qb object, print warning @@ -316,13 +408,37 @@ Opt_hints_qb *find_qb_hints(Parse_context *pc, if (qb_by_name == nullptr) qb_by_number= find_hints_by_select_number(pc, qb_name); + Opt_hints_qb *qb_by_implicit_name= nullptr; + if (qb_by_name == nullptr && qb_by_number == nullptr) + { + std::pair find_res= + find_hints_by_implicit_qb_name(pc, qb_name); + if (find_res.first == implicit_qb_result::AMBIGUOUS) + { + // Warn on ambiguous derived table name + print_warn(pc->thd, ER_WARN_AMBIGUOUS_QB_NAME, + hint_type, hint_state, &qb_name, + nullptr, nullptr, nullptr); + return nullptr; + } + if (find_res.first == implicit_qb_result::UNION) + { + print_warn(pc->thd, ER_WARN_IMPLICIT_QB_NAME_FOR_UNION, + hint_type, hint_state, &qb_name, + nullptr, nullptr, nullptr); + return nullptr; + } + qb_by_implicit_name= find_res.second; + } + // C++-style comment here, otherwise compiler warns of /* within comment. // We don't allow implicit query block names to be specified for hints local // to a view (e.g. CREATE VIEW v1 AS SELECT /*+ NO_ICP(@`select#2` t1) ... // because of select numbering issues. When we're ready to fix that, then we // can remove this gate. + // Implicit QB naming using DT/view aliases is also not supported inside views if (pc->thd->lex->sql_command == SQLCOM_CREATE_VIEW && - qb_by_number) + (qb_by_number || qb_by_implicit_name)) { print_warn(pc->thd, ER_WARN_NO_IMPLICIT_QB_NAMES_IN_VIEW, hint_type, hint_state, &qb_name, @@ -330,10 +446,21 @@ Opt_hints_qb *find_qb_hints(Parse_context *pc, return nullptr; } - Opt_hints_qb *qb= qb_by_name ? qb_by_name : qb_by_number; + Opt_hints_qb *qb= qb_by_name ? qb_by_name : + (qb_by_number ? qb_by_number : qb_by_implicit_name); if (qb == nullptr) + { print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state, &qb_name, NULL, NULL, NULL); + } + else if (qb == qb_by_implicit_name && qb->get_name().length == 0) + { + /* + If the block is addressed by an implicit name, assign a name to the block, + so it is properly printed in warnings + */ + qb->set_name(qb_name); + } return qb; } @@ -1656,7 +1783,7 @@ bool is_compound_hint(opt_hints_enum type_arg) /* @brief Perform "Hint Resolution" for Optimizer Hints (see opt_hints.h for - definition) + definition). @detail Hints use "Explain select numbering", so this must be called after the @@ -1665,16 +1792,13 @@ bool is_compound_hint(opt_hints_enum type_arg) On the other hand, this must be called before the first attempt to check any hint. */ - void LEX::resolve_optimizer_hints() { + if (selects_for_hint_resolution.is_empty()) + return; + Query_arena *arena, backup; arena= thd->activate_stmt_arena_if_needed(&backup); - SCOPE_EXIT([&] () mutable { - selects_for_hint_resolution.empty(); - if (arena) - thd->restore_active_arena(arena, &backup); - }); List_iterator it(selects_for_hint_resolution); SELECT_LEX *sel; @@ -1685,8 +1809,13 @@ void LEX::resolve_optimizer_hints() Parse_context pc(thd, sel); sel->parsed_optimizer_hints->resolve(&pc); } + + selects_for_hint_resolution.empty(); + if (arena) + thd->restore_active_arena(arena, &backup); } + #ifndef DBUG_OFF static char dbug_print_hint_buf[64]; diff --git a/sql/opt_hints.h b/sql/opt_hints.h index c0973f48ace72..d5b63be5c4378 100644 --- a/sql/opt_hints.h +++ b/sql/opt_hints.h @@ -113,6 +113,7 @@ #include #include "my_config.h" +#include "opt_hints_structs.h" #include "sql_alloc.h" #include "sql_list.h" #include "mem_root_array.h" diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc index 743c8339f0785..4161dfb21f143 100644 --- a/sql/opt_hints_parser.cc +++ b/sql/opt_hints_parser.cc @@ -53,12 +53,14 @@ void append_table_name(THD *thd, String *str, const LEX_CSTRING &table_name, static const Lex_ident_sys null_ident_sys; + Parse_context::Parse_context(THD *thd, st_select_lex *select) : thd(thd), mem_root(thd->mem_root), select(select) {} + Parse_context::Parse_context(Parse_context *pc, st_select_lex *select) : thd(pc->thd), mem_root(pc->mem_root), @@ -1283,6 +1285,20 @@ bool Parser::Hint_list::resolve(Parse_context *pc) const if (!get_qb_hints(pc)) return true; + /* + QB_NAME hints are resolved first so following hints can be attached to + the pre-configured query blocks + */ + for (Hint_list::iterator li= this->begin(); li != this->end(); ++li) + { + Parser::Hint &hint= *li; + if (const Qb_name_hint &qb_hint= hint) + { + if (qb_hint.resolve(pc)) + return true; + } + } + for (Hint_list::iterator li= this->begin(); li != this->end(); ++li) { Parser::Hint &hint= *li; @@ -1296,11 +1312,6 @@ bool Parser::Hint_list::resolve(Parse_context *pc) const if (index_hint.resolve(pc)) return true; } - else if (const Qb_name_hint &qb_hint= hint) - { - if (qb_hint.resolve(pc)) - return true; - } else if (const Max_execution_time_hint &max_hint= hint) { if (max_hint.resolve(pc)) @@ -1321,6 +1332,11 @@ bool Parser::Hint_list::resolve(Parse_context *pc) const if (join_order_hint.resolve(pc)) return true; } + else if (const Qb_name_hint &qb_hint __attribute__((unused)) = hint) + { + // QB_NAME hints have been resolved earlier + continue; + } else { DBUG_ASSERT(0); } diff --git a/sql/opt_hints_parser.h b/sql/opt_hints_parser.h index 272982b8ae31d..996389100371d 100644 --- a/sql/opt_hints_parser.h +++ b/sql/opt_hints_parser.h @@ -19,6 +19,7 @@ */ #include "lex_ident_sys.h" +#include "opt_hints_structs.h" #include "simple_tokenizer.h" #include "sql_list.h" #include "sql_string.h" @@ -28,46 +29,14 @@ class st_select_lex; class Opt_hints_qb; -/** - Hint types, MAX_HINT_ENUM should be always last. - This enum should be synchronized with opt_hint_info - array(see opt_hints.cc). -*/ -enum opt_hints_enum -{ - BKA_HINT_ENUM= 0, - BNL_HINT_ENUM, - ICP_HINT_ENUM, - MRR_HINT_ENUM, - NO_RANGE_HINT_ENUM, - QB_NAME_HINT_ENUM, - MAX_EXEC_TIME_HINT_ENUM, - SEMIJOIN_HINT_ENUM, - SUBQUERY_HINT_ENUM, - JOIN_PREFIX_HINT_ENUM, - JOIN_SUFFIX_HINT_ENUM, - JOIN_ORDER_HINT_ENUM, - JOIN_FIXED_ORDER_HINT_ENUM, - DERIVED_CONDITION_PUSHDOWN_HINT_ENUM, - MERGE_HINT_ENUM, - SPLIT_MATERIALIZED_HINT_ENUM, - INDEX_HINT_ENUM, - JOIN_INDEX_HINT_ENUM, - GROUP_INDEX_HINT_ENUM, - ORDER_INDEX_HINT_ENUM, - ROWID_FILTER_HINT_ENUM, - INDEX_MERGE_HINT_ENUM, - MAX_HINT_ENUM // This one must be the last in the list -}; - - /** Environment data for the name resolution phase */ -struct Parse_context { - THD * const thd; ///< Current thread handler - MEM_ROOT *mem_root; ///< Current MEM_ROOT - st_select_lex * select; ///< Current SELECT_LEX object +struct Parse_context +{ + THD * const thd; + MEM_ROOT *mem_root; + st_select_lex *select; Parse_context(THD *thd, st_select_lex *select); Parse_context(Parse_context *pc, st_select_lex *select); diff --git a/sql/opt_hints_structs.h b/sql/opt_hints_structs.h new file mode 100644 index 0000000000000..a4835e3982486 --- /dev/null +++ b/sql/opt_hints_structs.h @@ -0,0 +1,53 @@ +#ifndef OPT_HINTS_STRUCTS_H +#define OPT_HINTS_STRUCTS_H +/* + Copyright (c) 2026, MariaDB + + 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 +*/ + +/** + Hint types, MAX_HINT_ENUM should be always last. + This enum should be synchronized with opt_hint_info + array(see opt_hints.cc). +*/ +enum opt_hints_enum +{ + BKA_HINT_ENUM= 0, + BNL_HINT_ENUM, + ICP_HINT_ENUM, + MRR_HINT_ENUM, + NO_RANGE_HINT_ENUM, + QB_NAME_HINT_ENUM, + MAX_EXEC_TIME_HINT_ENUM, + SEMIJOIN_HINT_ENUM, + SUBQUERY_HINT_ENUM, + JOIN_PREFIX_HINT_ENUM, + JOIN_SUFFIX_HINT_ENUM, + JOIN_ORDER_HINT_ENUM, + JOIN_FIXED_ORDER_HINT_ENUM, + DERIVED_CONDITION_PUSHDOWN_HINT_ENUM, + MERGE_HINT_ENUM, + SPLIT_MATERIALIZED_HINT_ENUM, + INDEX_HINT_ENUM, + JOIN_INDEX_HINT_ENUM, + GROUP_INDEX_HINT_ENUM, + ORDER_INDEX_HINT_ENUM, + ROWID_FILTER_HINT_ENUM, + INDEX_MERGE_HINT_ENUM, + MAX_HINT_ENUM // This one must be the last in the list +}; + +#endif /* OPT_HINTS_STRUCTS_H */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 272002ffdc273..ad79a80cc11de 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12388,3 +12388,7 @@ ER_SLAVE_INCOMPATIBLE_TABLE_DEF eng "Table structure for binlog event is not compatible with the table definition on this slave: %s" ER_UNSUPPORTED_DATA_TYPE_ATTRIBUTE eng "Data type '%-.64s' doesn't support %s attribute." +ER_WARN_AMBIGUOUS_QB_NAME + eng "Query block name %s is ambiguous for %s hint" +ER_WARN_IMPLICIT_QB_NAME_FOR_UNION + eng "Implicit query block name %s is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for %s hint" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index eaaa6b21c6250..8ac717a892b0c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5816,6 +5816,8 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, if ((dt_phases & DT_INIT) && mysql_handle_derived(thd->lex, DT_INIT)) goto end; + thd->lex->resolve_optimizer_hints(); + // Process all phases remaining after DT_INIT if (mysql_handle_derived(thd->lex, dt_phases & ~DT_INIT)) goto end; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1fce0eb6014c2..92b9da85d8ac5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3539,7 +3539,6 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) */ lex->first_lists_tables_same(); lex->fix_first_select_number(); - lex->resolve_optimizer_hints(); /* should be assigned after making first tables same */ all_tables= lex->query_tables; /* set context for commands which do not use setup_tables */ @@ -4674,6 +4673,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) select_lex->table_list.first= second_table; select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; + lex->resolve_optimizer_hints(); res= mysql_insert_select_prepare(thd, result); Write_record write; if (!res && @@ -6129,6 +6129,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) { + lex->resolve_optimizer_hints(); if (lex->describe) { /* diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c572cb3d79970..7bab50950309d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1564,6 +1564,7 @@ static int mysql_test_select(Prepared_statement *stmt, DT_INIT | DT_PREPARE)) goto error; + lex->resolve_optimizer_hints(); thd->lex->used_tables= 0; // Updated by setup_fields /* @@ -2304,7 +2305,6 @@ static bool check_prepared_statement(Prepared_statement *stmt) lex->first_lists_tables_same(); lex->fix_first_select_number(); - lex->resolve_optimizer_hints(); tables= lex->query_tables; /* set context for commands which do not use setup_tables */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b8fbbe2f62f58..2bc18527cef10 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -34748,7 +34748,7 @@ bool Sql_cmd_dml::prepare(THD *thd) MYSQL_DML_START(thd); lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; - + lex->resolve_optimizer_hints(); if (open_tables_for_query(thd, lex->query_tables, &table_count, 0, get_dml_prelocking_strategy())) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2b4aa9ce702c7..29134e736055e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1375,6 +1375,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) goto exit; + thd->lex->resolve_optimizer_hints(); if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 450a1b3a9756b..7b88a41323f10 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -559,6 +559,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, /* prepare select to resolve all fields */ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; + lex->resolve_optimizer_hints(); if (unit->prepare(unit->derived, 0, 0)) { /* From f2d2e62feed1a7bdde7faf20fd176b8967960d97 Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Thu, 26 Feb 2026 16:08:44 +0700 Subject: [PATCH 3/3] MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks Extended QB_NAME hint to support path-based addressing of query blocks nested within views, derived tables, and CTEs, following TiDB's syntax. New syntax: QB_NAME(name, query_block_path), where query_block_path ::= query_block_path_element [ {. query_block_path_element }... ] query_block_path_element ::= @ qb_path_element_select_num | qb_path_element_view_sel qb_path_element_view_sel ::= qb_path_element_view_name [ @ qb_path_element_select_num ] For example, `SELECT /*+ qb_name(qb_v1, v1) */* FROM v1` The name `qb_v1` is assigned to the inner query block of the view `v1`. `SELECT /*+ qb_name(qb_v1, v1@sel_1) */* FROM v1` Means the same but specifies that `v1` is present in SELECT#1 of the current query block. `SELECT /*+ qb_name(qb_v1, v1@sel_1 .@sel_2) */* FROM v1` This means SELECT#2 of view `v1`, which is present in SELECT#1 of the current query block, gets the name `qb_v1`. It is possible to specify not only view names but also derived tables and CTE's in the path. Views and derived tables may be nested on multiple levels, for example: `SELECT /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2) no_index(t1@dt2_dt1_v1_1)*/ v1.* FROM v1 JOIN (SELECT v1.* FROM v1 JOIN (SELECT * FROM v2) dt2) dt1` Limitations: - Only SELECT statements support QB names with path. DML operations (UPDATE, DELETE, INSERT) only support explicitly defined QB names --- mysql-test/main/opt_hints_qb_name_path.inc | 316 +++++ mysql-test/main/opt_hints_qb_name_path.result | 1144 +++++++++++++++++ mysql-test/main/opt_hints_qb_name_path.test | 11 + sql/opt_hints.cc | 7 + sql/opt_hints_parser.cc | 389 ++++-- sql/opt_hints_parser.h | 161 ++- sql/share/errmsg-utf8.txt | 6 + sql/simple_parser.h | 29 + 8 files changed, 1924 insertions(+), 139 deletions(-) create mode 100644 mysql-test/main/opt_hints_qb_name_path.inc create mode 100644 mysql-test/main/opt_hints_qb_name_path.result create mode 100644 mysql-test/main/opt_hints_qb_name_path.test diff --git a/mysql-test/main/opt_hints_qb_name_path.inc b/mysql-test/main/opt_hints_qb_name_path.inc new file mode 100644 index 0000000000000..811a17eb0ef54 --- /dev/null +++ b/mysql-test/main/opt_hints_qb_name_path.inc @@ -0,0 +1,316 @@ +--source include/have_sequence.inc +--enable_prepare_warnings + +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); + +insert into t1 select seq, seq, 'filler' from seq_1_to_100; + +create table t2 as select * from t1; + +analyze table t1,t2 persistent for all; + +create view v1 as select * from t1 where a < 10; + +create view v2 as + select * from t1 join /* Name of this query block is @SEL_1 */ + ( + select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ + ) tt; + +create view v3 as + select * from t1 where a < 10 union select * from t1 where a > 90; + +--echo # ====================================== +--echo # Views +--echo # +--echo # select /* The name of the current query block is @SEL_1 */ * from v1; +--echo # +--echo # Addressing a view having one query block. +--echo # QB_NAME(qb_v1, v1) means: inner query block of the view `v1`, +--echo # which is present in the same query block as the hint, gets the name `qb_v1`. +--echo # This name can be used in other hints. +explain extended + select /*+ qb_name(qb_v1, v1) no_index(t1@qb_v1)*/* from v1; + +--echo # Equivalent to the above but specifying @SEL_1 explicitly. +--echo # QB_NAME(qb_v1, v1@sel_1) means: inner query block of view `v1`, +--echo # which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended + select /*+ qb_name(qb_v1, v1@sel_1) no_index(t1@qb_v1 idx_a)*/* from v1; + +--echo # Equivalent to the above but also specifying @SEL_1 of the view. +--echo # QB_NAME(qb_v1, v1@sel_1 .@sel_1) means: SELECT#1 of view `v1`, +--echo # which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended + select /*+ qb_name(qb_v1, v1@sel_1 .@sel_1) no_index(t1@qb_v1 idx_ab)*/* from v1; + +--echo # +--echo # The case when a particular view is used in more than one query block. +--echo # select /* Name of current query block is @SEL_1 */ * from v1 +--echo # join +--echo # (select /* Name of current query block is @SEL_2 */ * from v1) vvv1; +--echo # The first query block of view v1 can be declared as +--echo # QB_NAME(v1_1, v1@SEL_1 .@SEL_1), +--echo # and the second query block of the view v1 can be declared as +--echo # QB_NAME(v1_2, v1@SEL_1 .@SEL_2). +--echo # +--echo # By default, range access is used for both `t1`'s in the statement below. +explain extended + select * from v1 join (select * from v1) vvv1; + +--echo # Disable index access for t1 from the second occurence of view v1: +explain extended + select /*+ qb_name(v1_2, v1@SEL_2 .@SEL_1) no_index(t1@v1_2)*/ * + from v1 join (select * from v1) vvv1; + +--echo # Disable index access for t1 from both occurences of view v1: +explain extended + select /*+ qb_name(v1_1, v1@SEL_1) qb_name(v1_2, v1@SEL_2 .@SEL_1) + no_index(t1@v1_1) no_index(t1@v1_2)*/ * + from v1 join (select * from v1) vvv1; + +--echo # +--echo # The case when a particular view has more than one query block. +--echo # create view v2 as +--echo # select * from t1 join /* Name of this query block is @SEL_1 */ +--echo # ( +--echo # select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ +--echo # ) tt; +--echo # The first query block of view v2 can be declared as +--echo # QB_NAME(v2_1, v2@SEL_1 .@SEL_1), and the second query block can be +--echo # declared as QB_NAME(v2_2, v2@SEL_1 .@SEL_2). +--echo # +--echo # See the default execution plan: +explain extended select * from v2; + +--echo # Disable index access for t1 from the second query block of view v2: +explain extended + select /*+ qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2)*/* from v2; + +--echo # Disable index access for `t1` from view `v1` used in +--echo # the first query block of view `v2`: +explain extended + select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1)*/* from v2; + +--echo # Equivalent to the above but specifying @SEL_1 explicitly: +explain extended + select /*+ qb_name(v2_v1_sel1, v2@SEL_1 .v1@SEL_2 .@SEL_1) + no_index(t1@v2_v1_sel1)*/ * from v2; + +--echo # Disable index access for `t1` tables from views `v1` and `v2` +explain extended + select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1) + qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2) */ * from v2; + +--echo # ====================================== +--echo # Views with UNION +--echo # +--echo # Default execution plan: +explain extended select * from v3; + +--echo # `v3` in QB path corresponds to @SEL_1: +explain extended select /*+ qb_name(qb_v3, v3) no_index(t1@qb_v3)*/* from v3; + +--echo # Addressing @SEL_1 of `v3` explicitly: +explain extended + select /*+ qb_name(qb_v3_sel1, v3.@sel_1) no_index(t1@qb_v3_sel1)*/* from v3; + +--echo # Addressing @SEL_2 of `v3` explicitly: +explain extended + select /*+ qb_name(qb_v3_sel2, v3.@sel_2) no_index(t1@qb_v3_sel2)*/* from v3; + + +--echo # ====================================== +--echo # Derived tables +--echo # +--echo # QB_NAME(qb_dt, dt) means: inner query block of derived table `dt`, +--echo # which is present in the same query block as the hint, gets the name `qb_dt`. +--echo # This name can be used in other hints. +explain extended select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from + (select * from t1 where a < 10) dt; + +--echo # QB_NAME(qb_dt, dt@sel_1) means: inner query block of derived table `dt`, +--echo # which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1) no_index(t1@qb_dt)*/* from + (select * from t1 where a < 10) dt; + +--echo # QB_NAME(qb_dt, dt@sel_1 .@sel_1) means: SELECT#1 of derived table `dt`, +--echo # which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1 .@sel_1) no_index(t1@qb_dt)*/* from + (select * from t1 where a < 10) dt; + +--echo # QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +--echo # NO_MERGE(@qb4) forbids merging of any derived tables of this block. +--echo # There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) NO_MERGE(@qb4) */ a from ( +select a from ( + select a from ( + select a from ( + select a from t1) dt + ) du + ) dv +) dw; + +--echo # QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +--echo # MERGE(@qb4) allows merging of any derived tables of this block. +--echo # There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) MERGE(@qb4) */ a from ( +select a from ( + select a from ( + select a from ( + select a from t1) dt + ) du + ) dv +) dw; + +--echo # ====================================== +--echo # Derived tables with UNION +--echo # +--echo # Default execution plan: +explain extended +select * from (select * from t1 where a < 10 union select * from t1 where a > 90) dt; + +--echo # `dt` in QB path corresponds to @SEL_1: +explain extended +select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from + (select * from t1 where a < 10 union select * from t1 where a > 90) dt; + +--echo # Addressing @SEL_1 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel1, dt.@sel_1) no_index(t1@qb_dt_sel1)*/* from + (select * from t1 where a < 10 union select * from t1 where a > 90) dt; + +--echo # Addressing @SEL_2 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel2, dt.@sel_2) no_index(t1@qb_dt_sel2)*/* from + (select * from t1 where a < 10 union select * from t1 where a > 90) dt; + +--echo # ====================================== +--echo # Mix of views and derived tables +--echo # +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ * + from v1 join (select * from v1) dt1; + +--echo # More complicated query. Default execution plan: +explain extended +select v1.* from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; + +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ v1.* + from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; + +explain extended +select /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2) + no_index(t1@dt2_dt1_v1_1)*/ v1.* + from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; + +--echo # ====================================== +--echo # CTEs +--echo # +--echo # Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; + +--echo # Disable index access for t1 in CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte; + +--echo # Disable index access for t1 in dt1 of CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; + +--echo # Disable index access for both t1's +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte) + qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; + +--echo # Multiple references to a CTE in a query. +--echo # Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte join cte cte1; + +--echo # Disable index access for t1 in `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte join cte as cte1; + +--echo # Disable index access for t1 in `cte1` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte1, cte1) no_index(t1@qb_cte1)*/ * from cte join cte as cte1; + +--echo # ====================================== +--echo # Scalar context subquery +explain extended +select /*+ qb_name(inner, @sel_3) no_index(t1@inner) */ + (select (select max(a) from t1 where a < 10) + b) + from t1; + +--echo # ====================================== +--echo # Wrong paths generate warnings +--echo # +--disable_ps_protocol +# PS protocol is disabled because the warning is genererated only during +# PREPARE step of a statement. When the QB name cannot be resolved the hint +# is discarded, so it is not present during EXECUTE step. + +explain extended + select /*+ qb_name(`qb_v1`, `v2`)*/* from v1; + +--echo # Wrong view name `v2`, however `@sel_2` is correct as it addresses +--echo # the inner query block of `v1` +explain extended + select /*+ qb_name(qb_v1, `v2`@`sel_2`)*/* from v1; + +explain extended + select /*+ qb_name(qb_v1, v2@sel_1 .@sel_2)*/* from v1; + +--echo # Attempting to reference a regular table as a query block: +explain extended + select /*+ qb_name(qb_v1, dt .t1)*/* from (select t1.* from t1 join v1) dt; + +--echo # Wrong view name inside a derived table: +explain extended + select /*+ qb_name(qb_v1, dt .v2)*/* from (select t1.* from t1 join v1) dt; + +--echo # Wrong select number: +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_5)*/* from (select t1.* from t1 join v1) dt; + +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_1 .@sel_2)*/* from + (select t1.* from t1 join v1) dt; + +--echo # Wrong select number syntax: +explain extended + select /*+ qb_name(qb_v1, `v1`@`lex_2`)*/* from v1; + +explain extended + select /*+ qb_name(qb_v1, v1 .lex_2)*/* from v1; + +--echo # Select number is too large: +explain extended + select /*+ qb_name(qb_v1, `v1`@`SEL_9999999999`)*/* from v1; + +--echo # Exponential select numbers are not allowed: +explain extended + select /*+ qb_name(qb_v1, `v1`@`SEL_1e2`)*/* from v1; + +--echo # @SEL_N matches a SELECT in a sibling unit (warning expected). +--echo # v1 has only @SEL_1, v3 has @SEL_1 and @SEL_2 (UNION) +--echo # Navigate to v1, ask for @SEL_2 which matches v3's SELECT +explain extended + select /*+ qb_name(qb_wrong, v1 .@SEL_2) */ * from v1 join v3; + +--enable_ps_protocol + +drop table t1, t2; +drop view v1, v2, v3; \ No newline at end of file diff --git a/mysql-test/main/opt_hints_qb_name_path.result b/mysql-test/main/opt_hints_qb_name_path.result new file mode 100644 index 0000000000000..d8e1f5edecb72 --- /dev/null +++ b/mysql-test/main/opt_hints_qb_name_path.result @@ -0,0 +1,1144 @@ + +================================================================ +set optimizer_switch= 'derived_merge=on'; +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); +insert into t1 select seq, seq, 'filler' from seq_1_to_100; +create table t2 as select * from t1; +analyze table t1,t2 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +create view v1 as select * from t1 where a < 10; +create view v2 as +select * from t1 join /* Name of this query block is @SEL_1 */ +( +select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ +) tt; +create view v3 as +select * from t1 where a < 10 union select * from t1 where a > 90; +# ====================================== +# Views +# +# select /* The name of the current query block is @SEL_1 */ * from v1; +# +# Addressing a view having one query block. +# QB_NAME(qb_v1, v1) means: inner query block of the view `v1`, +# which is present in the same query block as the hint, gets the name `qb_v1`. +# This name can be used in other hints. +explain extended +select /*+ qb_name(qb_v1, v1) no_index(t1@qb_v1)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Equivalent to the above but specifying @SEL_1 explicitly. +# QB_NAME(qb_v1, v1@sel_1) means: inner query block of view `v1`, +# which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended +select /*+ qb_name(qb_v1, v1@sel_1) no_index(t1@qb_v1 idx_a)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 7 100.00 Using index condition +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1` `idx_a`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Equivalent to the above but also specifying @SEL_1 of the view. +# QB_NAME(qb_v1, v1@sel_1 .@sel_1) means: SELECT#1 of view `v1`, +# which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended +select /*+ qb_name(qb_v1, v1@sel_1 .@sel_1) no_index(t1@qb_v1 idx_ab)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# +# The case when a particular view is used in more than one query block. +# select /* Name of current query block is @SEL_1 */ * from v1 +# join +# (select /* Name of current query block is @SEL_2 */ * from v1) vvv1; +# The first query block of view v1 can be declared as +# QB_NAME(v1_1, v1@SEL_1 .@SEL_1), +# and the second query block of the view v1 can be declared as +# QB_NAME(v1_2, v1@SEL_1 .@SEL_2). +# +# By default, range access is used for both `t1`'s in the statement below. +explain extended +select * from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +# Disable index access for t1 from the second occurence of view v1: +explain extended +select /*+ qb_name(v1_2, v1@SEL_2 .@SEL_1) no_index(t1@v1_2)*/ * +from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`v1_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +# Disable index access for t1 from both occurences of view v1: +explain extended +select /*+ qb_name(v1_1, v1@SEL_1) qb_name(v1_2, v1@SEL_2 .@SEL_1) +no_index(t1@v1_1) no_index(t1@v1_2)*/ * +from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`v1_1`) NO_INDEX(`t1`@`v1_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +# +# The case when a particular view has more than one query block. +# create view v2 as +# select * from t1 join /* Name of this query block is @SEL_1 */ +# ( +# select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ +# ) tt; +# The first query block of view v2 can be declared as +# QB_NAME(v2_1, v2@SEL_1 .@SEL_1), and the second query block can be +# declared as QB_NAME(v2_2, v2@SEL_1 .@SEL_2). +# +# See the default execution plan: +explain extended select * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for t1 from the second query block of view v2: +explain extended +select /*+ qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2)*/* from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`v2_2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for `t1` from view `v1` used in +# the first query block of view `v2`: +explain extended +select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1)*/* from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Equivalent to the above but specifying @SEL_1 explicitly: +explain extended +select /*+ qb_name(v2_v1_sel1, v2@SEL_1 .v1@SEL_2 .@SEL_1) +no_index(t1@v2_v1_sel1)*/ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1_sel1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for `t1` tables from views `v1` and `v2` +explain extended +select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1) +qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2) */ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1`) NO_INDEX(`t1`@`v2_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`v2_2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# ====================================== +# Views with UNION +# +# Default execution plan: +explain extended select * from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# `v3` in QB path corresponds to @SEL_1: +explain extended select /*+ qb_name(qb_v3, v3) no_index(t1@qb_v3)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Addressing @SEL_1 of `v3` explicitly: +explain extended +select /*+ qb_name(qb_v3_sel1, v3.@sel_1) no_index(t1@qb_v3_sel1)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3_sel1`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Addressing @SEL_2 of `v3` explicitly: +explain extended +select /*+ qb_name(qb_v3_sel2, v3.@sel_2) no_index(t1@qb_v3_sel2)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 14 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 ALL NULL NULL NULL NULL 100 10.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3_sel2`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# ====================================== +# Derived tables +# +# QB_NAME(qb_dt, dt) means: inner query block of derived table `dt`, +# which is present in the same query block as the hint, gets the name `qb_dt`. +# This name can be used in other hints. +explain extended select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# QB_NAME(qb_dt, dt@sel_1) means: inner query block of derived table `dt`, +# which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# QB_NAME(qb_dt, dt@sel_1 .@sel_1) means: SELECT#1 of derived table `dt`, +# which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1 .@sel_1) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +# NO_MERGE(@qb4) forbids merging of any derived tables of this block. +# There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) NO_MERGE(@qb4) */ a from ( +select a from ( +select a from ( +select a from ( +select a from t1) dt +) du +) dv +) dw; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 100 100.00 +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(@`qb4`) */ `dt`.`a` AS `a` from (/* select#5 */ select `test`.`t1`.`a` AS `a` from `test`.`t1`) `dt` +# QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +# MERGE(@qb4) allows merging of any derived tables of this block. +# There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) MERGE(@qb4) */ a from ( +select a from ( +select a from ( +select a from ( +select a from t1) dt +) du +) dv +) dw; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 select /*+ MERGE(@`qb4`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# ====================================== +# Derived tables with UNION +# +# Default execution plan: +explain extended +select * from (select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# `dt` in QB path corresponds to @SEL_1: +explain extended +select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# Addressing @SEL_1 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel1, dt.@sel_1) no_index(t1@qb_dt_sel1)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt_sel1`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt_sel1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# Addressing @SEL_2 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel2, dt.@sel_2) no_index(t1@qb_dt_sel2)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 14 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 ALL NULL NULL NULL NULL 100 10.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt_sel2`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select /*+ QB_NAME(`qb_dt_sel2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# ====================================== +# Mix of views and derived tables +# +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ * +from v1 join (select * from v1) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +# More complicated query. Default execution plan: +explain extended +select v1.* from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index; Using join buffer (flat, BNL join) +1 PRIMARY t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (incremental, BNL join) +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (incremental, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` join `test`.`t1` join (/* select#7 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ v1.* +from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 9.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (incremental, BNL join) +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (incremental, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` join `test`.`t1` join (/* select#7 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2) +no_index(t1@dt2_dt1_v1_1)*/ v1.* +from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index; Using join buffer (flat, BNL join) +1 PRIMARY t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (incremental, BNL join) +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (incremental, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt2_dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` join `test`.`t1` join (/* select#7 */ select /*+ QB_NAME(`dt2_dt1_v1_1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` where `test`.`t1`.`a` < 10 and `test`.`t1`.`a` < 10 +# ====================================== +# CTEs +# +# Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for t1 in CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for t1 in dt1 of CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte_dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for both t1's +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte) +qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) NO_INDEX(`t1`@`qb_cte_dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Multiple references to a CTE in a query. +# Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte join cte cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +4 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# Disable index access for t1 in `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte join cte as cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +4 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) */ `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# Disable index access for t1 in `cte1` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte1, cte1) no_index(t1@qb_cte1)*/ * from cte join cte as cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +4 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using where; Using index +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 5)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte1`) */ `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# ====================================== +# Scalar context subquery +explain extended +select /*+ qb_name(inner, @sel_3) no_index(t1@inner) */ +(select (select max(a) from t1 where a < 10) + b) +from t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index NULL idx_ab 10 NULL 100 100.00 Using index +3 SUBQUERY t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #2 was resolved in SELECT #1 +Note 1249 Select 2 was reduced during optimization +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`inner`) */ (/* select#3 */ select /*+ QB_NAME(`inner`) */ max(`test`.`t1`.`a`) from `test`.`t1` where `test`.`t1`.`a` < 10) + `test`.`t1`.`b` AS `(select (select max(a) from t1 where a < 10) + b)` from `test`.`t1` +# ====================================== +# Wrong paths generate warnings +# +explain extended +select /*+ qb_name(`qb_v1`, `v2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Wrong view name `v2`, however `@sel_2` is correct as it addresses +# the inner query block of `v1` +explain extended +select /*+ qb_name(qb_v1, `v2`@`sel_2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`@`sel_2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(qb_v1, v2@sel_1 .@sel_2)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`@`sel_1` .@`sel_2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Attempting to reference a regular table as a query block: +explain extended +select /*+ qb_name(qb_v1, dt .t1)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `dt` .`t1`) is ignored. `t1` required at element #2 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 +# Wrong view name inside a derived table: +explain extended +select /*+ qb_name(qb_v1, dt .v2)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `dt` .`v2`) is ignored. `v2` required at element #2 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 +# Wrong select number: +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_5)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4259 Hint QB_NAME(`qb_v1`, `dt` .`v1`@`sel_5`) is ignored. SEL_5 required at element #2 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_1 .@sel_2)*/* from +(select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4259 Hint QB_NAME(`qb_v1`, `dt` .`v1`@`sel_1` .@`sel_2`) is ignored. SEL_2 required at element #3 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10 +# Wrong select number syntax: +explain extended +select /*+ qb_name(qb_v1, `v1`@`lex_2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`lex_2`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(qb_v1, v1 .lex_2)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v1` .`lex_2`) is ignored. `lex_2` required at element #2 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Select number is too large: +explain extended +select /*+ qb_name(qb_v1, `v1`@`SEL_9999999999`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`SEL_9999999999`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Exponential select numbers are not allowed: +explain extended +select /*+ qb_name(qb_v1, `v1`@`SEL_1e2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`SEL_1e2`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# @SEL_N matches a SELECT in a sibling unit (warning expected). +# v1 has only @SEL_1, v3 has @SEL_1 and @SEL_2 (UNION) +# Navigate to v1, ask for @SEL_2 which matches v3's SELECT +explain extended +select /*+ qb_name(qb_wrong, v1 .@SEL_2) */ * from v1 join v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +4 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4259 Hint QB_NAME(`qb_wrong`, `v1` .@`SEL_2`) is ignored. SEL_2 required at element #2 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`t1` join `test`.`v3` where `test`.`t1`.`a` < 10 +drop table t1, t2; +drop view v1, v2, v3; + +================================================================ +set optimizer_switch= 'derived_merge=off'; +create table t1 (a int, b int, c char(20), key idx_a(a), key idx_ab(a, b)); +insert into t1 select seq, seq, 'filler' from seq_1_to_100; +create table t2 as select * from t1; +analyze table t1,t2 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +create view v1 as select * from t1 where a < 10; +create view v2 as +select * from t1 join /* Name of this query block is @SEL_1 */ +( +select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ +) tt; +create view v3 as +select * from t1 where a < 10 union select * from t1 where a > 90; +# ====================================== +# Views +# +# select /* The name of the current query block is @SEL_1 */ * from v1; +# +# Addressing a view having one query block. +# QB_NAME(qb_v1, v1) means: inner query block of the view `v1`, +# which is present in the same query block as the hint, gets the name `qb_v1`. +# This name can be used in other hints. +explain extended +select /*+ qb_name(qb_v1, v1) no_index(t1@qb_v1)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Equivalent to the above but specifying @SEL_1 explicitly. +# QB_NAME(qb_v1, v1@sel_1) means: inner query block of view `v1`, +# which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended +select /*+ qb_name(qb_v1, v1@sel_1) no_index(t1@qb_v1 idx_a)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_ab idx_ab 5 NULL 7 100.00 Using index condition +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1` `idx_a`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Equivalent to the above but also specifying @SEL_1 of the view. +# QB_NAME(qb_v1, v1@sel_1 .@sel_1) means: SELECT#1 of view `v1`, +# which is present in SELECT#1 of the current query block, gets the name `qb_v1`. +explain extended +select /*+ qb_name(qb_v1, v1@sel_1 .@sel_1) no_index(t1@qb_v1 idx_ab)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`qb_v1` `idx_ab`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# +# The case when a particular view is used in more than one query block. +# select /* Name of current query block is @SEL_1 */ * from v1 +# join +# (select /* Name of current query block is @SEL_2 */ * from v1) vvv1; +# The first query block of view v1 can be declared as +# QB_NAME(v1_1, v1@SEL_1 .@SEL_1), +# and the second query block of the view v1 can be declared as +# QB_NAME(v1_2, v1@SEL_1 .@SEL_2). +# +# By default, range access is used for both `t1`'s in the statement below. +explain extended +select * from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`vvv1`.`a` AS `a`,`vvv1`.`b` AS `b`,`vvv1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `vvv1` where `test`.`t1`.`a` < 10 +# Disable index access for t1 from the second occurence of view v1: +explain extended +select /*+ qb_name(v1_2, v1@SEL_2 .@SEL_1) no_index(t1@v1_2)*/ * +from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v1_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`vvv1`.`a` AS `a`,`vvv1`.`b` AS `b`,`vvv1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `vvv1` where `test`.`t1`.`a` < 10 +# Disable index access for t1 from both occurences of view v1: +explain extended +select /*+ qb_name(v1_1, v1@SEL_1) qb_name(v1_2, v1@SEL_2 .@SEL_1) +no_index(t1@v1_1) no_index(t1@v1_2)*/ * +from v1 join (select * from v1) vvv1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 9.00 Using where +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v1_1`) NO_INDEX(`t1`@`v1_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`vvv1`.`a` AS `a`,`vvv1`.`b` AS `b`,`vvv1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `vvv1` where `test`.`t1`.`a` < 10 +# +# The case when a particular view has more than one query block. +# create view v2 as +# select * from t1 join /* Name of this query block is @SEL_1 */ +# ( +# select count(*) from t1 join v1 /* Name of this query block is @SEL_2 */ +# ) tt; +# The first query block of view v2 can be declared as +# QB_NAME(v2_1, v2@SEL_1 .@SEL_1), and the second query block can be +# declared as QB_NAME(v2_2, v2@SEL_1 .@SEL_2). +# +# See the default execution plan: +explain extended select * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for t1 from the second query block of view v2: +explain extended +select /*+ qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2)*/* from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`v2_2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for `t1` from view `v1` used in +# the first query block of view `v2`: +explain extended +select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1)*/* from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Equivalent to the above but specifying @SEL_1 explicitly: +explain extended +select /*+ qb_name(v2_v1_sel1, v2@SEL_1 .v1@SEL_2 .@SEL_1) +no_index(t1@v2_v1_sel1)*/ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1_sel1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# Disable index access for `t1` tables from views `v1` and `v2` +explain extended +select /*+ qb_name(v2_v1, v2@SEL_1 .v1@SEL_2) no_index(t1@v2_v1) +qb_name(v2_2, v2@SEL_1 .@SEL_2) no_index(t1@v2_2) */ * from v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 900 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`v2_v1`) NO_INDEX(`t1`@`v2_2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`v2_2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt` +# ====================================== +# Views with UNION +# +# Default execution plan: +explain extended select * from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# `v3` in QB path corresponds to @SEL_1: +explain extended select /*+ qb_name(qb_v3, v3) no_index(t1@qb_v3)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Addressing @SEL_1 of `v3` explicitly: +explain extended +select /*+ qb_name(qb_v3_sel1, v3.@sel_1) no_index(t1@qb_v3_sel1)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3_sel1`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# Addressing @SEL_2 of `v3` explicitly: +explain extended +select /*+ qb_name(qb_v3_sel2, v3.@sel_2) no_index(t1@qb_v3_sel2)*/* from v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 14 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 ALL NULL NULL NULL NULL 100 10.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_v3_sel2`) */ `v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`v3` +# ====================================== +# Derived tables +# +# QB_NAME(qb_dt, dt) means: inner query block of derived table `dt`, +# which is present in the same query block as the hint, gets the name `qb_dt`. +# This name can be used in other hints. +explain extended select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# QB_NAME(qb_dt, dt@sel_1) means: inner query block of derived table `dt`, +# which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# QB_NAME(qb_dt, dt@sel_1 .@sel_1) means: SELECT#1 of derived table `dt`, +# which is present in SELECT#1 of the current query block, gets the name `qb_dt`. +explain extended select /*+ qb_name(qb_dt, dt@sel_1 .@sel_1) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +# NO_MERGE(@qb4) forbids merging of any derived tables of this block. +# There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) NO_MERGE(@qb4) */ a from ( +select a from ( +select a from ( +select a from ( +select a from t1) dt +) du +) dv +) dw; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 100 100.00 +2 DERIVED ALL NULL NULL NULL NULL 100 100.00 +3 DERIVED ALL NULL NULL NULL NULL 100 100.00 +4 DERIVED ALL NULL NULL NULL NULL 100 100.00 +5 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(@`qb4`) */ `dw`.`a` AS `a` from (/* select#2 */ select `dv`.`a` AS `a` from (/* select#3 */ select `du`.`a` AS `a` from (/* select#4 */ select /*+ QB_NAME(`qb4`) */ `dt`.`a` AS `a` from (/* select#5 */ select `test`.`t1`.`a` AS `a` from `test`.`t1`) `dt`) `du`) `dv`) `dw` +# QB_NAME(qb4, dw .dv .du) addresses the query block `du`, and the hint +# MERGE(@qb4) allows merging of any derived tables of this block. +# There is one derived table `dt` inside this block, so the hint applies to it. +explain extended +select /*+ QB_NAME(qb4, dw .dv .du) MERGE(@qb4) */ a from ( +select a from ( +select a from ( +select a from ( +select a from t1) dt +) du +) dv +) dw; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 100 100.00 +2 DERIVED ALL NULL NULL NULL NULL 100 100.00 +3 DERIVED ALL NULL NULL NULL NULL 100 100.00 +4 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ MERGE(@`qb4`) */ `dw`.`a` AS `a` from (/* select#2 */ select `dv`.`a` AS `a` from (/* select#3 */ select `du`.`a` AS `a` from (/* select#4 */ select /*+ QB_NAME(`qb4`) */ `test`.`t1`.`a` AS `a` from `test`.`t1`) `du`) `dv`) `dw` +# ====================================== +# Derived tables with UNION +# +# Default execution plan: +explain extended +select * from (select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# `dt` in QB path corresponds to @SEL_1: +explain extended +select /*+ qb_name(qb_dt, dt) no_index(t1@qb_dt)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# Addressing @SEL_1 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel1, dt.@sel_1) no_index(t1@qb_dt_sel1)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 23 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +3 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt_sel1`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select /*+ QB_NAME(`qb_dt_sel1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# Addressing @SEL_2 of `dt` explicitly: +explain extended +select /*+ qb_name(qb_dt_sel2, dt.@sel_2) no_index(t1@qb_dt_sel2)*/* from +(select * from t1 where a < 10 union select * from t1 where a > 90) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 14 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +3 UNION t1 ALL NULL NULL NULL NULL 100 10.00 Using where +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`qb_dt_sel2`) */ `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 union /* select#3 */ select /*+ QB_NAME(`qb_dt_sel2`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` > 90) `dt` +# ====================================== +# Mix of views and derived tables +# +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ * +from v1 join (select * from v1) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 9 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`dt1`.`a` AS `a`,`dt1`.`b` AS `b`,`dt1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10) `dt1` where `test`.`t1`.`a` < 10 +# More complicated query. Default execution plan: +explain extended +select v1.* from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 250000 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +2 DERIVED ALL NULL NULL NULL NULL 50000 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +3 DERIVED ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#7 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt`) `dt2` where `test`.`t1`.`a` < 10) `dt1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(dt1_v1_1, dt1 .v1 .@SEL_1) no_index(t1@dt1_v1_1)*/ v1.* +from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 450000 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 ALL NULL NULL NULL NULL 100 9.00 Using where +2 DERIVED ALL NULL NULL NULL NULL 50000 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +3 DERIVED ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#7 */ select count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt`) `dt2` where `test`.`t1`.`a` < 10) `dt1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2) +no_index(t1@dt2_dt1_v1_1)*/ v1.* +from v1 join (select v1.* from v1 join (select * from v2) dt2) dt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 250000 100.00 Using join buffer (flat, BNL join) +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +2 DERIVED ALL NULL NULL NULL NULL 50000 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 +3 DERIVED ALL NULL NULL NULL NULL 500 100.00 Using join buffer (flat, BNL join) +7 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +7 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`dt2_dt1_v1_1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`tt`.`count(*)` AS `count(*)` from `test`.`t1` join (/* select#7 */ select /*+ QB_NAME(`dt2_dt1_v1_1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `tt`) `dt2` where `test`.`t1`.`a` < 10) `dt1` where `test`.`t1`.`a` < 10 +# ====================================== +# CTEs +# +# Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for t1 in CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for t1 in dt1 of CTE +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`qb_cte_dt1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte_dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Disable index access for both t1's +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte) +qb_name(qb_cte_dt1, cte .dt1) no_index(t1@qb_cte_dt1)*/ * from cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 400 100.00 +2 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 ALL NULL NULL NULL NULL 100 4.00 Using where +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select /*+ QB_NAME(`qb_cte_dt1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) NO_INDEX(`t1`@`qb_cte_dt1`) */ `cte`.`count(*)` AS `count(*)` from `cte` +# Multiple references to a CTE in a query. +# Default execution plan: +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select * from cte join cte cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +5 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# Disable index access for t1 in `cte` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte, cte) no_index(t1@qb_cte)*/ * from cte join cte as cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +5 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select /*+ QB_NAME(`qb_cte`) */ count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte`) */ `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# Disable index access for t1 in `cte1` +explain extended +with cte as (select count(*) from t1, (select * from t1 where a < 5) dt1) +select /*+ qb_name(qb_cte1, cte1) no_index(t1@qb_cte1)*/ * from cte join cte as cte1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 +1 PRIMARY ALL NULL NULL NULL NULL 200 100.00 Using join buffer (flat, BNL join) +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +5 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t1 index NULL idx_a 5 NULL 100 100.00 Using index; Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 2 100.00 Using index condition +Warnings: +Note 1003 with cte as (/* select#2 */ select count(0) AS `count(*)` from `test`.`t1` join (/* select#3 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 5) `dt1`)/* select#1 */ select /*+ NO_INDEX(`t1`@`qb_cte1`) */ `cte`.`count(*)` AS `count(*)`,`cte1`.`count(*)` AS `count(*)` from `cte` join `cte` `cte1` +# ====================================== +# Scalar context subquery +explain extended +select /*+ qb_name(inner, @sel_3) no_index(t1@inner) */ +(select (select max(a) from t1 where a < 10) + b) +from t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index NULL idx_ab 10 NULL 100 100.00 Using index +3 SUBQUERY t1 ALL NULL NULL NULL NULL 100 9.00 Using where +Warnings: +Note 1276 Field or reference 'test.t1.b' of SELECT #2 was resolved in SELECT #1 +Note 1249 Select 2 was reduced during optimization +Note 1003 /* select#1 */ select /*+ NO_INDEX(`t1`@`inner`) */ (/* select#3 */ select /*+ QB_NAME(`inner`) */ max(`test`.`t1`.`a`) from `test`.`t1` where `test`.`t1`.`a` < 10) + `test`.`t1`.`b` AS `(select (select max(a) from t1 where a < 10) + b)` from `test`.`t1` +# ====================================== +# Wrong paths generate warnings +# +explain extended +select /*+ qb_name(`qb_v1`, `v2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Wrong view name `v2`, however `@sel_2` is correct as it addresses +# the inner query block of `v1` +explain extended +select /*+ qb_name(qb_v1, `v2`@`sel_2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`@`sel_2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(qb_v1, v2@sel_1 .@sel_2)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v2`@`sel_1` .@`sel_2`) is ignored. `v2` required at element #1 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Attempting to reference a regular table as a query block: +explain extended +select /*+ qb_name(qb_v1, dt .t1)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `dt` .`t1`) is ignored. `t1` required at element #2 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# Wrong view name inside a derived table: +explain extended +select /*+ qb_name(qb_v1, dt .v2)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `dt` .`v2`) is ignored. `v2` required at element #2 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# Wrong select number: +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_5)*/* from (select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4259 Hint QB_NAME(`qb_v1`, `dt` .`v1`@`sel_5`) is ignored. SEL_5 required at element #2 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +explain extended +select /*+ qb_name(qb_v1, dt .v1@sel_1 .@sel_2)*/* from +(select t1.* from t1 join v1) dt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 500 100.00 +2 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using where; Using index +2 DERIVED t1 ALL NULL NULL NULL NULL 100 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4259 Hint QB_NAME(`qb_v1`, `dt` .`v1`@`sel_1` .@`sel_2`) is ignored. SEL_2 required at element #3 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `dt`.`a` AS `a`,`dt`.`b` AS `b`,`dt`.`c` AS `c` from (/* select#2 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` join `test`.`t1` where `test`.`t1`.`a` < 10) `dt` +# Wrong select number syntax: +explain extended +select /*+ qb_name(qb_v1, `v1`@`lex_2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`lex_2`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +explain extended +select /*+ qb_name(qb_v1, v1 .lex_2)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4260 Hint QB_NAME(`qb_v1`, `v1` .`lex_2`) is ignored. `lex_2` required at element #2 of the path is not found in the target query block. +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Select number is too large: +explain extended +select /*+ qb_name(qb_v1, `v1`@`SEL_9999999999`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`SEL_9999999999`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# Exponential select numbers are not allowed: +explain extended +select /*+ qb_name(qb_v1, `v1`@`SEL_1e2`)*/* from v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +Warnings: +Warning 4258 Hint QB_NAME(`qb_v1`, `v1`@`SEL_1e2`) is ignored. Element #1 of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...). +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where `test`.`t1`.`a` < 10 +# @SEL_N matches a SELECT in a sibling unit (warning expected). +# v1 has only @SEL_1, v3 has @SEL_1 and @SEL_2 (UNION) +# Navigate to v1, ask for @SEL_2 which matches v3's SELECT +explain extended +select /*+ qb_name(qb_wrong, v1 .@SEL_2) */ * from v1 join v3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +1 PRIMARY ALL NULL NULL NULL NULL 19 100.00 Using join buffer (flat, BNL join) +3 DERIVED t1 range idx_a,idx_ab idx_a 5 NULL 5 100.00 Using index condition +4 UNION t1 range idx_a,idx_ab idx_ab 5 NULL 14 100.00 Using index condition +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Warning 4259 Hint QB_NAME(`qb_wrong`, `v1` .@`SEL_2`) is ignored. SEL_2 required at element #2 of the path is not found in the target query block. +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`v3`.`a` AS `a`,`v3`.`b` AS `b`,`v3`.`c` AS `c` from `test`.`t1` join `test`.`v3` where `test`.`t1`.`a` < 10 +drop table t1, t2; +drop view v1, v2, v3; +set optimizer_switch= default; diff --git a/mysql-test/main/opt_hints_qb_name_path.test b/mysql-test/main/opt_hints_qb_name_path.test new file mode 100644 index 0000000000000..fc173afba504d --- /dev/null +++ b/mysql-test/main/opt_hints_qb_name_path.test @@ -0,0 +1,11 @@ +--echo +--echo ================================================================ +set optimizer_switch= 'derived_merge=on'; +--source opt_hints_qb_name_path.inc + +--echo +--echo ================================================================ +set optimizer_switch= 'derived_merge=off'; +--source opt_hints_qb_name_path.inc + +set optimizer_switch= default; diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc index becd69c94ac56..d9d368afdfb8a 100644 --- a/sql/opt_hints.cc +++ b/sql/opt_hints.cc @@ -86,6 +86,13 @@ int cmp_lex_string(const LEX_CSTRING &s, const LEX_CSTRING &t, (const uchar*)t.str, t.length); } +int cmp_lex_string_limit(const LEX_CSTRING &s, const LEX_CSTRING &t, + const CHARSET_INFO *cs, size_t chars_limit) +{ + return cs->coll->strnncollsp(cs, (const uchar*)s.str, chars_limit, + (const uchar*)t.str, chars_limit); +} + /* This is a version of push_warning_printf() guaranteeing no escalation of the warning to the level of error diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc index 4161dfb21f143..56eaa3448f073 100644 --- a/sql/opt_hints_parser.cc +++ b/sql/opt_hints_parser.cc @@ -17,6 +17,8 @@ */ #include "opt_hints_parser.h" +#include "lex_ident_sys.h" +#include "m_ctype.h" #include "sql_error.h" #include "mysqld_error.h" #include "sql_class.h" @@ -51,6 +53,12 @@ Opt_hints_table *get_table_hints(Parse_context *pc, void append_table_name(THD *thd, String *str, const LEX_CSTRING &table_name, const LEX_CSTRING &qb_name); +int cmp_lex_string(const LEX_CSTRING &s, const LEX_CSTRING &t, + const CHARSET_INFO *cs); + +int cmp_lex_string_limit(const LEX_CSTRING &s, const LEX_CSTRING &t, + const CHARSET_INFO *cs, size_t chars_limit); + static const Lex_ident_sys null_ident_sys; @@ -250,6 +258,8 @@ Optimizer_hint_tokenizer::get_token(CHARSET_INFO *cs) TokenID::tIDENT : find_keyword(ident)); if (!get_char(',')) return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tCOMMA); + if (!get_char('.')) + return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tDOT); if (!get_char('@')) return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tAT); if (!get_char('(')) @@ -297,69 +307,13 @@ void Parser::push_warning_syntax_error(THD *thd, uint start_lineno) } -bool -Parser::Table_name_list_container::add(Optimizer_hint_parser *p, - Table_name &&elem) -{ - Table_name *pe= (Table_name*) p->m_thd->alloc(sizeof(*pe)); - if (!pe) - return true; - *pe= std::move(elem); - return push_back(pe, p->m_thd->mem_root); -} - - -bool Parser::Hint_param_table_list_container::add(Optimizer_hint_parser *p, - Hint_param_table &&elem) -{ - Hint_param_table *pe= (Hint_param_table*) p->m_thd->alloc(sizeof(*pe)); - if (!pe) - return true; - *pe= std::move(elem); - return push_back(pe, p->m_thd->mem_root); -} - - -bool Parser::Hint_param_index_list_container::add(Optimizer_hint_parser *p, - Hint_param_index &&elem) -{ - Hint_param_index *pe= (Hint_param_index*) p->m_thd->alloc(sizeof(*pe)); - if (!pe) - return true; - *pe= std::move(elem); - return push_back(pe, p->m_thd->mem_root); -} - - -bool Parser::Hint_list_container::add(Optimizer_hint_parser *p, Hint &&elem) -{ - Hint *pe= new (p->m_thd->mem_root) Hint; - if (!pe) - return true; - *pe= std::move(elem); - return push_back(pe, p->m_thd->mem_root); -} - - -bool Parser::Semijoin_strategy_list_container::add(Optimizer_hint_parser *p, - Semijoin_strategy &&elem) -{ - Semijoin_strategy *pe= (Semijoin_strategy*) p->m_thd->alloc(sizeof(*pe)); - if (!pe) - return true; - *pe= std::move(elem); - return push_back(pe, p->m_thd->mem_root); -} - - /* Resolve a parsed table level hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. -Return value: -- false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed -- true: critical errors detected, break further hints processing + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing */ bool Parser::Table_level_hint::resolve(Parse_context *pc) const { @@ -495,10 +449,9 @@ bool Parser::Table_level_hint::resolve(Parse_context *pc) const Resolve a parsed index level hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. - Return value: - - false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed - - true: critical errors detected, break further hints processing + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing Taxonomy of index hints - 2 levels of hints: @@ -766,35 +719,236 @@ void Parser::Index_level_hint::append_args(THD *thd, String *str) const } + +/* + Check if a SELECT_LEX is a descendant of a given unit +*/ +static bool is_descendant_of_unit(SELECT_LEX *sl, st_select_lex_unit *unit) +{ + // Walk up the tree from sl to see if we reach unit + while (sl) + { + st_select_lex_unit *sl_unit= sl->master_unit(); + if (sl_unit == unit) + return true; + // Move up to the outer SELECT + sl= sl_unit->outer_select(); + } + return false; +} + + +extern void push_warning_safe(THD *thd, Sql_condition::enum_warning_level level, + uint code, const char *format, ...); + /* Resolve a parsed query block name hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. -Return value: -- false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed -- true: critical errors detected, break further hints processing + @details + If the hint has form QB_NAME(new_name, path), locate the SELECT pointed by + the path and set its opt_hints_qb->name to the new_name. + + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing */ bool Parser::Qb_name_hint::resolve(Parse_context *pc) const { - Opt_hints_qb *qb= pc->select->opt_hints_qb; + const opt_hints_enum hint_type= QB_NAME_HINT_ENUM; + Opt_hints_qb *target_qb= nullptr; + const Lex_ident_sys qb_name= Query_block_name::to_ident_sys(pc->thd); - DBUG_ASSERT(qb); + const Opt_query_block_path &path= *this; + if (path.is_empty()) + { + // Simple hint, e.g. QB_NAME(qb1) + target_qb= pc->select->opt_hints_qb; + } + else + { + // QB_NAME hint with path, for example QB_NAME(qb1, v1@sel_1 .@sel_2) + const Query_block_path_list &qb_path_list= path; + + // Start from the current SELECT_LEX + SELECT_LEX *target_select= pc->select; + // Iterate through all path elements + uint elem_num= 0; + for (const QB_path_element &elem : qb_path_list) + { + elem_num++; + Lex_ident_sys select_num_str; + Lex_ident_sys view_name; + if (const At_QB_path_element_select_num &at_select_num= elem) + { + // This path element is a SELECT_LEX number, for example @SEL_1 + const QB_path_element_select_num &qb_path_select_num= at_select_num; + select_num_str= qb_path_select_num.to_ident_sys(pc->thd); + } + else if (const QB_path_element_view_sel &view_sel= elem) + { + /* + This path element is a combination of view name + and optional SELECT_LEX number, for example v1 or v1@SEL_1 + */ + const QB_path_element_view_name &qb_path_view_name= view_sel; + view_name= qb_path_view_name.to_ident_sys(pc->thd); + const Opt_at_QB_path_element_select_num &opt_at_select_num= view_sel; + if (const At_QB_path_element_select_num2 &at_select_num= opt_at_select_num) + { + const QB_path_element_select_num &qb_path_select_num= at_select_num; + select_num_str= qb_path_select_num.to_ident_sys(pc->thd); + } + } - const Lex_ident_sys qb_name_sys= Query_block_name::to_ident_sys(pc->thd); + // Find SELECT_LEX corresponding to the parsed path element + if (select_num_str.length > 0) + { + // Check format and extract select number, then wind to the select_lex + const LEX_CSTRING format= { "SEL_", 4 }; + if (cmp_lex_string_limit(select_num_str, format, + system_charset_info, format.length)) + { + String str= get_hint_string(pc->thd); + push_warning_safe( + pc->thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_QB_NAME_PATH_INVALID_SELECT, + ER_THD(pc->thd, ER_WARN_QB_NAME_PATH_INVALID_SELECT), + str.c_ptr_safe(), elem_num); + return false; + } - if (qb->get_name().str || // QB name is already set - qb->get_parent()->find_by_name(qb_name_sys)) // Name is already used + const char *select_num_str_end= select_num_str.str + select_num_str.length; + char *endptr= nullptr; + int error; + long long hint_select_num= + my_strtoll10(select_num_str.str + format.length, &endptr, &error); + + /* + (1) error during conversion + (2) conversion stopped before end of string + (3) select number out of valid range (1-10000) + */ + if (error != 0 || // (1) + endptr != select_num_str_end || // (2) + hint_select_num < 1 || hint_select_num > 10000) // (3) + { + String str= get_hint_string(pc->thd); + push_warning_safe( + pc->thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_QB_NAME_PATH_INVALID_SELECT, + ER_THD(pc->thd, ER_WARN_QB_NAME_PATH_INVALID_SELECT), + str.c_ptr_safe(), elem_num); + return false; + } + + /* + Select number in the hint is treated as a relative offset from + current unit's first_select() select_number (base select_number). + I.e., @SEL_N means: find SELECT_LEX with select_number = + base select_number + (N - 1). + @SEL_1 always points to the base SELECT_LEX, @SEL_2 - to the next + one, and so on. + */ + st_select_lex_unit *unit= target_select->master_unit(); + uint base_select_num= unit->first_select()->select_number; + uint target_select_num= base_select_num + (hint_select_num - 1); + /* + Traverse the global SELECT_LEX list to find SELECT_LEX + with target_select_num + */ + target_select= nullptr; + for (SELECT_LEX *sl= pc->thd->lex->all_selects_list; sl; + sl= sl->next_select_in_list()) + { + if (sl->select_number == target_select_num && + is_descendant_of_unit(sl, unit)) + { + target_select= sl; + break; + } + } + if (!target_select) + { + // Target select_number wasn't found + String str= get_hint_string(pc->thd); + push_warning_safe( + pc->thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_QB_NAME_PATH_SELECT_NOT_FOUND, + ER_THD(pc->thd, ER_WARN_QB_NAME_PATH_SELECT_NOT_FOUND), + str.c_ptr_safe(), hint_select_num, elem_num); + return false; + } + } + if (view_name.length > 0) + { + // Traverse the list of current SELECT_LEX's tables to find `view_name` + bool found= false; + for (TABLE_LIST *tbl= target_select->table_list.first; tbl; + tbl= tbl->next_local) + { + if (tbl->is_view_or_derived() && + !cmp_lex_string(tbl->alias, view_name, system_charset_info)) + { + found= true; + target_select= tbl->get_unit()->first_select(); + break; + } + } + if (!found) + { + String str= get_hint_string(pc->thd); + push_warning_safe( + pc->thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_QB_NAME_PATH_VIEW_NOT_FOUND, + ER_THD(pc->thd, ER_WARN_QB_NAME_PATH_VIEW_NOT_FOUND), + str.c_ptr_safe(), view_name.str, elem_num); + return false; + } + } + } + /* + Loop is finished, and target SELECT_LEX has been successfully found. + Create Opt_hints_qb structure for it (or get if it is already created). + */ + Parse_context target_pc(pc->thd, target_select); + target_qb= get_qb_hints(&target_pc); + if (!target_qb) + return true; + } + + DBUG_ASSERT(target_qb); + /* + (1) Name is already assigned to this query block + (2) Given name is already used inside this block of hints + */ + if (target_qb->get_name().str || // (1) + target_qb->get_parent()->find_by_name(qb_name)) // (2) { - print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, true, - &qb_name_sys, nullptr, nullptr, nullptr); + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, nullptr); return false; } - - qb->set_name(qb_name_sys); + target_qb->set_name(qb_name); return false; } +/* + Generate the string representation of the QB_NAME hint, for example, + "QB_NAME(`qb_v1`, `v1` .`v2`@`sel_2`)" + + @return String representation of the hint +*/ +String Parser::Qb_name_hint::get_hint_string(THD *thd) const +{ + String str(STRING_WITH_LEN("QB_NAME("), system_charset_info); + const Lex_ident_sys qb_name= Query_block_name::to_ident_sys(thd); + append_identifier(thd, &str, qb_name.str, qb_name.length); + append_args(thd, &str); + str.append(')'); + return str; +} + void Parser::Semijoin_hint::fill_strategies_map(Opt_hints_qb *qb) const { @@ -836,10 +990,9 @@ void Parser::Semijoin_hint::add_strategy_to_map(TokenID token_id, Resolve a parsed semijoin hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. -Return value: -- false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed -- true: critical errors detected, break further hints processing + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing */ bool Parser::Semijoin_hint::resolve(Parse_context *pc) const { @@ -872,12 +1025,63 @@ bool Parser::Semijoin_hint::resolve(Parse_context *pc) const } +/* + Append QB_NAME hint arguments for printing in warnings/EXPLAIN +*/ +void Parser::Qb_name_hint::append_args(THD *thd, String *str) const +{ + const Query_block_path &qb_path= *this; + DBUG_ASSERT(!qb_path.is_empty()); // append_args() is only valid for + // complex QB_NAME hints with path + + // It is a complex QB_NAME hint with path, print the path to the query block + str->append(STRING_WITH_LEN(", ")); + bool first_element= true; + for (const QB_path_element &elem : qb_path) + { + if (!first_element) + str->append(STRING_WITH_LEN(" .")); + first_element= false; + Lex_ident_sys select_num; + + // Check if it's a select number or view/table name + if (const At_QB_path_element_select_num &at_select_num= elem) + { + const QB_path_element_select_num &qb_path_select_num= at_select_num; + select_num= qb_path_select_num.to_ident_sys(thd); + } + else if (const QB_path_element_view_sel &view_sel= elem) + { + const QB_path_element_view_name &qb_path_view_name= view_sel; + Lex_ident_sys view_name= qb_path_view_name.to_ident_sys(thd); + append_identifier(thd, str, &view_name); + + const Opt_at_QB_path_element_select_num &opt_at_select_num= view_sel; + if (const At_QB_path_element_select_num2 &at_select_num= + opt_at_select_num) + { + const QB_path_element_select_num &qb_path_select_num= at_select_num; + select_num= qb_path_select_num.to_ident_sys(thd); + } + } + else + { + DBUG_ASSERT(false); + } + if (select_num.length > 0) + { + str->append(STRING_WITH_LEN("@")); + append_identifier(thd, str, &select_num); + } + } +} + + /* Helper function to be called by Semijoin_hint::resolve(). -Return value: -- pointer to Opt_hints_qb if the hint was resolved successfully -- NULL if the hint was ignored + @return pointer to Opt_hints_qb if the hint was resolved successfully + NULL if the hint was ignored */ Opt_hints_qb* Parser::Semijoin_hint:: resolve_for_qb_name(Parse_context *pc, bool hint_state, @@ -952,10 +1156,9 @@ void Parser::Semijoin_hint:: Resolve a parsed subquery hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. -Return value: -- false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed -- true: critical errors detected, break further hints processing + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing */ bool Parser::Subquery_hint::resolve(Parse_context *pc) const { @@ -987,9 +1190,8 @@ bool Parser::Subquery_hint::resolve(Parse_context *pc) const /* Helper function to be called by Subquery_hint::resolve(). -Return value: -- pointer to Opt_hints_qb if the hint was resolved successfully -- NULL if the hint was ignored + @return pointer to Opt_hints_qb if the hint was resolved successfully + NULL if the hint was ignored */ Opt_hints_qb* Parser::Subquery_hint:: resolve_for_qb_name(Parse_context *pc, TokenID token_id, @@ -1116,10 +1318,9 @@ void Parser::Join_order_hint::append_args(THD *thd, String *str) const Resolve a parsed join order hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. - Return value: - - false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed - - true: critical errors detected, break further hints processing + @return false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + true: critical errors detected, break further hints processing */ bool Parser::Join_order_hint::resolve(Parse_context *pc) { diff --git a/sql/opt_hints_parser.h b/sql/opt_hints_parser.h index 996389100371d..64d305c1ef17c 100644 --- a/sql/opt_hints_parser.h +++ b/sql/opt_hints_parser.h @@ -62,6 +62,7 @@ class Optimizer_hint_tokenizer: public Extended_string_tokenizer tEOF= 2, // returned when the end of input is reached // One character tokens + tDOT= '.', tCOMMA= ',', tAT= '@', tLPAREN= '(', @@ -167,6 +168,7 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, m_syntax_error(false), m_fatal_error(false) { } + THD *thd() const { return m_thd; } bool set_syntax_error() { m_syntax_error= true; @@ -268,6 +270,8 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, using TokenAT= TokenParser; + using TokenCOMMA= TokenParser; + using TokenEOF= TokenParser; using Keyword_QB_NAME= TokenParser; @@ -447,14 +451,9 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, hint_param_table_list ::= hint_param_table [ {, hint_param_table}... ] opt_hint_param_table_list ::= [ hint_param_table_list ] */ - class Hint_param_table_list_container: public List - { - public: - Hint_param_table_list_container() - { } - bool add(Optimizer_hint_parser *p, Hint_param_table &&table); - size_t count() const { return elements; } - }; + using Hint_param_table_list_container= List_container; + class Opt_hint_param_table_list: public LIST - { - public: - Table_name_list_container() - { } - bool add(Optimizer_hint_parser *p, Table_name &&table); - size_t count() const { return elements; } - }; + using Table_name_list_container= List_container; class Opt_table_name_list: public LIST - { - public: - Hint_param_index_list_container() - { } - bool add(Optimizer_hint_parser *p, Hint_param_index &&table); - size_t count() const { return elements; } - }; + using Hint_param_index_list_container= List_container; class Opt_hint_param_index_list: public LIST + { + public: + using AND2::AND2; + using AND2::operator=; + }; + + // just a clone of At_QB_path_element_select_num to avoid inheritance issues + class At_QB_path_element_select_num2: + public AND2 + { + public: + using AND2::AND2; + using AND2::operator=; + }; + + class Opt_at_QB_path_element_select_num: + public OPT + { + public: + using OPT::OPT; + }; + + /* + qb_path_element_view_sel ::= qb_path_element_view_name + [ @ qb_path_element_select_num ] + */ + class QB_path_element_view_sel: public AND2 + { + public: + using AND2::AND2; + }; + + // qb_path_element ::= @ qb_path_element_select_num | qb_path_element_view_sel + class QB_path_element: public OR2 + { + public: + using OR2::OR2; + }; + + // Container for query block path elements + class Query_block_path_list: public List_container + { + public: + static Query_block_path_list empty(const Parser &) + { + return Query_block_path_list(); + } + }; + + /* + query_block_path ::= query_block_path_element + [ {, query_block_path_element }... ] + */ + class Query_block_path: public LIST + { + using LIST::LIST; + }; - // qb_name_hint ::= QB_NAME ( query_block_name ) + // opt_query_block_path ::= [, query_block_path] + class Opt_query_block_path: public AND2::Opt + { + public: + using Opt::Opt; + }; + + // qb_name_with_opt_path ::= query_block_name [, query_block_path] + class QB_name_with_opt_path: public AND2 + { + public: + using AND2::AND2; + }; + + // qb_name_hint ::= QB_NAME ( qb_name_with_opt_path ) class Qb_name_hint: public AND4 + QB_name_with_opt_path, + RParen>, + public Printable_parser_rule { public: using AND4::AND4; bool resolve(Parse_context *pc) const; + void append_args(THD *thd, String *str) const override; + String get_hint_string(THD *thd) const; }; @@ -698,15 +784,8 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, strategy_list ::= strategy_name [ {, strategy_name }... ] opt_strategy_list ::= [ strategy_list ] */ - class Semijoin_strategy_list_container: public List - { - public: - Semijoin_strategy_list_container() - { } - bool add(Optimizer_hint_parser *p, Semijoin_strategy &&strategy); - size_t count() const { return elements; } - }; - + using Semijoin_strategy_list_container= List_container; class Opt_sj_strategy_list: public LIST @@ -944,15 +1023,7 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, private: // hint_list ::= hint [ hint... ] - class Hint_list_container: public List - { - public: - Hint_list_container() - { } - bool add(Optimizer_hint_parser *p, Hint &&hint); - size_t count() const { return elements; } - }; - + using Hint_list_container= List_container; class Hint_list: public LIST diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ad79a80cc11de..a2f63bafe9975 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12392,3 +12392,9 @@ ER_WARN_AMBIGUOUS_QB_NAME eng "Query block name %s is ambiguous for %s hint" ER_WARN_IMPLICIT_QB_NAME_FOR_UNION eng "Implicit query block name %s is not supported for derived tables and views with UNION/EXCEPT/INTERSECT and is ignored for %s hint" +ER_WARN_QB_NAME_PATH_INVALID_SELECT + eng "Hint %s is ignored. Element #%u of the path contains invalid select number (expected: @SEL_1, @SEL_2, ...)." +ER_WARN_QB_NAME_PATH_SELECT_NOT_FOUND + eng "Hint %s is ignored. SEL_%u required at element #%u of the path is not found in the target query block." +ER_WARN_QB_NAME_PATH_VIEW_NOT_FOUND + eng "Hint %s is ignored. `%s` required at element #%u of the path is not found in the target query block." diff --git a/sql/simple_parser.h b/sql/simple_parser.h index 7e3307a7c13a7..60ff002f0e99c 100644 --- a/sql/simple_parser.h +++ b/sql/simple_parser.h @@ -1188,6 +1188,35 @@ class Parser_templates ELEMENT_PARSER, SEP, 0/*no elements is OK*/>; }; + /* + Generic container for parser list elements that handles memory allocation. + + This template provides a reusable container for LIST<> parsers, replacing + the need for individual *_list_container classes with custom add() methods. + + ELEM_TYPE: The type of elements stored in the list + PARSER: The parser class (must provide thd() method for memory allocation) + + Elements are allocated on the THD's memory pool and moved into place, + ensuring proper lifetime management within the parser's memory context. + */ + template + class List_container : public List + { + public: + bool add(PARSER *p, ELEM_TYPE &&elem) + { + // Allocate memory for the new list element + ELEM_TYPE *new_elem= (ELEM_TYPE*) p->thd()->alloc(sizeof(ELEM_TYPE)); + if (!new_elem) + return true; + // Move-construct the new element in the preallocated memory + ::new (new_elem) ELEM_TYPE(std::move(elem)); + return List::push_back(new_elem, p->thd()->mem_root); + } + + size_t count() const { return List::elements; } + }; }; #endif // SIMPLE_PARSER_H