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 ||