From 4179821b241bea23438fdd2be26f125b5327d85d Mon Sep 17 00:00:00 2001 From: Dave Gosselin <dave.gosselin@mariadb.com> Date: Mon, 24 Feb 2025 11:32:27 -0500 Subject: [PATCH] MDEV-36106 New-style hints: [NO_]DERIVED_CONDITION_PUSHDOWN, [NO_]MERGE Implements and tests the optimizer hints DERIVED_CONDITION_PUSHDOWN and NO_DERIVED_CONDITION_PUSHDOWN, table-level hints to enable and disable, respectively, the condition pushdown for derived tables which is typically controlled by the condition_pushdown_for_derived optimizer switch. Implements and tests the optimizer hints MERGE and NO_MERGE, table-level hints to enable and disable, respectively, the derived_merge optimization which is typically controlled by the derived_merge optimizer switch. Sometimes hints need to be fixed before TABLE instances are available, but after TABLE_LIST instances have been created (as in the cases of MERGE and NO_MERGE). This commit introduces code to fix such hints provisionally, allowing them to be fully fixed later, after their corresponding TABLE instances have been created. --- ...106-derived-condition-pushdown-hint.result | 1276 +++++++++++++++++ ...36106-derived-condition-pushdown-hint.test | 214 +++ mysql-test/main/mdev-36106-merge-hint.result | 476 ++++++ mysql-test/main/mdev-36106-merge-hint.test | 80 ++ sql/opt_hints.cc | 88 +- sql/opt_hints.h | 49 +- sql/opt_hints_parser.cc | 16 + sql/opt_hints_parser.h | 12 +- sql/sql_base.cc | 7 +- sql/sql_select.cc | 55 +- sql/table.cc | 12 +- 11 files changed, 2243 insertions(+), 42 deletions(-) create mode 100644 mysql-test/main/mdev-36106-derived-condition-pushdown-hint.result create mode 100644 mysql-test/main/mdev-36106-derived-condition-pushdown-hint.test create mode 100644 mysql-test/main/mdev-36106-merge-hint.result create mode 100644 mysql-test/main/mdev-36106-merge-hint.test diff --git a/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.result b/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.result new file mode 100644 index 0000000000000..902cc0559b717 --- /dev/null +++ b/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.result @@ -0,0 +1,1276 @@ +create table t1 (a int, b int, c int); +create table t2 (a int, b int, c int, d decimal); +insert into t1 values +(1,21,345), (1,33,7), (8,33,114), (1,21,500), (1,19,107), (5,14,787), +(8,33,123), (9,10,211), (5,16,207), (1,33,988), (5,27,132), (1,21,104), +(6,20,309), (6,20,315), (1,21,101), (8,33,404), (9,10,800), (1,21,123), +(7,11,708), (6,20,214); +create index t1_a on t1 (a); +insert into t2 values +(2,3,207,207.0000), (1,21,909,12.0000), (7,13,312,406.0000), +(8,64,248,107.0000), (6,20,315,279.3333), (1,19,203,107.0000), +(8,80,800,314.0000), (3,12,231,190.0000), (6,23,303,909.0000); +Warnings: +Note 1265 Data truncated for column 'd' at row 5 +create view v1 as select a, b, max(c) as max_c, avg(c) as avg_c from t1 +group by a,b having max_c < 707; +create table t3 select 2*seq as a, 2*seq+1 as b from seq_0_to_1000; +CREATE TABLE t4 (a INT, b INT); +INSERT INTO t4 VALUES (1,2),(2,3),(3,4); +create table t5 select seq as i, 10*seq as j from seq_1_to_10; +create view v2 as select * from t5; +set @save_optimizer_switch=@@optimizer_switch; +set session optimizer_switch='condition_pushdown_for_derived=on'; +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where +((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or +((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 9, + "cost": "COST_REPLACED", + "filtered": 100 + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 9, + "rows": 20, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "v1.max_c > 300 or v1.max_c < 135" + }, + "buffer_type": "flat", + "buffer_size": "238", + "join_type": "BNL", + "attached_condition": "v1.b = t2.b and v1.max_c > 300 and v1.avg_c > t2.d or v1.a = t2.a and v1.max_c < 135 and v1.max_c < t2.c", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "max_c < 707", + "filesort": { + "sort_key": "t1.a, t1.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 20, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where +((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or +((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +a b max_c avg_c a b c d +1 19 107 107.0000 1 21 909 12 +1 19 107 107.0000 1 19 203 107 +1 21 500 234.6000 1 21 909 12 +6 20 315 279.3333 6 20 315 279 +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from +(select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from +(select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +a +3 +5 +explain format=json select * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 3 or a = 5", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "having_condition": "t3.a MOD 10 <> 0 or t3.a MOD 5 <> 0", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "having_condition": "t3.a MOD 10 <> 0 or t3.a MOD 5 <> 0", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(du)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 3 or a = 5", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(du)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt,du)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt,du)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT t4.b AS a +FROM t4 +GROUP BY t4.a +) dt WHERE (dt.a=2); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 2", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t4.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t4", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT t4.b AS a +FROM t4 +GROUP BY t4.a +) dt WHERE (dt.a=2); +a +2 +explain format=json SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT qb.b AS a +FROM ( +select 1*t4.b as b, 1*t4.a as a from t4 +) qb +GROUP BY qb.a +) dt WHERE (dt.a=2); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 2", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "1 * t4.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t4", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT qb.b AS a +FROM ( +select 1*t4.b as b, 1*t4.a as a from t4 +) qb +GROUP BY qb.a +) dt WHERE (dt.a=2); +a +2 +set session optimizer_switch='condition_pushdown_for_derived=off'; +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where +((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or +((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t2", + "access_type": "ALL", + "loops": 1, + "rows": 9, + "cost": "COST_REPLACED", + "filtered": 100 + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 9, + "rows": 20, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "v1.max_c > 300 or v1.max_c < 135" + }, + "buffer_type": "flat", + "buffer_size": "238", + "join_type": "BNL", + "attached_condition": "v1.b = t2.b and v1.max_c > 300 and v1.avg_c > t2.d or v1.a = t2.a and v1.max_c < 135 and v1.max_c < t2.c", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "max_c < 707 and (max_c > 300 or max_c < 135)", + "filesort": { + "sort_key": "t1.a, t1.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 20, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where +((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or +((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +a b max_c avg_c a b c d +1 19 107 107.0000 1 21 909 12 +1 19 107 107.0000 1 19 203 107 +1 21 500 234.6000 1 21 909 12 +6 20 315 279.3333 6 20 315 279 +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from +(select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 3 or a = 5", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from +(select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +a +3 +5 +explain format=json SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT t4.b AS a +FROM t4 +GROUP BY t4.a +) dt WHERE (dt.a=2); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 2", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 2", + "filesort": { + "sort_key": "t4.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t4", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT t4.b AS a +FROM t4 +GROUP BY t4.a +) dt WHERE (dt.a=2); +a +2 +explain format=json SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT qb.b AS a +FROM ( +select 1*t4.b as b, 1*t4.a as a from t4 +) qb +GROUP BY qb.a +) dt WHERE (dt.a=2); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 2", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "b = 2", + "filesort": { + "sort_key": "1 * t4.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t4", + "access_type": "ALL", + "loops": 1, + "rows": 3, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + } + ] + } +} +SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( +SELECT qb.b AS a +FROM ( +select 1*t4.b as b, 1*t4.a as a from t4 +) qb +GROUP BY qb.a +) dt WHERE (dt.a=2); +a +2 +explain format=json select * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 3 or a = 5", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(du)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "having_condition": "t3.a MOD 10 <> 0 or t3.a MOD 5 <> 0", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ DERIVED_CONDITION_PUSHDOWN(du)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt,du)*/ * from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a = 3 or dt.a = 5", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "having_condition": "a = 3 or a = 5", + "filesort": { + "sort_key": "t3.a", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100 + } + } + ] + } + } + } + } + } + }, + { + "block-nl-join": { + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "loops": 1001, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0" + }, + "buffer_type": "flat", + "buffer_size": "8Kb", + "join_type": "BNL", + "attached_condition": "du.a MOD 10 <> 0 or du.a MOD 5 <> 0", + "materialized": { + "query_block": { + "select_id": 3, + "cost": "COST_REPLACED", + "having_condition": "t3.a MOD 10 <> 0 or t3.a MOD 5 <> 0", + "filesort": { + "sort_key": "t3.b", + "temporary_table": { + "nested_loop": [ + { + "table": { + "table_name": "t3", + "access_type": "ALL", + "loops": 1, + "rows": 1001, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t3.a MOD 2 = 0" + } + } + ] + } + } + } + } + } + } + ] + } +} +select /*+ DERIVED_CONDITION_PUSHDOWN(dt,du)*/ count(*) from ( +select t3.b as a from t3 group by t3.a +) dt join ( +select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +count(*) +1600 +set optimizer_switch=@save_optimizer_switch; +drop view v1, v2; +drop table t1, t2, t3, t4, t5; diff --git a/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.test b/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.test new file mode 100644 index 0000000000000..b08c6142e0a52 --- /dev/null +++ b/mysql-test/main/mdev-36106-derived-condition-pushdown-hint.test @@ -0,0 +1,214 @@ +--source include/have_sequence.inc +--disable_ps_protocol +--disable_view_protocol +create table t1 (a int, b int, c int); +create table t2 (a int, b int, c int, d decimal); +insert into t1 values + (1,21,345), (1,33,7), (8,33,114), (1,21,500), (1,19,107), (5,14,787), + (8,33,123), (9,10,211), (5,16,207), (1,33,988), (5,27,132), (1,21,104), + (6,20,309), (6,20,315), (1,21,101), (8,33,404), (9,10,800), (1,21,123), + (7,11,708), (6,20,214); +create index t1_a on t1 (a); +insert into t2 values + (2,3,207,207.0000), (1,21,909,12.0000), (7,13,312,406.0000), + (8,64,248,107.0000), (6,20,315,279.3333), (1,19,203,107.0000), + (8,80,800,314.0000), (3,12,231,190.0000), (6,23,303,909.0000); +create view v1 as select a, b, max(c) as max_c, avg(c) as avg_c from t1 + group by a,b having max_c < 707; +create table t3 select 2*seq as a, 2*seq+1 as b from seq_0_to_1000; +CREATE TABLE t4 (a INT, b INT); +INSERT INTO t4 VALUES (1,2),(2,3),(3,4); +create table t5 select seq as i, 10*seq as j from seq_1_to_10; +create view v2 as select * from t5; + +set @save_optimizer_switch=@@optimizer_switch; + +set session optimizer_switch='condition_pushdown_for_derived=on'; + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where + ((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or + ((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where + ((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or + ((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from + (select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from + (select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); + +--source include/explain-no-costs.inc +explain format=json select * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(du)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(du)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt,du)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt,du)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT t4.b AS a + FROM t4 + GROUP BY t4.a +) dt WHERE (dt.a=2); +SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT t4.b AS a + FROM t4 + GROUP BY t4.a +) dt WHERE (dt.a=2); + +--source include/explain-no-costs.inc +explain format=json SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT qb.b AS a + FROM ( + select 1*t4.b as b, 1*t4.a as a from t4 + ) qb + GROUP BY qb.a +) dt WHERE (dt.a=2); +SELECT /*+ NO_DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT qb.b AS a + FROM ( + select 1*t4.b as b, 1*t4.a as a from t4 + ) qb + GROUP BY qb.a +) dt WHERE (dt.a=2); + +set session optimizer_switch='condition_pushdown_for_derived=off'; + +--source include/explain-no-costs.inc +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where + ((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or + ((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); +select /*+ DERIVED_CONDITION_PUSHDOWN(v1) */ * from v1,t2 where + ((v1.max_c>300) and (v1.avg_c>t2.d) and (v1.b=t2.b)) or + ((v1.max_c<135) and (v1.max_c<t2.c) and (v1.a=t2.a)); + +--source include/explain-no-costs.inc +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from + (select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); +select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from + (select t3.b as a from t3 group by t3.a) dt where (dt.a=3 or dt.a=5); + +--source include/explain-no-costs.inc +explain format=json SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT t4.b AS a + FROM t4 + GROUP BY t4.a +) dt WHERE (dt.a=2); +SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT t4.b AS a + FROM t4 + GROUP BY t4.a +) dt WHERE (dt.a=2); + +--source include/explain-no-costs.inc +explain format=json SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT qb.b AS a + FROM ( + select 1*t4.b as b, 1*t4.a as a from t4 + ) qb + GROUP BY qb.a +) dt WHERE (dt.a=2); +SELECT /*+ DERIVED_CONDITION_PUSHDOWN(dt) */ * FROM ( + SELECT qb.b AS a + FROM ( + select 1*t4.b as b, 1*t4.a as a from t4 + ) qb + GROUP BY qb.a +) dt WHERE (dt.a=2); + +--source include/explain-no-costs.inc +explain format=json select * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ DERIVED_CONDITION_PUSHDOWN(dt)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(du)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ DERIVED_CONDITION_PUSHDOWN(du)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +--source include/explain-no-costs.inc +explain format=json select /*+ DERIVED_CONDITION_PUSHDOWN(dt,du)*/ * from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); +select /*+ DERIVED_CONDITION_PUSHDOWN(dt,du)*/ count(*) from ( + select t3.b as a from t3 group by t3.a +) dt join ( + select t3.a as a, t3.b as b from t3 where t3.a % 2 = 0 group by t3.b +) du where (dt.a=3 or dt.a=5) and (du.a % 10 <> 0 or du.a % 5 <> 0); + +set optimizer_switch=@save_optimizer_switch; +drop view v1, v2; +drop table t1, t2, t3, t4, t5; +--enable_view_protocol +--enable_ps_protocol diff --git a/mysql-test/main/mdev-36106-merge-hint.result b/mysql-test/main/mdev-36106-merge-hint.result new file mode 100644 index 0000000000000..77841da95ae85 --- /dev/null +++ b/mysql-test/main/mdev-36106-merge-hint.result @@ -0,0 +1,476 @@ +create table t1 select seq as i, 10*seq as j from seq_1_to_10; +create view v1 as select * from t1 where i % 2 = 0; +set @save_optimizer_switch=@@optimizer_switch; +set session optimizer_switch='derived_merge=on'; +explain format=json SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i < 3 and t1.j > 8" + } + } + ] + } +} +SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +a b +1 10 +2 20 +explain format=json SELECT /*+ NO_MERGE(dt) */ a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a < 3 and dt.b > 8", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i < 3 and t1.j > 8" + } + } + ] + } + } + } + } + ] + } +} +SELECT /*+ NO_MERGE(dt) */ a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +a b +1 10 +2 20 +explain format=json select * from (select i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.j MOD 5 = 0 and t1.i MOD 2 = 0" + } + } + ] + } +} +select * from (select i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select /*+ NO_MERGE(dt) */ * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.j MOD 5 = 0", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i MOD 2 = 0 and t1.j MOD 5 = 0" + } + } + ] + } + } + } + } + ] + } +} +select /*+ NO_MERGE(dt) */ * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.j MOD 5 = 0 and t1.i MOD 2 = 0" + } + } + ] + } +} +select * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select /*+ NO_MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.j MOD 5 = 0", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i MOD 2 = 0 and t1.j MOD 5 = 0" + } + } + ] + } + } + } + } + ] + } +} +select /*+ NO_MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +set session optimizer_switch='derived_merge=off'; +explain format=json SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.a < 3 and dt.b > 8", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i < 3 and t1.j > 8" + } + } + ] + } + } + } + } + ] + } +} +SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +a b +1 10 +2 20 +explain format=json select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.j MOD 5 = 0 and t1.i MOD 2 = 0" + } + } + ] + } +} +select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select * from (select i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.j MOD 5 = 0", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i MOD 2 = 0 and t1.j MOD 5 = 0" + } + } + ] + } + } + } + } + ] + } +} +select * from (select i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select /*+ MERGE(dt) */ * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.j MOD 5 = 0 and t1.i MOD 2 = 0" + } + } + ] + } +} +select /*+ MERGE(dt) */ * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "dt.j MOD 5 = 0", + "materialized": { + "query_block": { + "select_id": 2, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.i MOD 2 = 0 and t1.j MOD 5 = 0" + } + } + ] + } + } + } + } + ] + } +} +select * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +explain format=json select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "cost": "COST_REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "t1", + "access_type": "ALL", + "loops": 1, + "rows": 10, + "cost": "COST_REPLACED", + "filtered": 100, + "attached_condition": "t1.j MOD 5 = 0 and t1.i MOD 2 = 0" + } + } + ] + } +} +select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +i j +2 20 +4 40 +6 60 +8 80 +10 100 +set optimizer_switch=@save_optimizer_switch; +explain extended select /*+ NO_MERGE(t) */ * from (select * from t1) t; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 10 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 10 100.00 +Warnings: +Note 1003 /* select#1 */ select /*+ NO_MERGE(`t`@`select#1`) */ `t`.`i` AS `i`,`t`.`j` AS `j` from (/* select#2 */ select `test`.`t1`.`i` AS `i`,`test`.`t1`.`j` AS `j` from `test`.`t1`) `t` +explain extended select /*+ MERGE(t) */ * from (select * from t1) t; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 100.00 +Warnings: +Note 1003 select /*+ MERGE(`t`@`select#1`) */ `test`.`t1`.`i` AS `i`,`test`.`t1`.`j` AS `j` from `test`.`t1` +create table t2 select seq as i, 10*seq as j from seq_1_to_10; +create view v2 as select * from t2; +set session optimizer_switch='derived_merge=off'; +explain extended +SELECT /*+ merge(wrong_name) */a, b FROM +(SELECT /*+ merge(wrong_name) */ i as a, j as b FROM +(select i*10 as i, j*5 as j from v2) dt_in) AS dt_out +WHERE a < 3 AND b > 8; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY <derived2> ALL NULL NULL NULL NULL 10 100.00 Using where +2 DERIVED <derived3> ALL NULL NULL NULL NULL 10 100.00 Using where +3 DERIVED t2 ALL NULL NULL NULL NULL 10 100.00 Using where +Warnings: +Warning 4209 Unresolved table name `wrong_name`@`select#2` for MERGE hint +Warning 4209 Unresolved table name `wrong_name`@`select#1` for MERGE hint +Note 1003 /* select#1 */ select `dt_out`.`a` AS `a`,`dt_out`.`b` AS `b` from (/* select#2 */ select `dt_in`.`i` AS `a`,`dt_in`.`j` AS `b` from (/* select#3 */ select `test`.`t2`.`i` * 10 AS `i`,`test`.`t2`.`j` * 5 AS `j` from `test`.`t2` where `test`.`t2`.`i` * 10 < 3 and `test`.`t2`.`j` * 5 > 8) `dt_in` where `dt_in`.`i` < 3 and `dt_in`.`j` > 8) `dt_out` where `dt_out`.`a` < 3 and `dt_out`.`b` > 8 +drop table t1, t2; +drop view v1, v2; diff --git a/mysql-test/main/mdev-36106-merge-hint.test b/mysql-test/main/mdev-36106-merge-hint.test new file mode 100644 index 0000000000000..210cccbb08764 --- /dev/null +++ b/mysql-test/main/mdev-36106-merge-hint.test @@ -0,0 +1,80 @@ +--source include/have_sequence.inc +--disable_ps_protocol +--disable_view_protocol +create table t1 select seq as i, 10*seq as j from seq_1_to_10; +create view v1 as select * from t1 where i % 2 = 0; + +set @save_optimizer_switch=@@optimizer_switch; + +set session optimizer_switch='derived_merge=on'; + +--source include/explain-no-costs.inc +explain format=json SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; + +--source include/explain-no-costs.inc +explain format=json SELECT /*+ NO_MERGE(dt) */ a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +SELECT /*+ NO_MERGE(dt) */ a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; + +--source include/explain-no-costs.inc +explain format=json select * from (select i, j from v1) dt where dt.j % 5 = 0; +select * from (select i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_MERGE(dt) */ * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +select /*+ NO_MERGE(dt) */ * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +select * from (select /*+ NO_MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select /*+ NO_MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +select /*+ NO_MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; + +set session optimizer_switch='derived_merge=off'; + +--source include/explain-no-costs.inc +explain format=json SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; +SELECT a, b FROM (SELECT i as a, j as b FROM t1) AS dt WHERE a < 3 AND b > 8; + +--source include/explain-no-costs.inc +explain format=json select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select * from (select i, j from v1) dt where dt.j % 5 = 0; +select * from (select i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select /*+ MERGE(dt) */ * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +select /*+ MERGE(dt) */ * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; +select * from (select /*+ MERGE(v1) */ i, j from v1) dt where dt.j % 5 = 0; + +--source include/explain-no-costs.inc +explain format=json select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; +select /*+ MERGE(dt) */ * from (select i, j from v1) dt where dt.j % 5 = 0; + +set optimizer_switch=@save_optimizer_switch; + +# Test warnings +--enable_warnings +explain extended select /*+ NO_MERGE(t) */ * from (select * from t1) t; +explain extended select /*+ MERGE(t) */ * from (select * from t1) t; +create table t2 select seq as i, 10*seq as j from seq_1_to_10; +create view v2 as select * from t2; +set session optimizer_switch='derived_merge=off'; +explain extended + SELECT /*+ merge(wrong_name) */a, b FROM + (SELECT /*+ merge(wrong_name) */ i as a, j as b FROM + (select i*10 as i, j*5 as j from v2) dt_in) AS dt_out + WHERE a < 3 AND b > 8; +--disable_warnings + +drop table t1, t2; +drop view v1, v2; +--enable_view_protocol +--enable_ps_protocol diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc index 097983feb7b79..fade11cab14f8 100644 --- a/sql/opt_hints.cc +++ b/sql/opt_hints.cc @@ -41,6 +41,8 @@ struct st_opt_hint_info opt_hint_info[]= {{STRING_WITH_LEN("MAX_EXECUTION_TIME")}, false, true}, {{STRING_WITH_LEN("SEMIJOIN")}, false, true}, {{STRING_WITH_LEN("SUBQUERY")}, false, true}, + {{STRING_WITH_LEN("DERIVED_CONDITION_PUSHDOWN")}, false, false}, + {{STRING_WITH_LEN("MERGE")}, false, false}, {null_clex_str, 0, 0} }; @@ -177,7 +179,13 @@ static Opt_hints_qb *get_qb_hints(Parse_context *pc) /* Mark the query block as resolved as we know which SELECT_LEX it is attached to. + Note that children (indexes, tables) are probably not resolved, yet. + + Note also that for early hints (like [NO_]MERGE), there is no TABLE* + instance available, so later hint resolution will update the fixed + value accordingly, to allow for delayed hint resolution to the time + when the TABLE* instance is available. */ qb->set_fixed(); } @@ -369,6 +377,46 @@ Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg, } +void Opt_hints_qb::fix_hints_for_table(TABLE_LIST *table_list) +{ + Opt_hints_table *tab= static_cast<Opt_hints_table *>(find_by_name(table_list->alias)); + + /* + This instance will have been marked as fixed on the basis of its + attachment to a SELECT_LEX (during get_qb_hints) but that is + insufficient to consider it fixed for the case where a TABLE + instance is required but not yet available. If the associated + table isn't yet fixed, then reset this instance's fixed value to + DELAYED. Later, during fix_hints_for_table(TABLE*), the hint and + its corresponding TABLE instance will be marked with fixed=FIXED. + */ + if (is_fixed()) + { + if (tab && !tab->is_fixed()) + { + DBUG_ASSERT(!table_list->opt_hints_qb); + DBUG_ASSERT(!table_list->opt_hints_table); + table_list->opt_hints_qb= this; + table_list->opt_hints_table= tab; + set_fixed(Opt_hints::Fixed_state::DELAYED); + } + + return; + } + + table_list->opt_hints_qb= this; + table_list->opt_hints_table= tab; + + /* + What's delayed here is tab->fix_hint(table) as we don't have + a TABLE instance to call fix_hint with. That will be done later, + during setup_tables, when a TABLE instance is available and calls + fix_hints_for_table(TABLE*). + */ + set_fixed(Opt_hints::Fixed_state::DELAYED); +} + + Opt_hints_table *Opt_hints_qb::fix_hints_for_table(TABLE *table, const Lex_ident_table &alias) { @@ -382,6 +430,15 @@ Opt_hints_table *Opt_hints_qb::fix_hints_for_table(TABLE *table, if (!tab->fix_hint(table)) incr_fully_fixed_children(); + /* + If this was a delayed hint, mark it as fixed now that we were able + to fix it with the TABLE instance in hand. + */ + if (is_delayed()) { + set_fixed(); + incr_fully_fixed_children(); + } + return tab; } @@ -542,11 +599,9 @@ bool hint_key_state(const THD *thd, const TABLE *table, } -bool hint_table_state(const THD *thd, const TABLE *table, - opt_hints_enum type_arg, - bool fallback_value) +bool hint_table_state(const THD *thd, const TABLE_LIST *table_list, + opt_hints_enum type_arg, bool fallback_value) { - TABLE_LIST *table_list= table->pos_in_table_list; if (table_list->opt_hints_qb) { bool ret_val= false; @@ -559,6 +614,15 @@ bool hint_table_state(const THD *thd, const TABLE *table, return fallback_value; } + +bool hint_table_state(const THD *thd, const TABLE *table, + opt_hints_enum type_arg, + bool fallback_value) +{ + return hint_table_state(thd, table->pos_in_table_list, type_arg, + fallback_value); +} + /* Resolve a parsed table level hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. @@ -592,6 +656,22 @@ bool Parser::Table_level_hint::resolve(Parse_context *pc) const hint_type= BKA_HINT_ENUM; hint_state= false; break; + case TokenID::keyword_DERIVED_CONDITION_PUSHDOWN: + hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_DERIVED_CONDITION_PUSHDOWN: + hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_MERGE: + hint_type= MERGE_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_MERGE: + hint_type= MERGE_HINT_ENUM; + hint_state= false; + break; default: DBUG_ASSERT(0); return true; diff --git a/sql/opt_hints.h b/sql/opt_hints.h index d998da0d1a629..c4bffeda6173e 100644 --- a/sql/opt_hints.h +++ b/sql/opt_hints.h @@ -49,6 +49,11 @@ each table, as a result TABLE_LIST::opt_hints_table points to the table's hints. + Hints may be fixed before TABLE instances are available, using a + TABLE_LIST instance. In that case, the hints are DELAYED and must be + fixed later after their TABLE instances have been created. This should + be done automatically during name resolution as described above. + == Hint hierarchy == Hints have this hierarchy, less specific to more specific: @@ -106,6 +111,8 @@ enum opt_hints_enum MAX_EXEC_TIME_HINT_ENUM, SEMIJOIN_HINT_ENUM, SUBQUERY_HINT_ENUM, + DERIVED_CONDITION_PUSHDOWN_HINT_ENUM, + MERGE_HINT_ENUM, MAX_HINT_ENUM // This one must be the last in the list }; @@ -205,6 +212,14 @@ class Opt_hints : public Sql_alloc for key level. */ Lex_ident_sys name; + + /* + Hints by default are NOT_FIXED. Hints which must be + created before a TABLE instance exists are DELAYED. + When hints are fixed, during hint resolution, they + transition from NOT_FIXED or DELAYED to FIXED. + */ + enum class Fixed_state { NOT_FIXED, FIXED, DELAYED }; private: /* Parent object. There is no parent for global level, @@ -220,8 +235,8 @@ class Opt_hints : public Sql_alloc /* Array of child objects. i.e. array of the lower level objects */ Mem_root_array<Opt_hints*, true> child_array; - /* true if hint is connected to the real object */ - bool fixed; + /* FIXED if hint is connected to the real object (see above) */ + Fixed_state fixed; /* Number of child hints that are fully fixed, that is, fixed and @@ -235,7 +250,7 @@ class Opt_hints : public Sql_alloc Opt_hints *parent_arg, MEM_ROOT *mem_root_arg) : name(name_arg), parent(parent_arg), child_array(mem_root_arg), - fixed(false), n_fully_fixed_children(0) + fixed(Fixed_state::NOT_FIXED), n_fully_fixed_children(0) { } bool is_specified(opt_hints_enum type_arg) const @@ -287,8 +302,9 @@ class Opt_hints : public Sql_alloc } void set_name(const Lex_ident_sys &name_arg) { name= name_arg; } Opt_hints *get_parent() const { return parent; } - void set_fixed() { fixed= true; } - bool is_fixed() const { return fixed; } + void set_fixed(Fixed_state fv=Fixed_state::FIXED) { fixed= fv; } + bool is_fixed() const { return fixed == Fixed_state::FIXED; } + bool is_delayed() const { return fixed == Fixed_state::DELAYED; } void incr_fully_fixed_children() { n_fully_fixed_children++; } Mem_root_array<Opt_hints*, true> *child_array_ptr() { return &child_array; } @@ -464,6 +480,15 @@ class Opt_hints_qb : public Opt_hints DBUG_ASSERT(0); } + /** + Function finds Opt_hints_table object corresponding to + table alias in the query block and attaches corresponding + key hint objects to appropriate KEY structures. + + @param table_list Pointer to TABLE_LIST object + */ + void fix_hints_for_table(TABLE_LIST *table_list); + /** Function finds Opt_hints_table object corresponding to table alias in the query block and attaches corresponding @@ -630,6 +655,20 @@ bool hint_key_state(const THD *thd, const TABLE *table, uint keyno, opt_hints_enum type_arg, uint optimizer_switch); +/** + Returns table hint value if hint is specified, returns + fallback value if hint is not specified. + + @param thd Pointer to THD object + @param table_lsit Pointer to TABLE_LIST object + @param type_arg Hint type + @param fallback_value Value to be returned if the hint is not set + + @return table hint value if hint is specified, + otherwise fallback value. +*/ +bool hint_table_state(const THD *thd, const TABLE_LIST *table_list, + opt_hints_enum type_arg, bool fallback_value); /** Returns table hint value if hint is specified, returns diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc index 31b0d08ac7c83..7b0e1f0f29017 100644 --- a/sql/opt_hints_parser.cc +++ b/sql/opt_hints_parser.cc @@ -45,6 +45,10 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) if ("MRR"_Lex_ident_column.streq(str)) return TokenID::keyword_MRR; break; + case 5: + if ("MERGE"_Lex_ident_column.streq(str)) return TokenID::keyword_MERGE; + break; + case 6: if ("NO_BKA"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_BKA; if ("NO_BNL"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_BNL; @@ -62,6 +66,8 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) return TokenID::keyword_SEMIJOIN; else if ("SUBQUERY"_Lex_ident_column.streq(str)) return TokenID::keyword_SUBQUERY; + else if ("NO_MERGE"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_MERGE; break; case 9: @@ -97,6 +103,16 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) if ("NO_RANGE_OPTIMIZATION"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_RANGE_OPTIMIZATION; break; + + case 26: + if ("DERIVED_CONDITION_PUSHDOWN"_Lex_ident_column.streq(str)) + return TokenID::keyword_DERIVED_CONDITION_PUSHDOWN; + break; + + case 29: + if ("NO_DERIVED_CONDITION_PUSHDOWN"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_DERIVED_CONDITION_PUSHDOWN; + break; } if (str.length > 0 && (str.str[0] >= '0' && str.str[0] <= '9')) diff --git a/sql/opt_hints_parser.h b/sql/opt_hints_parser.h index 42aba17912621..53f1863188b8c 100644 --- a/sql/opt_hints_parser.h +++ b/sql/opt_hints_parser.h @@ -85,7 +85,11 @@ class Optimizer_hint_tokenizer: public Extended_string_tokenizer keyword_FIRSTMATCH, keyword_LOOSESCAN, keyword_DUPSWEEDOUT, - keyword_INTOEXISTS + keyword_INTOEXISTS, + keyword_DERIVED_CONDITION_PUSHDOWN, + keyword_NO_DERIVED_CONDITION_PUSHDOWN, + keyword_MERGE, + keyword_NO_MERGE }; class Token: public Lex_cstring @@ -336,7 +340,11 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer, return id == TokenID::keyword_BKA || id == TokenID::keyword_BNL || id == TokenID::keyword_NO_BKA || - id == TokenID::keyword_NO_BNL; + id == TokenID::keyword_NO_BNL || + id == TokenID::keyword_DERIVED_CONDITION_PUSHDOWN || + id == TokenID::keyword_NO_DERIVED_CONDITION_PUSHDOWN || + id == TokenID::keyword_MERGE || + id == TokenID::keyword_NO_MERGE; } }; class Table_level_hint_type: public TokenChoice<Parser, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index cf2baf10263bb..d390532f75403 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8296,8 +8296,11 @@ bool setup_tables(THD *thd, Name_resolution_context *context, DBUG_RETURN(1); } - if (qb_hints && // QB hints initialized - !table_list->opt_hints_table) // Table hints are not adjusted yet + // This is invoked for every table, so we promote DELAYED to FIXED + // here. + if (qb_hints && // QB hints initialized + (!table_list->opt_hints_table || // Table hints not yet adjusted... + qb_hints->is_delayed())) // ...or hints were delayed. { table_list->opt_hints_table= qb_hints->fix_hints_for_table(table_list->table, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 76239050a09eb..d39274b61bdc7 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2490,43 +2490,42 @@ JOIN::optimize_inner() DBUG_RETURN(TRUE); } - if (optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)) + TABLE_LIST *tbl; + List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); + while ((tbl= li++)) { - TABLE_LIST *tbl; - List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables); - while ((tbl= li++)) + const bool is_derived_pushdown_allowed= hint_table_state( + thd, tbl->table, DERIVED_CONDITION_PUSHDOWN_HINT_ENUM, + optimizer_flag(thd, OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED)); + if (!is_derived_pushdown_allowed) { - /* + /* Run optimize phase on this derived table/view. */ + if (tbl->is_view_or_derived() && + tbl->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + continue; + } + + if (tbl->is_materialized_derived()) + { + JOIN *join= tbl->get_unit()->first_select()->join; + if (join && + join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE && + join->with_two_phase_optimization) + continue; + /* Do not push conditions from where into materialized inner tables of outer joins: this is not valid. */ - if (tbl->is_materialized_derived()) + if (!tbl->is_inner_table_of_outer_join()) { - JOIN *join= tbl->get_unit()->first_select()->join; - if (join && - join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE && - join->with_two_phase_optimization) - continue; - /* - Do not push conditions from where into materialized inner tables - of outer joins: this is not valid. - */ - if (!tbl->is_inner_table_of_outer_join()) - { - if (pushdown_cond_for_derived(thd, conds, tbl)) - DBUG_RETURN(1); - } - if (mysql_handle_single_derived(thd->lex, tbl, DT_OPTIMIZE)) - DBUG_RETURN(1); + if (pushdown_cond_for_derived(thd, conds, tbl)) + DBUG_RETURN(1); } + if (mysql_handle_single_derived(thd->lex, tbl, DT_OPTIMIZE)) + DBUG_RETURN(1); } } - else - { - /* Run optimize phase for all derived tables/views used in this SELECT. */ - if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) - DBUG_RETURN(1); - } { if (select_lex->where) { diff --git a/sql/table.cc b/sql/table.cc index 034e11427fe3b..ad1f433311eff 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -57,6 +57,7 @@ #endif #include "log_event.h" // MAX_TABLE_MAP_ID #include "sql_class.h" +#include "opt_hints.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -10069,6 +10070,15 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) bool forced_no_merge_for_update_delete= belong_to_view ? belong_to_view->updating : !unit->outer_select()->outer_select(); + + if (select_lex->opt_hints_qb && // QB hints initialized + !this->opt_hints_table) // Table hints are not adjusted yet + select_lex->opt_hints_qb->fix_hints_for_table(this); + + bool is_derived_merge_allowed= + hint_table_state(thd, this, MERGE_HINT_ENUM, + optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)); + if (!is_materialized_derived() && unit->can_be_merged() && /* Following is special case of @@ -10085,7 +10095,7 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) (!first_select->group_list.elements && !first_select->order_list.elements)) && (is_view() || - optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE)) && + is_derived_merge_allowed) && !thd->lex->can_not_use_merged() && !(!is_view() && forced_no_merge_for_update_delete && (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||