Draft: MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks#4700
Draft: MDEV-38045: Implement QB_NAME hint with path syntax for nested query blocks#4700
Conversation
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
|
This PR is based on #4692 that is why three commits are displayed. Actually, only the latest commit represent the feature. |
b98f295 to
30a82bb
Compare
DaveGosselin-MariaDB
left a comment
There was a problem hiding this comment.
Impressive test cases and documentation. Found just a couple places where we may be able to improve test coverage.
sql/opt_hints_parser.cc
Outdated
| } | ||
|
|
||
| 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); |
There was a problem hiding this comment.
Is it possible for hint_select_num to be zero?
There was a problem hiding this comment.
Technically yes. This will generate standard warning Hint QB_NAME(... @SEL_0) is ignored. Check whether the path to the query block is correct
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I agree, rewrote the code with my_strtoll10 and added checks for conversion errors and valid range.
30a82bb to
d8e4118
Compare
|
What I understood looks good so far. A request about error messages: can they be more specific? At the moment one gets: or Can those instead be: and As far as I understand, the information for producing better warnings is easily available in Tables and views used in the above: https://gist.github.com/spetrunia/21e4c2b17c449ea1b597bc4672cdaed0 |
sql/opt_hints_parser.cc
Outdated
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Doxygen only here or across the whole opt_hints_parser.cc? Other functions also have non-doxygen style description
There was a problem hiding this comment.
OK, re-formatted in a more doxygen-like style.
Good idea, see the new commit expanding the warnings text. |
ca7043a to
3668eef
Compare
…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
3668eef to
c1bf045
Compare
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:
For example,
SELECT /*+ qb_name(qb_v1, v1) */* FROM v1The name
qb_v1is assigned to the inner query block of the viewv1.SELECT /*+ qb_name(qb_v1, v1@sel_1) */* FROM v1Means the same but specifies that
v1is present in SELECT#1 of the currentquery block.
SELECT /*+ qb_name(qb_v1, v1@sel_1 .@sel_2) */* FROM v1This means SELECT#2 of view
v1, which is present in SELECT#1 ofthe 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) dt1Limitations:
(UPDATE, DELETE, INSERT) only support explicitly defined QB names