diff --git a/mysql-test/main/opt_hints_comments.result b/mysql-test/main/opt_hints_comments.result new file mode 100644 index 0000000000000..a677c40fd2e8f --- /dev/null +++ b/mysql-test/main/opt_hints_comments.result @@ -0,0 +1,115 @@ +# MDEV-41606 Comments in new-style optimizer hints +CREATE TABLE t1 (a INT, KEY(a)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (1,10),(2,20),(3,30); +# Comments before, between and after hints; commented hints are ignored +EXPLAIN EXTENDED +SELECT /*+ +-- NO_INDEX(t1) +QB_NAME(qb)-- name the query block +-- INDEX(t1 a) +NO_INDEX(t1) -- do not use the index +*/ a FROM t1 WHERE a= 2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ QB_NAME(`qb`) NO_INDEX(`t1`@`qb`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +# Comments inside hint arguments +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(-- query block name +qb) NO_INDEX(-- table name +t1@qb -- index name +a) */ a FROM t1 WHERE a= 2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ QB_NAME(`qb`) NO_INDEX(`t1`@`qb` `a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +# Query block paths and join order from the report +EXPLAIN EXTENDED +SELECT /*+ +QB_NAME(child_select, derived1.derived2) -- name the nested block +-- JOIN_ORDER(@child_select t1, t2) +JOIN_ORDER(@child_select t2, t1) -- reverse the join order +*/ * FROM +(SELECT * FROM +(SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a= t2.a LIMIT 10) +AS derived2) +AS derived1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 +3 DERIVED t2 ALL NULL NULL NULL NULL 3 100.00 Using where +3 DERIVED t1 ref a a 5 test.t2.a 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ JOIN_ORDER(@`child_select` `t2`,`t1`) */ `derived2`.`a` AS `a`,`derived2`.`b` AS `b` from (/* select#3 */ select /*+ QB_NAME(`child_select`) */ `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`a` = `test`.`t2`.`a` limit 10) `derived2` +# A quoted identifier can contain a comment marker +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(`-- qb`) NO_INDEX(t1@`-- qb`) */ a +FROM t1 WHERE a= 2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ QB_NAME(`-- qb`) NO_INDEX(`t1`@`-- qb`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +EXPLAIN EXTENDED +SELECT /*+ QB_NAME("-- qb") NO_INDEX(t1@"-- qb") */ a +FROM t1 WHERE a= 2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ QB_NAME(`-- qb`) NO_INDEX(`t1`@`-- qb`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +# A line comment can run up to the end of the hint +EXPLAIN EXTENDED +SELECT /*+ NO_INDEX(t1) -- INDEX(t1 a) */ a FROM t1 WHERE a= 2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +# Comment-only hints retain the existing empty-hint warning +SELECT /*+ */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1 +SELECT /*+ -- QB_NAME(ignored) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1 +SELECT /*+ --*/ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1 +# Tabs after the marker and CRLF line endings +SET @stmt= CONCAT('EXPLAIN EXTENDED SELECT /*+ --', CHAR(9), 'ignored', +CHAR(13), CHAR(10), +'NO_INDEX(t1) */ a FROM t1 WHERE a= 2'); +PREPARE stmt FROM @stmt; +EXECUTE stmt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 2 +DEALLOCATE PREPARE stmt; +# Whitespace is required after the two dashes +SELECT /*+ --not_a_comment */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '--not_a_comment */ 1' at line 1 +SELECT /*+ - */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '- */ 1' at line 1 +# Syntax errors after a comment retain their line number +SELECT /*+ +-- ignored text +NOT_A_HINT() +*/ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'NOT_A_HINT() +*/ 1' at line 3 +DROP TABLE t1, t2; diff --git a/mysql-test/main/opt_hints_comments.test b/mysql-test/main/opt_hints_comments.test new file mode 100644 index 0000000000000..9a65bb54448ae --- /dev/null +++ b/mysql-test/main/opt_hints_comments.test @@ -0,0 +1,76 @@ +--echo # MDEV-41606 Comments in new-style optimizer hints +--enable_prepare_warnings + +CREATE TABLE t1 (a INT, KEY(a)); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t1 VALUES (1),(2),(3); +INSERT INTO t2 VALUES (1,10),(2,20),(3,30); + +--echo # Comments before, between and after hints; commented hints are ignored +EXPLAIN EXTENDED +SELECT /*+ + -- NO_INDEX(t1) + QB_NAME(qb)-- name the query block + -- INDEX(t1 a) + NO_INDEX(t1) -- do not use the index +*/ a FROM t1 WHERE a= 2; + +--echo # Comments inside hint arguments +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(-- query block name + qb) NO_INDEX(-- table name + t1@qb -- index name + a) */ a FROM t1 WHERE a= 2; + +--echo # Query block paths and join order from the report +EXPLAIN EXTENDED +SELECT /*+ + QB_NAME(child_select, derived1.derived2) -- name the nested block + -- JOIN_ORDER(@child_select t1, t2) + JOIN_ORDER(@child_select t2, t1) -- reverse the join order +*/ * FROM + (SELECT * FROM + (SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a= t2.a LIMIT 10) + AS derived2) + AS derived1; + +--echo # A quoted identifier can contain a comment marker +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(`-- qb`) NO_INDEX(t1@`-- qb`) */ a +FROM t1 WHERE a= 2; +EXPLAIN EXTENDED +SELECT /*+ QB_NAME("-- qb") NO_INDEX(t1@"-- qb") */ a +FROM t1 WHERE a= 2; + +--echo # A line comment can run up to the end of the hint +EXPLAIN EXTENDED +SELECT /*+ NO_INDEX(t1) -- INDEX(t1 a) */ a FROM t1 WHERE a= 2; + +--echo # Comment-only hints retain the existing empty-hint warning +SELECT /*+ */ 1; +SELECT /*+ -- QB_NAME(ignored) */ 1; +SELECT /*+ --*/ 1; + +--echo # Tabs after the marker and CRLF line endings +SET @stmt= CONCAT('EXPLAIN EXTENDED SELECT /*+ --', CHAR(9), 'ignored', + CHAR(13), CHAR(10), + 'NO_INDEX(t1) */ a FROM t1 WHERE a= 2'); +PREPARE stmt FROM @stmt; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +--echo # Whitespace is required after the two dashes +--disable_ps_protocol +--disable_view_protocol +SELECT /*+ --not_a_comment */ 1; +SELECT /*+ - */ 1; + +--echo # Syntax errors after a comment retain their line number +SELECT /*+ + -- ignored text + NOT_A_HINT() +*/ 1; +--enable_ps_protocol +--enable_view_protocol + +DROP TABLE t1, t2; diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc index 0d1dad9f5cbf6..9043b13502b7b 100644 --- a/sql/opt_hints_parser.cc +++ b/sql/opt_hints_parser.cc @@ -224,7 +224,18 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) Optimizer_hint_tokenizer::Token Optimizer_hint_tokenizer::get_token(CHARSET_INFO *cs) { - get_spaces(); + for ( ; ; ) + { + get_spaces(); + if (m_end - m_ptr < 2 || m_ptr[0] != '-' || m_ptr[1] != '-') + break; + // Match the SQL lexer's whitespace requirement after --. + if (m_end - m_ptr > 2 && + !my_isspace(cs, m_ptr[2]) && !my_iscntrl(cs, m_ptr[2])) + break; + for (m_ptr+= 2; !eof() && *m_ptr != '\n'; m_ptr++) + { } + } if (eof()) return Token(Lex_cstring(m_ptr, m_ptr), TokenID::tEOF); const char head= m_ptr[0];