Bug #104092 NO_BNL hint not working if query contains a merged view
Submitted: 23 Jun 2021 12:45 Modified: 23 Jun 2021 12:54
Reporter: Guilhem Bichot Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.25 OS:Linux
Assigned to: CPU Architecture:Any

[23 Jun 2021 12:45] Guilhem Bichot
Description:
This hint in SQL queries  /*+ NO_BNL() */
is expected to disable hash join in the query, as explained in
https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html#optimizer-hints-table-level

<quote>
hint_name: These hint names are permitted:
...
BNL, NO_BNL: Enable or disable block nested loop for the specified tables. In MySQL 8.0.18 and later, these hints also enable and disable the hash join optimization.
...
tbl_name: The name of a table used in the statement. The hint applies to all tables that it names. If the hint names no tables, it applies to all tables of the query block in which it occurs.
</quote>

It doesn't work if one of the tables is a merged view:

create table t(a int, b int);
create view v as select 3 from t;
explain format=tree  select /*+ no_bnl() */ * from  t join v on t.a=v.`3`*100;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                            |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Inner hash join (no condition)  (cost=0.70 rows=1)
...
"Hash join" should not be seen here, "Nested loop join" should be. That's the bug.

If I use optimizer_switch="block_nested_loop=off" then it works, I get "nested loop join".

If I replace the view 'v' with a normal table it works too.

How to repeat:
create table t(a int, b int); create view v as select 3 from t;
# you'll see hash join
explain format=tree  select /*+ no_bnl() */ * from  t join v on t.a=v.`3`*100;

Suggested fix:
As the view is merged, what we get in the list of tables, after merging, is table "t"  (initially specified) and merged table "t" (merged from "v").
Then when the optimizer decides to do hash join or not, for each of the two tables it calls:
bool get_hint_table_state(const TABLE_LIST *table_list, opt_hints_enum type_arg,
                          bool *hint_val) {
  if (table_list->opt_hints_qb) {
    bool ret_val = false;
    if (get_hint_state(table_list->opt_hints_table, table_list->opt_hints_qb,
                       type_arg, &ret_val)) {
      *hint_val = ret_val;
      return true;
    }
  }

  return false;
}

Likely, for the merged "t", opt_hints_qb above is not set. Because hint parsing is done before view merging. So, the merged "t" is not prevented from doing hash join. It's only a guess.
[23 Jun 2021 12:54] MySQL Verification Team
Hello Guilhem,

Thank you for the report and test case.

regards,
Umesh