Skip to content
/ server Public

Draft: MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks#4700

Open
Olernov wants to merge 3 commits intomainfrom
13.0-MDEV-38045-qb-name-path
Open

Draft: MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks#4700
Olernov wants to merge 3 commits intomainfrom
13.0-MDEV-38045-qb-name-path

Conversation

@Olernov
Copy link
Contributor

@Olernov Olernov commented Feb 26, 2026

Extended QB_NAME hint to support path-based addressing of query blocks
nested within views, derived tables, and CTEs, following TiDB's syntax.

New syntax:

  QB_NAME(name, query_block_path), where
    query_block_path ::= query_block_path_element
                          [ {, query_block_path_element }... ]
    query_block_path_element ::= @ qb_path_element_select_num |
                                    qb_path_element_view_sel
    qb_path_element_view_sel ::= qb_path_element_view_name
                                  [ @ qb_path_element_select_num ]

For example,
SELECT /*+ qb_name(qb_v1, v1) */* FROM v1
The name qb_v1 is assigned to the inner query block of the view v1.

SELECT /*+ qb_name(qb_v1, v1@sel_1) */* FROM v1
Means the same but specifies that v1 is present in SELECT#1 of the current
query block.

SELECT /*+ qb_name(qb_v1, v1@sel_1 .@sel_2) */* FROM v1
This means SELECT#2 of view v1, which is present in SELECT#1 of
the current query block, gets the name qb_v1.

It is possible to specify not only view names but also derived tables
and CTE's in the path.

Views and derived tables may be nested on multiple levels, for example:
SELECT /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2) no_index(t1@dt2_dt1_v1_1)*/ v1.* FROM v1 JOIN (SELECT v1.* FROM v1 JOIN (SELECT * FROM v2) dt2) dt1

Limitations:

  • Only SELECT statements support QB names with path. DML operations
    (UPDATE, DELETE, INSERT) only support explicitly defined QB names

1. `TABLE_LIST::init_derived` is called twice during derived tables
processing: first time from `mysql_derived_init` and the second
time from `mysql_derived_prepare`. Both times there is a check
for whether an optimizer hint or switch setting allows a derived
table to be merged.
However, it is not necessary to restrict merging during the
initialization call, it is enough to apply hints/switch setting
during the preparation phase.

2. `open_normal_and_derived_tables()` runs processing of all phases
of derived tables handling with a single call. But for future
commits it is required to separate DT initialization from other
phases. This commit implements the separation.
This patch implements support for implicit query block (QB) names in
optimizer hints, allowing hints to reference query blocks and tables
within derived tables, views and CTEs without requiring explicit
QB_NAME hints.

Examples.
-- Addressing a table inside a derived table using implicit QB name
select /*+ no_index(t1@dt) */ *
  from (select * from t1 where a > 10) as DT;
-- this is an equivalent to:
select /*+ no_index(t1@dt) */ * from
  (select /*+ qb_name(dt)*/ * from t1 where a > 10) as DT;
-- Addressing a query block corresponding to the derived table
select /*+ no_bnl(@dt) */ *
  from (select * from t1, t2 where t.1.a > t2.a) as DT;

-- View
create view v1 as select * from t1 where a > 10 and b > 100;
-- referencing a table inside a view by implicit QB name:
select /*+ index_merge(t1@v1 idx_a, idx_b) */ *
  from v1, t2 where v1.a = t2.a;
-- equivalent to:
create view v1 as select /*+ qb_name(qb_v1) */ *
  from t1 where a > 10 and b > 100;
select /*+ index_merge(t1@qb_v1 idx_a, idx_b) */ *
  from v1, t2 where v1.a = t2.a;

-- CTE
with aless100 as (select a from t1 where b <100)
  select /*+ index(t1@aless100) */ * from aless100;
-- equivalent to:
with aless100 as (select /*+ qb_name(aless100) */ a from t1 where b <100)
  select /*+ index(t1@aless100) */ * from aless100;

Limitations:
  - Only SELECT statements support implicit QB names. DML operations
    (UPDATE, DELETE, INSERT) only support explicit QB names
@Olernov
Copy link
Contributor Author

Olernov commented Feb 26, 2026

This PR is based on #4692 that is why three commits are displayed. Actually, only the latest commit represent the feature.

Copy link
Member

@DaveGosselin-MariaDB DaveGosselin-MariaDB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impressive test cases and documentation. Found just a couple places where we may be able to improve test coverage.

}

const Lex_ident_sys qb_name_sys= Query_block_name::to_ident_sys(pc->thd);
uint hint_select_num= atoi(select_num_str.str + format.length);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for hint_select_num to be zero?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically yes. This will generate standard warning Hint QB_NAME(... @SEL_0) is ignored. Check whether the path to the query block is correct

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One can do various mischief like specifying SEL_9999999999999999999999 which causes
hint_select_num=0xffffffff which then can overflow.

Also man page says "POSIX.1 leaves the return value of atoi() on error unspecified".

Would it be better to use system_charset_info->strtoll10 and check for error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, rewrote the code with my_strtoll10 and added checks for conversion errors and valid range.

@Olernov Olernov force-pushed the 13.0-MDEV-38045-qb-name-path branch from 30a82bb to d8e4118 Compare February 28, 2026 16:27
@spetrunia
Copy link
Member

What I understood looks good so far.

A request about error messages: can they be more specific? At the moment one gets:

Warning (Code 4258): Hint QB_NAME(`n1` , `v1`@`SEL_1` .@`SEL_3333`) is ignored. Check whether the path to the query block is correct

or

Warning (Code 4258): Hint QB_NAME(`n1` , `v1000`@`SEL_1` .@`SEL_3`) is ignored. Check whether the path to the query block is correct

Can those instead be:

Couldn't find select SEL_3333 at step 2 of hint QB_NAME(`n1` , `v1`@`SEL_1` .@`SEL_3333`). Hint will be ignored.

and

Couldn't find table v1000 at step 1 of hint QB_NAME(`n1` , `v1000`@`SEL_1` .@`SEL_3`). Hint will be ignored.

As far as I understand, the information for producing better warnings is easily available in Parser::Qb_name_hint::resolve.
You may need to introduce two warnings instead of one ER_WARN_WRONG_PATH_IN_QB_NAME.

Tables and views used in the above: https://gist.github.com/spetrunia/21e4c2b17c449ea1b597bc4672cdaed0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use doxygen style formatting here.

Please also mention in@detail section the summary:
If the hint has form QB_NAME(new_name, path), locate the SELECT pointed by the path and set its opt_hints_qb->name to the new_name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doxygen only here or across the whole opt_hints_parser.cc? Other functions also have non-doxygen style description

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, re-formatted in a more doxygen-like style.

@Olernov
Copy link
Contributor Author

Olernov commented Mar 5, 2026

What I understood looks good so far.

A request about error messages: can they be more specific? At the moment one gets:

Warning (Code 4258): Hint QB_NAME(`n1` , `v1`@`SEL_1` .@`SEL_3333`) is ignored. Check whether the path to the query block is correct

or

Warning (Code 4258): Hint QB_NAME(`n1` , `v1000`@`SEL_1` .@`SEL_3`) is ignored. Check whether the path to the query block is correct

Can those instead be:

Couldn't find select SEL_3333 at step 2 of hint QB_NAME(`n1` , `v1`@`SEL_1` .@`SEL_3333`). Hint will be ignored.

and

Couldn't find table v1000 at step 1 of hint QB_NAME(`n1` , `v1000`@`SEL_1` .@`SEL_3`). Hint will be ignored.

As far as I understand, the information for producing better warnings is easily available in Parser::Qb_name_hint::resolve. You may need to introduce two warnings instead of one ER_WARN_WRONG_PATH_IN_QB_NAME.

Tables and views used in the above: https://gist.github.com/spetrunia/21e4c2b17c449ea1b597bc4672cdaed0

Good idea, see the new commit expanding the warnings text.

@Olernov Olernov requested a review from spetrunia March 5, 2026 14:33
@Olernov Olernov force-pushed the 13.0-MDEV-38045-qb-name-path branch from ca7043a to 3668eef Compare March 5, 2026 14:45
…blocks

Extended QB_NAME hint to support path-based addressing of query blocks
nested within views, derived tables, and CTEs, following TiDB's syntax.

New syntax:
  QB_NAME(name, query_block_path), where
    query_block_path ::= query_block_path_element
                          [ {. query_block_path_element }... ]
    query_block_path_element ::= @ qb_path_element_select_num |
                                    qb_path_element_view_sel
    qb_path_element_view_sel ::= qb_path_element_view_name
                                  [ @ qb_path_element_select_num ]

For example,
  `SELECT /*+ qb_name(qb_v1, v1) */* FROM v1`
The name `qb_v1` is assigned to the inner query block of the view `v1`.

  `SELECT /*+ qb_name(qb_v1, v1@sel_1) */* FROM v1`
Means the same but specifies that `v1` is present in SELECT#1 of the current
query block.

  `SELECT /*+ qb_name(qb_v1, v1@sel_1 .@sel_2) */* FROM v1`
This means SELECT#2 of view `v1`, which is present in SELECT#1 of
the current query block, gets the name `qb_v1`.

It is possible to specify not only view names but also derived tables
and CTE's in the path.

Views and derived tables may be nested on multiple levels, for example:
  `SELECT /*+ qb_name(dt2_dt1_v1_1, dt1 .dt2 .v2 .@SEL_2)
              no_index(t1@dt2_dt1_v1_1)*/ v1.*
  FROM v1 JOIN (SELECT v1.* FROM v1 JOIN (SELECT * FROM v2) dt2) dt1`

Limitations:
  - Only SELECT statements support QB names with path. DML operations
    (UPDATE, DELETE, INSERT) only support explicitly defined QB names
@Olernov Olernov force-pushed the 13.0-MDEV-38045-qb-name-path branch from 3668eef to c1bf045 Compare March 10, 2026 13:43
@Olernov Olernov changed the title MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks Draft: MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants