diff --git a/client/mysql.cc b/client/mysql.cc index 727d966fa6584..94c3c6e78cf0c 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -236,6 +236,8 @@ static char **defaults_argv; enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT}; typedef enum enum_info_type INFO_TYPE; +enum ss_comment_type { SSC_NONE= 0, SSC_CONDITIONAL, SSC_HINT }; + static MYSQL mysql; /* The connection */ static my_bool ignore_errors=0,wait_flag=0,quick=0, connected=0,opt_raw_data=0,unbuffered=0,output_tables=0, @@ -1158,7 +1160,8 @@ static void fix_history(String *final_command); static COMMANDS *find_command(char *name); static COMMANDS *find_command(char cmd_name); -static bool add_line(String &, char *, size_t line_length, char *, bool *, bool); +static bool add_line(String &, char *, size_t line_length, char *, + bool *, ss_comment_type *, bool); static void remove_cntrl(String &buffer); static void print_table_data(MYSQL_RES *result); static void print_table_data_html(MYSQL_RES *result); @@ -2211,6 +2214,7 @@ static int read_and_execute(bool interactive) char in_string=0; ulong line_number=0; bool ml_comment= 0; + ss_comment_type ss_comment= SSC_NONE; COMMANDS *com; size_t line_length= 0; status.exit_status=1; @@ -2357,7 +2361,8 @@ static int read_and_execute(bool interactive) #endif continue; } - if (add_line(glob_buffer, line, line_length, &in_string, &ml_comment, + if (add_line(glob_buffer, line, line_length, &in_string, + &ml_comment, &ss_comment, status.line_buff ? status.line_buff->truncated : 0)) break; } @@ -2526,13 +2531,13 @@ static COMMANDS *find_command(char *name) static bool add_line(String &buffer, char *line, size_t line_length, - char *in_string, bool *ml_comment, bool truncated) + char *in_string, bool *ml_comment, + ss_comment_type *ss_comment, bool truncated) { uchar inchar; char buff[80], *pos, *out; COMMANDS *com; bool need_space= 0; - bool ss_comment= 0; DBUG_ENTER("add_line"); if (!line[0] && buffer.is_empty()) @@ -2607,7 +2612,7 @@ static bool add_line(String &buffer, char *line, size_t line_length, DBUG_RETURN(1); // Quit if (com->takes_params) { - if (ss_comment) + if (*ss_comment != SSC_NONE) { /* If a client-side macro appears inside a server-side comment, @@ -2641,7 +2646,8 @@ static bool add_line(String &buffer, char *line, size_t line_length, continue; } } - else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter)) + else if (!*ml_comment && !*in_string && *ss_comment != SSC_HINT && + is_prefix(pos, delimiter)) { // Found a statement. Continue parsing after the delimiter pos+= delimiter_length; @@ -2729,8 +2735,9 @@ static bool add_line(String &buffer, char *line, size_t line_length, break; } - else if (!*in_string && inchar == '/' && *(pos+1) == '*' && - !(*(pos+2) == '!' || (*(pos+2) == 'M' && *(pos+3) == '!'))) + else if (!*in_string && inchar == '/' && pos[1] == '*' && + pos[2] != '!' && !(pos[2] == 'M' && pos[3] == '!') && + pos[2] != '+' && *ss_comment != SSC_HINT) { if (preserve_comments) { @@ -2746,7 +2753,8 @@ static bool add_line(String &buffer, char *line, size_t line_length, out=line; } } - else if (*ml_comment && !ss_comment && inchar == '*' && *(pos + 1) == '/') + else if (*ml_comment && *ss_comment == SSC_NONE && + inchar == '*' && *(pos + 1) == '/') { if (preserve_comments) { @@ -2767,14 +2775,24 @@ static bool add_line(String &buffer, char *line, size_t line_length, } else { // Add found char to buffer - if (!*in_string && inchar == '/' && *(pos + 1) == '*' && - *(pos + 2) == '!') - ss_comment= 1; - else if (!*in_string && ss_comment && inchar == '*' && *(pos + 1) == '/') - ss_comment= 0; + if (!*in_string && inchar == '/' && pos[1] == '*') + { + if (pos[2] == '!') + *ss_comment= SSC_CONDITIONAL; + else if (pos[2] == '+') + *ss_comment= SSC_HINT; + } + else if (!*in_string && *ss_comment != SSC_NONE && + inchar == '*' && *(pos + 1) == '/') + { + *ss_comment= SSC_NONE; + *out++= *pos++; // copy '*' + *out++= *pos; // copy '/' + continue; + } if (inchar == *in_string) *in_string= 0; - else if (!*ml_comment && !*in_string && + else if (!*ml_comment && !*in_string && *ss_comment != SSC_HINT && (inchar == '\'' || inchar == '"' || inchar == '`')) *in_string= (char) inchar; if (!*ml_comment || preserve_comments) diff --git a/include/my_sys.h b/include/my_sys.h index e129f03ff3c3d..d0d3981867d26 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -502,6 +502,13 @@ typedef struct st_io_cache /* Used when caching files */ size_t alloced_buffer; } IO_CACHE; +typedef struct Prepared_error +{ + uint code; + char message[MYSYS_ERRMSG_SIZE]; + myf flags; +} MY_PREPARED_ERROR; + typedef void (*my_error_reporter)(enum loglevel level, const char *format, ...) ATTRIBUTE_FORMAT_FPTR(printf, 2, 3); @@ -743,6 +750,8 @@ extern int my_error_register(const char** (*get_errmsgs) (int nr), extern my_bool my_error_unregister(uint first, uint last); extern void my_message(uint my_err, const char *str,myf MyFlags); extern void my_message_stderr(uint my_err, const char *str, myf MyFlags); +extern struct Prepared_error my_error_prepare(uint nr, myf MyFlags, ...); +extern void my_error_issue(struct Prepared_error* error); extern my_bool my_init(void); extern void my_end(int infoflag); extern int my_redel(const char *from, const char *to, time_t backup_time_stamp, diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 95b013a0c9077..8bf64b047e8ce 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -157,6 +157,9 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/json_table.cc ../sql/opt_histogram_json.cc ../sql/sp_instr.cc + ../sql/opt_hints_parser.cc ../sql/opt_hints_parser.h + ../sql/scan_char.h + ../sql/opt_hints.cc ../sql/opt_hints.h ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE} ) diff --git a/mysql-test/include/default_optimizer_switch.inc b/mysql-test/include/default_optimizer_switch.inc index 5790cec4bc1fd..5260b7a03c9c4 100644 --- a/mysql-test/include/default_optimizer_switch.inc +++ b/mysql-test/include/default_optimizer_switch.inc @@ -8,7 +8,7 @@ set @save_optimizer_switch=@@optimizer_switch; set @save_join_cache_level=@@join_cache_level; -set optimizer_switch="index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on"; +set optimizer_switch="index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on"; set optimizer_use_condition_selectivity=4; set optimizer_search_depth=62; diff --git a/mysql-test/main/lowercase_table.result b/mysql-test/main/lowercase_table.result index eef41327375f3..eaeb495aa8408 100644 --- a/mysql-test/main/lowercase_table.result +++ b/mysql-test/main/lowercase_table.result @@ -208,3 +208,30 @@ DROP PACKAGE test.pkg; # # End of 11.4 tests # +# +# Start of 11.7 tests +# +# +# MDEV-33281 Implement optimizer hints like in MySQL +# +SET NAMES utf8mb4; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); +SELECT /*+BKA(a) BKA(å)*/ a.a FROM t1 a, t1 å; +a +1 +2 +1 +2 +SELECT a.a, A.a FROM t1 a, t1 A; +ERROR 42000: Not unique table/alias: 'A' +SELECT /*+BKA(a) BKA(A)*/ a.a FROM t1 a; +a +1 +2 +Warnings: +Warning 4210 Hint BKA("A") is ignored as conflicting/duplicated +DROP TABLE t1; +# +# End of 11.7 tests +# diff --git a/mysql-test/main/lowercase_table.test b/mysql-test/main/lowercase_table.test index 0795347a46bcb..fcd98f32fb57e 100644 --- a/mysql-test/main/lowercase_table.test +++ b/mysql-test/main/lowercase_table.test @@ -204,3 +204,35 @@ DROP PACKAGE test.pkg; --echo # --echo # End of 11.4 tests --echo # + + +--echo # +--echo # Start of 11.7 tests +--echo # + +--echo # +--echo # MDEV-33281 Implement optimizer hints like in MySQL +--echo # + +SET NAMES utf8mb4; + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); + +--enable_prepare_warnings +# Test that aliases are accent sensitive with lowercase-table-names=1 +# Test that table names in hints are also accent sensitive +SELECT /*+BKA(a) BKA(å)*/ a.a FROM t1 a, t1 å; + +# Test that aliases are case insensitive with lowercase-table-names=1 +--error ER_NONUNIQ_TABLE +SELECT a.a, A.a FROM t1 a, t1 A; +# Test that table names in hints are also case insensitive +SELECT /*+BKA(a) BKA(A)*/ a.a FROM t1 a; +--disable_prepare_warnings + +DROP TABLE t1; + +--echo # +--echo # End of 11.7 tests +--echo # diff --git a/mysql-test/main/lowercase_table5.result b/mysql-test/main/lowercase_table5.result index 9a2a02b50e0c0..ff5098ebdd3cd 100644 --- a/mysql-test/main/lowercase_table5.result +++ b/mysql-test/main/lowercase_table5.result @@ -189,3 +189,28 @@ DROP DATABASE MYSQL; # # End of 10.5 tests # +# +# Start of 11.7 tests +# +# +# MDEV-33281 Implement optimizer hints like in MySQL +# +SET NAMES utf8mb4; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); +SELECT /*+BKA(a) BKA(å)*/ a.a, å.a FROM t1 a, t1 å; +a a +1 1 +2 1 +1 2 +2 2 +SELECT /*+BKA(a) BKA(A)*/ a.a, A.a FROM t1 a, t1 A; +a a +1 1 +2 1 +1 2 +2 2 +DROP TABLE t1; +# +# End of 11.7 tests +# diff --git a/mysql-test/main/lowercase_table5.test b/mysql-test/main/lowercase_table5.test index fcbb3fefb3355..a51be6677cf3e 100644 --- a/mysql-test/main/lowercase_table5.test +++ b/mysql-test/main/lowercase_table5.test @@ -187,3 +187,30 @@ DROP DATABASE MYSQL; --echo # --echo # End of 10.5 tests --echo # + +--echo # +--echo # Start of 11.7 tests +--echo # + +--echo # +--echo # MDEV-33281 Implement optimizer hints like in MySQL +--echo # + +SET NAMES utf8mb4; + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1), (2); + +# Test that table aliases are accent sensitive with lowercase-table-names=0 +# Test that table names in hints are also accent sensitive +SELECT /*+BKA(a) BKA(å)*/ a.a, å.a FROM t1 a, t1 å; + +# Test that table aliases are case sensitive with lowercase-table-names=0 +# Test that table names in hints are also case sensitive +SELECT /*+BKA(a) BKA(A)*/ a.a, A.a FROM t1 a, t1 A; + +DROP TABLE t1; + +--echo # +--echo # End of 11.7 tests +--echo # diff --git a/mysql-test/main/mysql_comments.result b/mysql-test/main/mysql_comments.result index 5b2a68974e0fd..df3dc6e14edec 100644 --- a/mysql-test/main/mysql_comments.result +++ b/mysql-test/main/mysql_comments.result @@ -33,6 +33,14 @@ Trigger sql_mode SQL Original Statement character_set_client collation_connectio t1_bi CREATE DEFINER=`root`@`localhost` trigger t1_bi before insert on t1\nfor each row\nbegin\n\n\n\n \n declare b int;\n declare c float;\n\n \n \n\n \n set NEW.data := 12;\nend latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci --TIME-- id data trig 12 +id data +trig 12 +id data +trig 12 +id data +trig 12 +id data +trig 12 "Pass 2 : --enable-comments" 1 1 @@ -60,5 +68,13 @@ Trigger sql_mode SQL Original Statement character_set_client collation_connectio t1_bi CREATE DEFINER=`root`@`localhost` trigger t1_bi before insert on t1\nfor each row\nbegin\n# comment 1a\n-- comment 1b\n/*\n comment 1c\n*/\n -- declare some variables here\n declare b int;\n declare c float;\n\n -- do more stuff here\n -- commented nicely and so on\n\n -- famous last words ...\n set NEW.data := 12;\nend latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci --TIME-- id data trig 12 +id data +trig 12 +id data +trig 12 +id data +trig 12 +id data +trig 12 set global sql_mode=default; End of 5.0 tests diff --git a/mysql-test/main/mysql_comments.sql b/mysql-test/main/mysql_comments.sql index 2497c35e4658f..c7d20dd8bcbab 100644 --- a/mysql-test/main/mysql_comments.sql +++ b/mysql-test/main/mysql_comments.sql @@ -211,6 +211,18 @@ show create trigger t1_bi; insert into t1(id) value ("trig"); select * from t1; +##============================================================================ +## Optimizer hints +##============================================================================ + +select /*+ no_icp(t1)*/* from t1; +select /*+ no_icp(t1) " ;,` */* from t1; +select /*+ "some text" */* from t1; + +select /* no_icp(t1) +no_bka(t1) +*/* from t1; + ##============================================================================ ## Cleanup ##============================================================================ diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 00fbdf55eba79..50a13e6465b54 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -877,7 +877,7 @@ The following specify which files/extra groups are read (specified before remain index_merge_sort_union, index_merge_intersection, index_merge_sort_intersection, index_condition_pushdown, derived_merge, derived_with_keys, firstmatch, loosescan, - materialization, in_to_exists, semijoin, + duplicateweedout, materialization, in_to_exists, semijoin, partial_match_rowid_merge, partial_match_table_scan, subquery_cache, mrr, mrr_cost_based, mrr_sort_keys, outer_join_with_cache, semijoin_with_cache, @@ -1880,7 +1880,7 @@ optimizer-rowid-copy-cost 0.002653 optimizer-scan-setup-cost 10 optimizer-search-depth 62 optimizer-selectivity-sampling-limit 100 -optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on optimizer-trace optimizer-trace-max-mem-size 1048576 optimizer-use-condition-selectivity 4 diff --git a/mysql-test/main/mysqltest_tracking_info.result b/mysql-test/main/mysqltest_tracking_info.result index 39d898f91081b..4266e845d3479 100644 --- a/mysql-test/main/mysqltest_tracking_info.result +++ b/mysql-test/main/mysqltest_tracking_info.result @@ -33,7 +33,7 @@ set @save_optimizer_switch=@@optimizer_switch; SET @@session.session_track_system_variables='optimizer_switch'; set optimizer_switch='index_merge=off,index_merge_union=off,index_merge_sort_union=off,index_merge_intersection=off,index_merge_sort_intersection=on,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=on,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off'; -- Tracker : SESSION_TRACK_SYSTEM_VARIABLES --- optimizer_switch: index_merge=off,index_merge_union=off,index_merge_sort_union=off,index_merge_intersection=off,index_merge_sort_intersection=on,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=on,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +-- optimizer_switch: index_merge=off,index_merge_union=off,index_merge_sort_union=off,index_merge_intersection=off,index_merge_sort_intersection=on,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=on,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=on,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on set @@optimizer_switch=@save_optimizer_switch; SET @@session.session_track_system_variables= @save_session_track_system_variables; diff --git a/mysql-test/main/opt_hint_timeout.result b/mysql-test/main/opt_hint_timeout.result new file mode 100644 index 0000000000000..2eb86462d184c --- /dev/null +++ b/mysql-test/main/opt_hint_timeout.result @@ -0,0 +1,118 @@ +# +# MAX_EXECUTION_TIME hint testing +# +CREATE TABLE t1 (a INT, b VARCHAR(300)); +INSERT INTO t1 VALUES (1, 'string'); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +# Correct hint usage +SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +EXPLAIN EXTENDED SELECT /*+ MAX_EXECUTION_TIME(000149) */* FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 512 100.00 +Warnings: +Note 1003 select /*+ MAX_EXECUTION_TIME(000149) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` +SELECT /*+ MAX_EXECUTION_TIME(20) */ *, SLEEP(1) FROM t1 UNION SELECT 1, 2, 3; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +(SELECT /*+ MAX_EXECUTION_TIME(30) */ *, SLEEP(1) FROM t1) UNION (SELECT 1, 2, 3); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +((SELECT /*+ MAX_EXECUTION_TIME(50) */ *, SLEEP(1) FROM t1)); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +# Make sure the hint overrides global/session/statement settings. +# Global setting 30 seconds, won't be exceeded for sure +SET @@max_statement_time = 30; +SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +# Session setting 10 seconds, too long to be exceeded +SET SESSION max_statement_time = 10; +SELECT /*+ MAX_EXECUTION_TIME(15) */* FROM t1 a, t1 b; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +SET STATEMENT max_statement_time = 20 FOR +SELECT /*+ MAX_EXECUTION_TIME(5) */* FROM t1 a, t1 b; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +# Check that prepared statements process the hint correctly +PREPARE s FROM 'SELECT /*+ MAX_EXECUTION_TIME(20) */ seq, SLEEP(1) FROM seq_1_to_10'; +EXECUTE s; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +EXECUTE s; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +# Hint duplication +SELECT /*+ MAX_EXECUTION_TIME(10) MAX_EXECUTION_TIME(100) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 4210 Hint MAX_EXECUTION_TIME(100) is ignored as conflicting/duplicated +# Wrong values +SELECT /*+ MAX_EXECUTION_TIME(0) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 1912 Incorrect value '0' for option 'MAX_EXECUTION_TIME' +SELECT /*+ MAX_EXECUTION_TIME(-1) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 1064 Optimizer hint syntax error near '-1) */ count(*) FROM t1' at line 1 +SELECT /*+ MAX_EXECUTION_TIME(4294967296) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 1912 Incorrect value '4294967296' for option 'MAX_EXECUTION_TIME' + +# only SELECT statements supports the MAX_EXECUTION_TIME hint (warning): + +CREATE TABLE t2 (i INT); +INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1; +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context +REPLACE /*+ MAX_EXECUTION_TIME(15) */ INTO t2 SELECT 1; +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(15)' is not allowed in this context +UPDATE /*+ MAX_EXECUTION_TIME(23) */ t2 SET i = 1; +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(23)' is not allowed in this context +DELETE /*+ MAX_EXECUTION_TIME(5000) */ FROM t2 WHERE i = 1; +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(5000)' is not allowed in this context +# Not supported inside stored procedures/functions +CREATE PROCEDURE p1() BEGIN SELECT /*+ MAX_EXECUTION_TIME(10) */ count(*) FROM t1 a, t1 b +INTO @a; END| +CALL p1(); +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context +DROP PROCEDURE p1; +# Hint in a subquery is not allowed (warning): +SELECT 1 FROM (SELECT /*+ MAX_EXECUTION_TIME(10) */ 1) a; +1 +1 +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context +# Hint is allowed only for the first select of UNION (warning): +SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*) FROM t1 +UNION +SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 4210 Hint MAX_EXECUTION_TIME(30) is ignored as conflicting/duplicated +SELECT count(*) FROM t1 +UNION +SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1; +count(*) +512 +Warnings: +Warning 4172 'MAX_EXECUTION_TIME(30)' is not allowed in this context +# Check that hint actually works: +SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*), SLEEP(1) FROM t1 +UNION +SELECT count(*), SLEEP(1) FROM t1; +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +DROP TABLE t1, t2; diff --git a/mysql-test/main/opt_hint_timeout.test b/mysql-test/main/opt_hint_timeout.test new file mode 100644 index 0000000000000..08b4c7f0a24bf --- /dev/null +++ b/mysql-test/main/opt_hint_timeout.test @@ -0,0 +1,113 @@ +# Setting statement time-outs is not possible for the embedded server neither +# by SET STATEMENT max_statement_time=X nor by /*+ MAX_EXECUTION_TIME(X)*/ hint. +# That is why this test is disabled for the embedded server +--source include/not_embedded.inc + +--disable_view_protocol # Since optimizer hints are not supported inside views + +--source include/have_sequence.inc +--echo # +--echo # MAX_EXECUTION_TIME hint testing +--echo # +--enable_prepare_warnings + +CREATE TABLE t1 (a INT, b VARCHAR(300)); +INSERT INTO t1 VALUES (1, 'string'); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; + +-- disable_query_log +-- disable_result_log +analyze table t1; +-- enable_result_log +-- enable_query_log + +--echo # Correct hint usage +--error ER_STATEMENT_TIMEOUT +SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b; + +EXPLAIN EXTENDED SELECT /*+ MAX_EXECUTION_TIME(000149) */* FROM t1; + +--error ER_STATEMENT_TIMEOUT +SELECT /*+ MAX_EXECUTION_TIME(20) */ *, SLEEP(1) FROM t1 UNION SELECT 1, 2, 3; +--error ER_STATEMENT_TIMEOUT +(SELECT /*+ MAX_EXECUTION_TIME(30) */ *, SLEEP(1) FROM t1) UNION (SELECT 1, 2, 3); +--error ER_STATEMENT_TIMEOUT +((SELECT /*+ MAX_EXECUTION_TIME(50) */ *, SLEEP(1) FROM t1)); + +--echo # Make sure the hint overrides global/session/statement settings. +--echo # Global setting 30 seconds, won't be exceeded for sure +SET @@max_statement_time = 30; +--error ER_STATEMENT_TIMEOUT +SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b; + +--echo # Session setting 10 seconds, too long to be exceeded +SET SESSION max_statement_time = 10; +--error ER_STATEMENT_TIMEOUT +SELECT /*+ MAX_EXECUTION_TIME(15) */* FROM t1 a, t1 b; + +--error ER_STATEMENT_TIMEOUT +SET STATEMENT max_statement_time = 20 FOR + SELECT /*+ MAX_EXECUTION_TIME(5) */* FROM t1 a, t1 b; + +--echo # Check that prepared statements process the hint correctly +PREPARE s FROM 'SELECT /*+ MAX_EXECUTION_TIME(20) */ seq, SLEEP(1) FROM seq_1_to_10'; +--error ER_STATEMENT_TIMEOUT +EXECUTE s; +--error ER_STATEMENT_TIMEOUT +EXECUTE s; + +--echo # Hint duplication + +SELECT /*+ MAX_EXECUTION_TIME(10) MAX_EXECUTION_TIME(100) */ count(*) FROM t1; + +--echo # Wrong values +SELECT /*+ MAX_EXECUTION_TIME(0) */ count(*) FROM t1; +SELECT /*+ MAX_EXECUTION_TIME(-1) */ count(*) FROM t1; +SELECT /*+ MAX_EXECUTION_TIME(4294967296) */ count(*) FROM t1; + +--echo +--echo # only SELECT statements supports the MAX_EXECUTION_TIME hint (warning): +--echo +CREATE TABLE t2 (i INT); +INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1; +REPLACE /*+ MAX_EXECUTION_TIME(15) */ INTO t2 SELECT 1; +UPDATE /*+ MAX_EXECUTION_TIME(23) */ t2 SET i = 1; +DELETE /*+ MAX_EXECUTION_TIME(5000) */ FROM t2 WHERE i = 1; + +--echo # Not supported inside stored procedures/functions +DELIMITER |; +CREATE PROCEDURE p1() BEGIN SELECT /*+ MAX_EXECUTION_TIME(10) */ count(*) FROM t1 a, t1 b +INTO @a; END| +DELIMITER ;| + +CALL p1(); +DROP PROCEDURE p1; + +--echo # Hint in a subquery is not allowed (warning): +SELECT 1 FROM (SELECT /*+ MAX_EXECUTION_TIME(10) */ 1) a; + +--echo # Hint is allowed only for the first select of UNION (warning): +SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*) FROM t1 +UNION +SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1; + +SELECT count(*) FROM t1 +UNION +SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1; + +--echo # Check that hint actually works: +--error ER_STATEMENT_TIMEOUT +SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*), SLEEP(1) FROM t1 +UNION +SELECT count(*), SLEEP(1) FROM t1; + +DROP TABLE t1, t2; + diff --git a/mysql-test/main/opt_hints.result b/mysql-test/main/opt_hints.result new file mode 100644 index 0000000000000..6442cb623471e --- /dev/null +++ b/mysql-test/main/opt_hints.result @@ -0,0 +1,1989 @@ +SET NAMES utf8mb4; +# Testing that index names in hints are accent sensitive case insensitive +CREATE TABLE t1 (a INT, ä INT, INDEX idx_a(a), INDEX idx_ä(ä)); +INSERT INTO t1 VALUES (1,1),(2,2); +SELECT /*+ NO_MRR(t1 idx_a) */ a FROM t1; +a +1 +2 +SELECT /*+ NO_MRR(t1 idx_A) */ a FROM t1; +a +1 +2 +SELECT /*+ NO_MRR(t1 idx_å) */ a FROM t1; +a +1 +2 +Warnings: +Warning 4213 Unresolved index name `t1`@`select#1` `idx_å` for NO_MRR hint +SELECT /*+ NO_MRR(t1 idx_a, idx_å, idx_A) */ a FROM t1; +a +1 +2 +Warnings: +Warning 4210 Hint NO_MRR(`t1` `idx_A`) is ignored as conflicting/duplicated +Warning 4213 Unresolved index name `t1`@`select#1` `idx_å` for NO_MRR hint +DROP TABLE t1; +# Testing that query block names are accent sensitive case insensitive +CREATE TABLE t1 (a INT); +SELECT /*+ QB_NAME(a) BKA(t1@a) BKA(t1@A) */ * FROM t1; +a +Warnings: +Warning 4210 Hint BKA(`t1`@`A`) is ignored as conflicting/duplicated +SELECT /*+ QB_NAME(a) BKA(t1@a) BKA(t1@å) */ * FROM t1; +a +Warnings: +Warning 4211 Query block name `å` is not found for BKA hint +DROP TABLE t1; +CREATE TABLE t1(f1 INT, f2 INT); +INSERT INTO t1 VALUES +(1,1),(2,2),(3,3); +CREATE TABLE t2(f1 INT NOT NULL, f2 INT NOT NULL, f3 CHAR(200), KEY(f1, f2)); +INSERT INTO t2 VALUES +(1,1, 'qwerty'),(1,2, 'qwerty'),(1,3, 'qwerty'), +(2,1, 'qwerty'),(2,2, 'qwerty'),(2,3, 'qwerty'), (2,4, 'qwerty'),(2,5, 'qwerty'), +(3,1, 'qwerty'),(3,4, 'qwerty'), +(4,1, 'qwerty'),(4,2, 'qwerty'),(4,3, 'qwerty'), (4,4, 'qwerty'), +(1,1, 'qwerty'),(1,2, 'qwerty'),(1,3, 'qwerty'), +(2,1, 'qwerty'),(2,2, 'qwerty'),(2,3, 'qwerty'), (2,4, 'qwerty'),(2,5, 'qwerty'), +(3,1, 'qwerty'),(3,4, 'qwerty'), +(4,1, 'qwerty'),(4,2, 'qwerty'),(4,3, 'qwerty'), (4,4, 'qwerty'); +CREATE TABLE t3 (f1 INT NOT NULL, f2 INT, f3 VARCHAR(32), +PRIMARY KEY(f1), KEY f2_idx(f1), KEY f3_idx(f3)); +INSERT INTO t3 VALUES +(1, 1, 'qwerty'), (2, 1, 'ytrewq'), +(3, 2, 'uiop'), (4, 2, 'poiu'), (5, 2, 'lkjh'), +(6, 2, 'uiop'), (7, 2, 'poiu'), (8, 2, 'lkjh'), +(9, 2, 'uiop'), (10, 2, 'poiu'), (11, 2, 'lkjh'), +(12, 2, 'uiop'), (13, 2, 'poiu'), (14, 2, 'lkjh'); +INSERT INTO t3 SELECT f1 + 20, f2, f3 FROM t3; +INSERT INTO t3 SELECT f1 + 40, f2, f3 FROM t3; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +ANALYZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +ANALYZE TABLE t3; +Table Op Msg_type Msg_text +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +# NO_RANGE_OPTIMIZATION hint testing +set optimizer_switch=default; +# Check statistics with no hint +FLUSH STATUS; +SELECT f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +f1 +31 +32 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 1 +Handler_read_last 0 +Handler_read_next 2 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +# Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +f1 +31 +32 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 1 +Handler_read_key 0 +Handler_read_last 0 +Handler_read_next 56 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +EXPLAIN EXTENDED SELECT f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 range PRIMARY,f2_idx PRIMARY 4 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t3`.`f1` AS `f1` from `test`.`t3` where `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 +# Turn off range access for PRIMARY key +# Should use range access by f2_idx key +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) */ f1 +FROM t3 WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 range PRIMARY,f2_idx f2_idx 4 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t3` where `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 +# Turn off range access for PRIMARY & f2_idx keys +# Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1 +FROM t3 WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 index PRIMARY,f2_idx PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `f2_idx`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t3` where `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 +# Turn off range access for all keys +# Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3) */ f1 +FROM t3 WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 index PRIMARY,f2_idx PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t3` where `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 +# Turn off range access for PRIMARY & f2_idx keys +# Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) NO_RANGE_OPTIMIZATION(t3 f2_idx) */ f1 +FROM t3 WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 index PRIMARY,f2_idx PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `f2_idx`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t3` where `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 +# Create a clone of t3 with cyrillic names +CREATE TABLE таблица (f1 INT NOT NULL, поле2 INT, поле3 VARCHAR(32), +PRIMARY KEY(f1), KEY f2_индекс(f1), KEY f3_индекс(поле3)) +AS SELECT * FROM t3; +ANALYZE TABLE таблица; +Table Op Msg_type Msg_text +test.таблица analyze status Engine-independent statistics collected +test.таблица analyze status Table is already up to date +# Turn off range access for PRIMARY key +# Should use range access by f2_индекс key +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY) */ f1 +FROM таблица WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE таблица range PRIMARY,f2_индекс f2_индекс 4 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`таблица`@`select#1` `PRIMARY`) */ `test`.`таблица`.`f1` AS `f1` from `test`.`таблица` where `test`.`таблица`.`f1` > 30 and `test`.`таблица`.`f1` < 33 +# Turn off range access for PRIMARY & f2_индекс keys +# Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY, f2_индекс) */ f1 +FROM таблица WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE таблица index PRIMARY,f2_индекс PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`таблица`@`select#1` `PRIMARY`) NO_RANGE_OPTIMIZATION(`таблица`@`select#1` `f2_индекс`) */ `test`.`таблица`.`f1` AS `f1` from `test`.`таблица` where `test`.`таблица`.`f1` > 30 and `test`.`таблица`.`f1` < 33 +# Turn off range access for all keys +# Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица) */ f1 +FROM таблица WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE таблица index PRIMARY,f2_индекс PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`таблица`@`select#1`) */ `test`.`таблица`.`f1` AS `f1` from `test`.`таблица` where `test`.`таблица`.`f1` > 30 and `test`.`таблица`.`f1` < 33 +# Turn off range access for PRIMARY & f2_индекс keys +# Should use index access +EXPLAIN EXTENDED +SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY) NO_RANGE_OPTIMIZATION(таблица f2_индекс) */ f1 +FROM таблица WHERE f1 > 30 AND f1 < 33; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE таблица index PRIMARY,f2_индекс PRIMARY 4 NULL 56 4.11 Using where; Using index +Warnings: +Note 1003 select /*+ NO_RANGE_OPTIMIZATION(`таблица`@`select#1` `PRIMARY`) NO_RANGE_OPTIMIZATION(`таблица`@`select#1` `f2_индекс`) */ `test`.`таблица`.`f1` AS `f1` from `test`.`таблица` where `test`.`таблица`.`f1` > 30 and `test`.`таблица`.`f1` < 33 +DROP TABLE таблица; +# NO_ICP hint testing +set optimizer_switch='index_condition_pushdown=on'; +CREATE TABLE t4 (x INT, y INT, KEY x_idx(x), KEY y_idx(y)); +INSERT INTO t4 (x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13); +UPDATE t4 SET y=x; +EXPLAIN EXTENDED SELECT * FROM +(SELECT t4.x, t5.y FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +EXPLAIN EXTENDED SELECT * FROM +(SELECT /*+ NO_ICP(t5 x_idx, y_idx) */ t4.x, t5.y FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_ICP(`t5`@`select#2` `x_idx`) NO_ICP(`t5`@`select#2` `y_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +EXPLAIN EXTENDED SELECT /*+ NO_ICP(t5@qb1 x_idx) */ * FROM +(SELECT /*+ QB_NAME(QB1) */ t4.x, t5.y FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_ICP(`t5`@`QB1` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +# Cyrillic query block name +EXPLAIN EXTENDED SELECT /*+ NO_ICP(t5@блок1 x_idx) */ * FROM +(SELECT /*+ QB_NAME(блок1) */ t4.x, t5.y FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_ICP(`t5`@`блок1` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +# Expected warning for z_idx key, unresolved name. +EXPLAIN EXTENDED SELECT * FROM +(SELECT /*+ NO_ICP(t5 y_idx, x_idx, z_idx) */ t4.x, t5.y FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Warning 4213 Unresolved index name `t5`@`select#2` `z_idx` for NO_ICP hint +Note 1003 select /*+ NO_ICP(`t5`@`select#2` `y_idx`) NO_ICP(`t5`@`select#2` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +# ICP should still be used +EXPLAIN EXTENDED SELECT * FROM +(SELECT /*+ NO_ICP(t5 y_idx) */ t4.x, t5.y FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ NO_ICP(`t5`@`select#2` `y_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0) +# BKA & NO_BKA hint testing +set optimizer_switch = DEFAULT; +set optimizer_switch = 'mrr=on,mrr_cost_based=off'; +set join_cache_level = 8; +CREATE TABLE t10(a INT); +INSERT INTO t10 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +CREATE TABLE t11(a INT); +INSERT INTO t11 SELECT A.a + B.a* 10 + C.a * 100 from t10 A, t10 B, t10 C; +CREATE TABLE t12(a INT, b INT); +INSERT INTO t12 SELECT a,a from t10; +CREATE TABLE t13(a INT, b INT, c INT, filler CHAR(100), key (a,b)); +INSERT INTO t13 select a,a,a, 'filler-data' FROM t11; +# Make sure BKA is expected to be used when there are no hints +EXPLAIN +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +# Disable BKA +set optimizer_switch='join_cache_bka=off'; +EXPLAIN EXTENDED +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +# Check statistics without hint +FLUSH STATUS; +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +a b a b c filler +0 0 0 0 0 filler-data +1 1 1 1 1 filler-data +2 2 2 2 2 filler-data +3 3 3 3 3 filler-data +4 4 4 4 4 filler-data +5 5 5 5 5 filler-data +6 6 6 6 6 filler-data +7 7 7 7 7 filler-data +8 8 8 8 8 filler-data +9 9 9 9 9 filler-data +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_last 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 1012 +# Check statistics with hint +FLUSH STATUS; +SELECT /*+ BKA() */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +a b a b c filler +0 0 0 0 0 filler-data +1 1 1 1 1 filler-data +2 2 2 2 2 filler-data +3 3 3 3 3 filler-data +4 4 4 4 4 filler-data +5 5 5 5 5 filler-data +6 6 6 6 6 filler-data +7 7 7 7 7 filler-data +8 8 8 8 8 filler-data +9 9 9 9 9 filler-data +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 10 +Handler_read_last 0 +Handler_read_next 10 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 10 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 11 +EXPLAIN EXTENDED SELECT /*+ BKA(t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ BKA() */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`t12`@`select#1`) BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ BKA(t12) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select /*+ BKA(`t12`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ QB_NAME(QB1) BKA(t13@QB1) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ QB_NAME(`QB1`) BKA(`t13`@`QB1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +# Hint overrides both join_cache_level and optimizer switch +set join_cache_level = 0; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`t12`@`select#1`) BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +set join_cache_level = 2; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`t12`@`select#1`) BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +set join_cache_level = 4; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`t12`@`select#1`) BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +set join_cache_level = 8; +# Enable BKA +set optimizer_switch='join_cache_bka=on'; +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select /*+ NO_BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ NO_BKA() */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select /*+ NO_BKA(@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t12, t13) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select /*+ NO_BKA(`t12`@`select#1`) NO_BKA(`t13`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t12) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ NO_BKA(`t12`@`select#1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +EXPLAIN EXTENDED SELECT /*+ QB_NAME(QB1) NO_BKA(t13@QB1) */ * FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select /*+ QB_NAME(`QB1`) NO_BKA(`t13`@`QB1`) */ `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +# UPDATE|DELETE|INSERT|REPLACE hint testing +EXPLAIN EXTENDED UPDATE t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 range PRIMARY,f2_idx,f3_idx PRIMARY 4 NULL 2 100.00 Using index condition; Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3.33 Using where +1 PRIMARY t2 ref f1 f1 8 test.t3.f1,test.t3.f2 2 50.00 Using where; FirstMatch(t3) +Warnings: +Note 1003 update `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f2` = `test`.`t3`.`f2` and `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 and `test`.`t3`.`f2` between `test`.`t3`.`f1` and `test`.`t1`.`f2` and `test`.`t3`.`f2` + 1 >= `test`.`t3`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Turn off range access for PRIMARY key. +# Range access should be used for f2_idx key. +EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) */ t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT /*+ BKA(t2) NO_BNL(t1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 range PRIMARY,f2_idx,f3_idx f2_idx 4 NULL 2 100.00 Using index condition; Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3.33 Using where +1 PRIMARY t2 ref f1 f1 8 test.t3.f1,test.t3.f2 2 50.00 Using where; FirstMatch(t3) +Warnings: +Note 1003 update /*+ BKA(`t2`@`select#2`) NO_BNL(`t1`@`select#2`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) */ `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f2` = `test`.`t3`.`f2` and `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 and `test`.`t3`.`f2` between `test`.`t3`.`f1` and `test`.`t1`.`f2` and `test`.`t3`.`f2` + 1 >= `test`.`t3`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Turn off range access for all keys. +EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3) */ t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 range f1 f1 4 NULL 1 100.00 Using index condition; Start temporary +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 11.11 Using where +1 PRIMARY t3 eq_ref PRIMARY,f2_idx,f3_idx PRIMARY 4 test.t2.f1 1 100.00 Using where; End temporary +Warnings: +Note 1003 update /*+ NO_RANGE_OPTIMIZATION(`t3`@`select#1`) */ `test`.`t3` semi join (`test`.`t1` join `test`.`t2`) set `test`.`t3`.`f3` = 'mnbv' where `test`.`t1`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f2` = `test`.`t2`.`f2` and `test`.`t2`.`f1` > 30 and `test`.`t2`.`f1` < 33 and `test`.`t2`.`f2` between `test`.`t2`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t2`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +EXPLAIN EXTENDED DELETE FROM t3 +WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 range PRIMARY,f2_idx,f3_idx PRIMARY 4 NULL 2 100.00 Using index condition; Using where +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 3.33 Using where +1 PRIMARY t2 ref f1 f1 8 test.t3.f1,test.t3.f2 2 50.00 Using where; FirstMatch(t3) +Warnings: +Note 1003 delete from `test`.`t3` using (`test`.`t1` join `test`.`t2`) where `test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f1` = `test`.`t3`.`f1` and `test`.`t2`.`f2` = `test`.`t3`.`f2` and `test`.`t3`.`f1` > 30 and `test`.`t3`.`f1` < 33 and `test`.`t3`.`f2` between `test`.`t3`.`f1` and `test`.`t1`.`f2` and `test`.`t3`.`f2` + 1 >= `test`.`t3`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Turn off range access. Range access should not be used. +EXPLAIN EXTENDED +DELETE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) NO_BNL(t1@QB1) */ FROM t3 +WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN +(SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 range f1 f1 4 NULL 1 100.00 Using index condition; Start temporary +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 11.11 Using where +1 PRIMARY t3 eq_ref PRIMARY,f2_idx,f3_idx PRIMARY 4 test.t2.f1 1 100.00 Using where; End temporary +Warnings: +Note 1003 delete /*+ NO_BNL(`t1`@`qb1`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `PRIMARY`) NO_RANGE_OPTIMIZATION(`t3`@`select#1` `f2_idx`) */ from `test`.`t3` using (`test`.`t1` join `test`.`t2`) where `test`.`t1`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f1` = `test`.`t2`.`f1` and `test`.`t3`.`f2` = `test`.`t2`.`f2` and `test`.`t2`.`f1` > 30 and `test`.`t2`.`f1` < 33 and `test`.`t2`.`f2` between `test`.`t2`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t2`.`f1` + 1 and `test`.`t3`.`f3` = `test`.`t2`.`f3` +# Make sure ICP is expected to be used when there are no hints +EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) +(SELECT t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (insert into `test`.`t3`(f1,f2,f3) select `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP. ICP should not be used. +EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) +(SELECT /*+ NO_ICP(t5) */t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP for a particular table +EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3) +(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP for a particular table and a key +EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3) +(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Make sure ICP is expected to be used when there are no hints +EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3) +(SELECT t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using index condition; Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (replace into `test`.`t3`(f1,f2,f3) select `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP. ICP should not be used. +EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3) +(SELECT /*+ NO_ICP(t5) */t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP for a particular table +EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3) +(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Turn off ICP for a particular table and a key +EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3) +(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 +WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00 +1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and (8 + 0)) +# Misc tests +# Should issue warning +EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) QB_NAME(qb1 ) */ * FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 28 100.00 +Warnings: +Warning 4210 Hint QB_NAME(`qb1`) is ignored as conflicting/duplicated +Note 1003 select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t2` +# Should issue warning +EXPLAIN EXTENDED SELECT /*+ BKA(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1; +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 +1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Warning 4211 Query block name `qb1` is not found for BKA hint +Note 1003 select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1 +# Should not crash +PREPARE stmt1 FROM "SELECT /*+ BKA(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1"; +EXECUTE stmt1; +f1 f2 f3 +1 1 qwerty +2 2 qwerty +1 1 qwerty +2 2 qwerty +EXECUTE stmt1; +f1 f2 f3 +1 1 qwerty +2 2 qwerty +1 1 qwerty +2 2 qwerty +DEALLOCATE PREPARE stmt1; +# Check use of alias +set optimizer_switch='join_cache_bka=off'; +EXPLAIN EXTENDED +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 hash_ALL a #hash#a 5 test.t12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Note 1003 select `test`.`t12`.`a` AS `a`,`test`.`t12`.`b` AS `b`,`test`.`t13`.`a` AS `a`,`test`.`t13`.`b` AS `b`,`test`.`t13`.`c` AS `c`,`test`.`t13`.`filler` AS `filler` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +# Turn on BKA for multiple tables. BKA should be used for tbl13. +EXPLAIN EXTENDED SELECT /*+ BKA(tbl12, tbl13) */ * FROM t12 tbl12, t13 tbl13 +WHERE tbl12.a=tbl13.a AND (tbl13.b+1 <= tbl13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE tbl12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE tbl13 ref a a 5 test.tbl12.a 1 100.00 Using index condition; Using where; Using join buffer (flat, BKAH join); Rowid-ordered scan +Warnings: +Note 1003 select /*+ BKA(`tbl12`@`select#1`) BKA(`tbl13`@`select#1`) */ `test`.`tbl12`.`a` AS `a`,`test`.`tbl12`.`b` AS `b`,`test`.`tbl13`.`a` AS `a`,`test`.`tbl13`.`b` AS `b`,`test`.`tbl13`.`c` AS `c`,`test`.`tbl13`.`filler` AS `filler` from `test`.`t12` `tbl12` join `test`.`t13` `tbl13` where `test`.`tbl13`.`a` = `test`.`tbl12`.`a` and `test`.`tbl13`.`b` + 1 <= `test`.`tbl13`.`b` + 1 +# Print warnings for nonexistent names +EXPLAIN EXTENDED +SELECT /*+ BKA(t2) NO_BNL(t1) BKA(t3) NO_RANGE_OPTIMIZATION(t3 idx1) NO_RANGE_OPTIMIZATION(t3) */ +t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1; +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 +1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where; Using join buffer (flat, BNL join) +Warnings: +Warning 4212 Unresolved table name `t3`@`select#1` for BKA hint +Warning 4212 Unresolved table name `t3`@`select#1` for NO_RANGE_OPTIMIZATION hint +Warning 4213 Unresolved index name `t3`@`select#1` `idx1` for NO_RANGE_OPTIMIZATION hint +Note 1003 select /*+ BKA(`t2`@`select#1`) NO_BNL(`t1`@`select#1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1 +# Check illegal syntax +EXPLAIN EXTENDED SELECT /*+ BKA(qb1 t3@qb1) */ f2 FROM +(SELECT /*+ QB_NAME(qb1) */ f2, f3, f1 FROM t3 WHERE f1 > 2 AND f3 = 'poiu') AS TD +WHERE TD.f1 > 2 AND TD.f3 = 'poiu'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL PRIMARY,f2_idx,f3_idx NULL NULL NULL 56 27.55 Using where +Warnings: +Warning 1064 Optimizer hint syntax error near 't3@qb1) */ f2 FROM +(SELECT /*+ QB_NAME(qb1) */ f2, f3, f1 FROM t3 WHERE f1 > ...' at line 1 +Note 1003 select `test`.`t3`.`f2` AS `f2` from `test`.`t3` where `test`.`t3`.`f1` > 2 and `test`.`t3`.`f3` = 'poiu' and `test`.`t3`.`f1` > 2 +# Check illegal syntax +EXPLAIN EXTENDED SELECT * FROM +(SELECT /*+ QB_NAME(qb1) BKA(@qb1 t1@qb1, t2@qb1, t3) */ t2.f1, t2.f2, t2.f3 FROM t1,t2,t3) tt; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 28 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 index NULL PRIMARY 4 NULL 56 100.00 Using index; Using join buffer (incremental, BNL join) +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb1, t2@qb1, t3) */ t2.f1, t2.f2, t2.f3 FROM t1,t2,t3) tt' at line 2 +Note 1003 select `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Check '@qb_name table_name' syntax +EXPLAIN EXTENDED SELECT /*+ BKA(@qb1 t13) */ * FROM (SELECT /*+ QB_NAME(QB1) */ t12.a, t13.b FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1)) AS s1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE t13 ref a a 5 test.t12.a 1 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ BKA(`t13`@`QB1`) */ `test`.`t12`.`a` AS `a`,`test`.`t13`.`b` AS `b` from `test`.`t12` join `test`.`t13` where `test`.`t13`.`a` = `test`.`t12`.`a` and `test`.`t13`.`b` + 1 <= `test`.`t13`.`b` + 1 +# Check that original table name is not recognized if alias is used. +EXPLAIN EXTENDED SELECT /*+ BKA(tbl2) */ * FROM t12 tbl12, t13 tbl13 +WHERE tbl12.a=tbl13.a AND (tbl13.b+1 <= tbl13.b+1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE tbl12 ALL NULL NULL NULL NULL 10 100.00 Using where +1 SIMPLE tbl13 hash_ALL a #hash#a 5 test.tbl12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join) +Warnings: +Warning 4212 Unresolved table name `tbl2`@`select#1` for BKA hint +Note 1003 select `test`.`tbl12`.`a` AS `a`,`test`.`tbl12`.`b` AS `b`,`test`.`tbl13`.`a` AS `a`,`test`.`tbl13`.`b` AS `b`,`test`.`tbl13`.`c` AS `c`,`test`.`tbl13`.`filler` AS `filler` from `test`.`t12` `tbl12` join `test`.`t13` `tbl13` where `test`.`tbl13`.`a` = `test`.`tbl12`.`a` and `test`.`tbl13`.`b` + 1 <= `test`.`tbl13`.`b` + 1 +# Check that PS and conventional statements give the same result. +FLUSH STATUS; +SELECT /*+ BKA(t13) */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +a b a b c filler +0 0 0 0 0 filler-data +1 1 1 1 1 filler-data +2 2 2 2 2 filler-data +3 3 3 3 3 filler-data +4 4 4 4 4 filler-data +5 5 5 5 5 filler-data +6 6 6 6 6 filler-data +7 7 7 7 7 filler-data +8 8 8 8 8 filler-data +9 9 9 9 9 filler-data +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 10 +Handler_read_last 0 +Handler_read_next 10 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 10 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 11 +PREPARE stmt1 FROM "SELECT /*+ BKA(t13) */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1)"; +FLUSH STATUS; +EXECUTE stmt1; +a b a b c filler +0 0 0 0 0 filler-data +1 1 1 1 1 filler-data +2 2 2 2 2 filler-data +3 3 3 3 3 filler-data +4 4 4 4 4 filler-data +5 5 5 5 5 filler-data +6 6 6 6 6 filler-data +7 7 7 7 7 filler-data +8 8 8 8 8 filler-data +9 9 9 9 9 filler-data +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 10 +Handler_read_last 0 +Handler_read_next 10 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 10 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 11 +FLUSH STATUS; +EXECUTE stmt1; +a b a b c filler +0 0 0 0 0 filler-data +1 1 1 1 1 filler-data +2 2 2 2 2 filler-data +3 3 3 3 3 filler-data +4 4 4 4 4 filler-data +5 5 5 5 5 filler-data +6 6 6 6 6 filler-data +7 7 7 7 7 filler-data +8 8 8 8 8 filler-data +9 9 9 9 9 filler-data +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 10 +Handler_read_last 0 +Handler_read_next 10 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 10 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 11 +DEALLOCATE PREPARE stmt1; +DROP TABLE t1, t2, t3, t10, t11, t12, t13; +# +# BNL and NO_BNL hint testing +# +set optimizer_switch=default; +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(2,2); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t2 VALUES (1,1),(2,2); +CREATE TABLE t3 (a INT, b INT); +INSERT INTO t3 VALUES (1,1),(2,2); +# Check statistics without hint +FLUSH STATUS; +SELECT t1.* FROM t1,t2,t3; +a b +1 1 +2 2 +1 1 +2 2 +1 1 +2 2 +1 1 +2 2 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_last 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 9 +# Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_BNL() */t1.* FROM t1,t2,t3; +a b +1 1 +1 1 +1 1 +1 1 +2 2 +2 2 +2 2 +2 2 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_last 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 21 +EXPLAIN EXTENDED SELECT t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ NO_BNL() */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ NO_BNL(@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ NO_BNL(t2, t3) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ NO_BNL(`t2`@`select#1`) NO_BNL(`t3`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ NO_BNL(t1, t3) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ NO_BNL(`t1`@`select#1`) NO_BNL(`t3`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Make sure query results are the same for any hints combination +SELECT * FROM t1,t2,t3; +a b a b a b +1 1 1 1 1 1 +1 1 1 1 2 2 +1 1 2 2 1 1 +1 1 2 2 2 2 +2 2 1 1 1 1 +2 2 1 1 2 2 +2 2 2 2 1 1 +2 2 2 2 2 2 +SELECT /*+ NO_BNL(t1, t2) */* FROM t1,t2,t3; +a b a b a b +1 1 1 1 1 1 +1 1 1 1 2 2 +1 1 2 2 1 1 +1 1 2 2 2 2 +2 2 1 1 1 1 +2 2 1 1 2 2 +2 2 2 2 1 1 +2 2 2 2 2 2 +SELECT /*+ NO_BNL(t1, t3) */* FROM t1,t2,t3; +a b a b a b +1 1 1 1 1 1 +1 1 1 1 2 2 +1 1 2 2 1 1 +1 1 2 2 2 2 +2 2 1 1 1 1 +2 2 1 1 2 2 +2 2 2 2 1 1 +2 2 2 2 2 2 +SELECT /*+ NO_BNL(t2, t3) */* FROM t1,t2,t3; +a b a b a b +1 1 1 1 1 1 +1 1 1 1 2 2 +1 1 2 2 1 1 +1 1 2 2 2 2 +2 2 1 1 1 1 +2 2 1 1 2 2 +2 2 2 2 1 1 +2 2 2 2 2 2 +# BNL() overrides current join_cache_level setting +set join_cache_level = 0; +EXPLAIN EXTENDED SELECT t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ BNL() */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ BNL(@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ BNL(t1, t2) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ BNL(`t1`@`select#1`) BNL(`t2`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ BNL(t1, t3) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ BNL(`t1`@`select#1`) BNL(`t3`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ BNL(t2, t3) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ BNL(`t2`@`select#1`) BNL(`t3`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +EXPLAIN EXTENDED SELECT /*+ BNL(t2) BNL(t3) */t1.* FROM t1,t2,t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ BNL(`t2`@`select#1`) BNL(`t3`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` join `test`.`t2` join `test`.`t3` +DROP TABLE t1, t2, t3; +set join_cache_level = 8; +# BNL in subquery +set optimizer_switch = DEFAULT; +CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INT); +CREATE TABLE t3 (a INT, b INT, INDEX a (a,b)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (20,4), (30,5); +ANALYZE TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) */ * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index; Using join buffer (incremental, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2); Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL() */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 4 10.00 Using where +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2); Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(@`q`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t1, t2) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 4 10.00 Using where +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2); Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(`t1`@`q`) NO_BNL(`t2`@`q`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(@`subq1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t4@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(`t4`@`subq1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t3@subq1,t4@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(`t3`@`subq1`) NO_BNL(`t4`@`subq1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(@subq1 t3, t4) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(`t3`@`subq1`) NO_BNL(`t4`@`subq1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) NO_BNL(t3, t4) */ t3.b +FROM t3 JOIN t1 t4 ON t3.a = t4.b); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 10.00 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 index a a 10 NULL 3 10.00 Using where; Using index +1 PRIMARY t4 ALL NULL NULL NULL NULL 4 25.00 Using where; FirstMatch(t2) +Warnings: +Note 1003 select /*+ QB_NAME(`q`) NO_BNL(`t3`@`subq1`) NO_BNL(`t4`@`subq1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` semi join (`test`.`t3` join `test`.`t1` `t4`) join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` and `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t4`.`b` = `test`.`t3`.`a` +# Make sure query results are the same for any hints combination +SELECT * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) */ * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL() */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL(t1, t2) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL(@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL(t4@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL(t3@subq1,t4@subq1) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) NO_BNL(@subq1 t3, t4) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +SELECT /*+ QB_NAME(q) */ * +FROM t1 JOIN t2 ON t1.a = t2.a +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) NO_BNL(t3, t4) */ t3.b +FROM t3 JOIN t1 t4 ON t3.a = t4.b); +a b a +3 30 3 +4 40 4 +DROP TABLE t1, t2, t3, t4; +# +# BNL() and NO_BNL() for LEFT JOINs +# +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE TABLE t3 (a int); +CREATE TABLE t4 (a int); +INSERT INTO t1 VALUES (null), (2), (null), (1); +set join_cache_level = 8; +# Two join buffers are employed by default (without hints): +EXPLAIN SELECT t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 +WHERE t1.a OR t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (incremental, BNL join) +# Disabling either of join buffers disables another one automatically due +# to join buffer employment rules: +EXPLAIN SELECT /*+ NO_BNL(t2) */ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 +WHERE t1.a OR t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +EXPLAIN SELECT /*+ NO_BNL(t3) */ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 +WHERE t1.a OR t3.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +# Three join buffers are employed here by default (without hints): +EXPLAIN SELECT t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (incremental, BNL join) +# Disabling either of join buffers disables others automatically due +# to join buffer employment rules: +EXPLAIN SELECT /*+ NO_BNL(t2)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where +EXPLAIN SELECT /*+ NO_BNL(t3)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where +EXPLAIN SELECT /*+ NO_BNL(t4)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where +set join_cache_level=0; +# It is not allowed to enable join buffers on some subset of inner tables +# of an outer join. Either all tables should use join buffers or none: +EXPLAIN SELECT /*+ BNL(t2)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where +EXPLAIN SELECT /*+ BNL(t2, t3)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where +EXPLAIN SELECT /*+ BNL(t2, t3, t4)*/ t1.a +FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 +WHERE t1.a OR t4.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t4 ALL NULL NULL NULL NULL 0 Using where; Using join buffer (incremental, BNL join) +set join_cache_level=8; +INSERT INTO t2 VALUES (1), (2), (null), (1); +# BNLH buffer is used when allowed by JCL setting +EXPLAIN SELECT * FROM t1, t2 WHERE t1.a=t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where +1 SIMPLE t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 Using where; Using join buffer (flat, BNLH join) +# Make sure the hint enables BNL even when JCL=0: +set join_cache_level=0; +EXPLAIN SELECT /*+BNL(t2) */ * FROM t1, t2 WHERE t1.a=t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where +1 SIMPLE t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 Using where; Using join buffer (flat, BNLH join) +set join_cache_level=2; +EXPLAIN SELECT /*+BNL(t2) */ * FROM t1, t2 WHERE t1.a=t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where +1 SIMPLE t2 hash_ALL NULL #hash#$hj 5 test.t1.a 4 Using where; Using join buffer (flat, BNLH join) +DROP TABLE t1,t2,t3,t4; +# +# Mix of BNL/BKA flat and incremental join buffers for OUTER JOINs +# +set optimizer_switch='outer_join_with_cache=on,mrr=on,mrr_sort_keys=on,index_condition_pushdown=on'; +CREATE TABLE t1 (a1 varchar(32)) ; +INSERT INTO t1 VALUES ('s'),('k'); +CREATE TABLE t2 (a2 int PRIMARY KEY, b2 varchar(32)) ; +INSERT INTO t2 VALUES (7,'s'); +CREATE TABLE t3 (a3 int PRIMARY KEY, b3 varchar(32)) ; +INSERT INTO t3 VALUES (7,'s'); +CREATE TABLE t4 (a4 int); +INSERT INTO t4 VALUES (9); +CREATE TABLE t5(a5 int PRIMARY KEY, b5 int) ; +INSERT INTO t5 VALUES (7,0); +ANALYZE TABLES t1, t2, t3, t4, t5; +# Disable join buffering to enable it selectively on particular tables +SET SESSION join_cache_level = 0; +EXPLAIN +SELECT t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a3 1 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where +1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using where +SELECT t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +EXPLAIN +SELECT /*+ BNL(t4) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a3 1 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using where +SELECT /*+ BNL(t4) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a3 1 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 hash_ALL PRIMARY #hash#PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BNLH join) +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +# BNL() hint overrides join_cache_levels from 0 to 3 increasing it to 4 +set join_cache_level = 1; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a3 1 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 hash_ALL PRIMARY #hash#PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BNLH join) +set join_cache_level = 2; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t3.a3 1 Using where +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 hash_ALL PRIMARY #hash#PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BNLH join) +set join_cache_level = 3; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 1 Using where +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using index +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 hash_ALL PRIMARY #hash#PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BNLH join) +# Enable all join buffering capabilities: +SET SESSION join_cache_level = 8; +EXPLAIN +SELECT t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 1 Using where +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using index +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BKAH join); Key-ordered Rowid-ordered scan +SELECT t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +EXPLAIN +SELECT /*+ NO_BNL(t4)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 1 Using where +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using index +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where +1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (flat, BKAH join); Key-ordered Rowid-ordered scan +SELECT /*+ NO_BNL(t4)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +# Disable BKA so the BNLH join buffer will be employed: +EXPLAIN +SELECT /*+ NO_BKA(t5)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 1 Using where +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using index +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 hash_ALL PRIMARY #hash#PRIMARY 4 test.t2.a2 1 Using where; Using join buffer (incremental, BNLH join) +SELECT /*+ NO_BKA(t5)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +EXPLAIN +SELECT /*+ NO_BKA(t5) NO_BNL(t5)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 1 Using where +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using index +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t2.a2 1 Using where +SELECT /*+ NO_BKA(t5) NO_BNL(t5)*/ t4.a4, t5.b5 +FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) +LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +a4 b5 +9 0 +9 NULL +DROP TABLE t1,t2,t3,t4,t5; +# MRR & NO_MRR hint testing +set optimizer_switch=default; +set join_cache_level = 8; +CREATE TABLE t1 +( +f1 int NOT NULL DEFAULT '0', +f2 int NOT NULL DEFAULT '0', +f3 int NOT NULL DEFAULT '0', +INDEX idx1(f2, f3), INDEX idx2(f3) +); +INSERT INTO t1(f1) VALUES (1), (2), (3), (4), (5), (6), (7), (8); +INSERT INTO t1(f2, f3) VALUES (3,4), (3,4); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +set optimizer_switch='mrr=on,mrr_cost_based=off'; +# Check statistics without hint +FLUSH STATUS; +SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +f1 f2 f3 +0 3 4 +0 3 4 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 1 +Handler_read_last 0 +Handler_read_next 2 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 2 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +# Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +f1 f2 f3 +0 3 4 +0 3 4 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 1 +Handler_read_last 0 +Handler_read_next 2 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +# Make sure hints are preserved in a stored procedure body +CREATE PROCEDURE p() SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +SHOW CREATE PROCEDURE p; +Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation +p STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` PROCEDURE `p`() +SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3 utf8mb4 utf8mb4_uca1400_ai_ci utf8mb4_uca1400_ai_ci +FLUSH STATUS; +CALL p(); +f1 f2 f3 +0 3 4 +0 3 4 +SHOW STATUS LIKE 'handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 2 +Handler_read_last 0 +Handler_read_next 2 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +DROP PROCEDURE p; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn off MRR. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select /*+ NO_MRR(`t1`@`select#1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn off MRR. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1 idx2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select /*+ NO_MRR(`t1`@`select#1` `idx2`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn off MRR for unused key. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1 idx1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select /*+ NO_MRR(`t1`@`select#1` `idx1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +set optimizer_switch='mrr=off,mrr_cost_based=off'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 IDX2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1` `IDX2`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR for unused key. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 idx1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1` `idx1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +set optimizer_switch='mrr=off,mrr_cost_based=on'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 idx2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1` `idx2`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +# Turn on MRR for unused key. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 IDX1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range idx1,idx2 idx2 4 NULL 2 100.00 Using index condition; Using where +Warnings: +Note 1003 select /*+ MRR(`t1`@`select#1` `IDX1`) */ `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`f2` AS `f2`,`test`.`t1`.`f3` AS `f3` from `test`.`t1` where `test`.`t1`.`f2` <= 3 and 3 <= `test`.`t1`.`f3` +DROP TABLE t1; +set optimizer_switch=default; +# +# Duplicate hints +# +CREATE TABLE t1 (i INT PRIMARY KEY); +SELECT /*+ BKA() BKA() */ 1; +1 +1 +Warnings: +Warning 4210 Hint BKA() is ignored as conflicting/duplicated +SELECT /*+ BKA(t1) BKA(t1) */ * FROM t1; +i +Warnings: +Warning 4210 Hint BKA(`t1`) is ignored as conflicting/duplicated +SELECT /*+ QB_NAME(q1) BKA(t1@q1) BKA(t1@q1) */ * FROM t1; +i +Warnings: +Warning 4210 Hint BKA(`t1`@`q1`) is ignored as conflicting/duplicated +SELECT /*+ QB_NAME(q1) NO_ICP(@q1 t1 PRIMARY) NO_ICP(@q1 t1 PRIMARY) */ * FROM t1; +i +Warnings: +Warning 4210 Hint NO_ICP(`t1`@`q1` `PRIMARY`) is ignored as conflicting/duplicated +DROP TABLE t1; +# +# Hints inside views are not supported +# +CREATE TABLE t1 (a INT, INDEX idx_a(a)); +INSERT INTO t1 VALUES (1),(2); +CREATE VIEW v1 AS SELECT /*+ NO_MRR(t1 idx_a) */ a FROM t1; +Warnings: +Warning 4214 Optimizer hints are not supported inside view definitions +SELECT * FROM v1; +a +1 +2 +# Make sure hints are not present inside the view definition: +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +EXPLAIN EXTENDED SELECT * FROM v1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL idx_a 5 NULL 2 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` +CREATE OR REPLACE VIEW v1 AS SELECT /*+ NO_MRR(t1 idx_a) BKA(t1)*/ a FROM t1; +Warnings: +Warning 4214 Optimizer hints are not supported inside view definitions +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +ALTER VIEW v1 AS SELECT /*+ QB_NAME(q1)*/ a FROM t1; +Warnings: +Warning 4214 Optimizer hints are not supported inside view definitions +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +SELECT * FROM v1; +a +1 +2 +# Wrong place for the hint, must be simply ignored: +CREATE OR REPLACE VIEW v1 AS SELECT a /*+ NO_ICP(t1 idx_a)*/ FROM t1; +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +# Incorrect hint does not prevent view creation, only a warning generated: +CREATE VIEW v2 AS SELECT /*+ BAD HINT*/ a+10 FROM t1; +Warnings: +Warning 1064 Optimizer hint syntax error near 'BAD HINT*/ a+10 FROM t1' at line 1 +SELECT * FROM v2; +a+10 +11 +12 +EXPLAIN EXTENDED SELECT * FROM v2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL idx_a 5 NULL 2 100.00 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` + 10 AS `a+10` from `test`.`t1` +SHOW CREATE VIEW v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`a` + 10 AS `a+10` from `t1` utf8mb4 utf8mb4_uca1400_ai_ci +DROP VIEW v1, v2; +DROP TABLE t1; +# +# Tests of parser for optimizer hints +# +CREATE TABLE t1 (i INT, j INT); +CREATE INDEX i1 ON t1(i); +CREATE INDEX i2 ON t1(j); + +# invalid hint sequences, must issue warnings: + +SELECT /*+*/ 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 +SELECT /*+ * ** / // /* */ 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 +SELECT /*+ @foo */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '@foo */ 1' at line 1 +SELECT /*+ foo@bar */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'foo@bar */ 1' at line 1 +SELECT /*+ foo @bar */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'foo @bar */ 1' at line 1 +SELECT /*+ `@` */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '`@` */ 1' at line 1 +SELECT /*+ `@foo` */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '`@foo` */ 1' at line 1 +SELECT /*+ `foo@bar` */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '`foo@bar` */ 1' at line 1 +SELECT /*+ `foo @bar` */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '`foo @bar` */ 1' at line 1 +SELECT /*+ BKA( @) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ BKA( @) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ BKA(t1 @) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 + +# We don't support "*/" inside quoted identifiers (syntax error): + +SELECT /*+ BKA(`test*/`) */ 1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '`) */ 1' at line 1 + +# invalid hint sequences, must issue warnings: + +SELECT /*+ NO_ICP() */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+NO_ICP()*/ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ')*/ 1' at line 1 +SELECT /*+ NO_ICP () */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ NO_ICP ( ) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ NO_ICP() */ 1 UNION SELECT 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1 UNION SELECT 1' at line 1 +(SELECT /*+ NO_ICP() */ 1) UNION (SELECT 1); +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1) UNION (SELECT 1)' at line 1 +((SELECT /* + NO_ICP() */ 1)); +1 +1 +UPDATE /*+ NO_ICP() */ t1 SET i = 10; +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ t1 SET i = 10' at line 1 +INSERT /*+ NO_ICP() */ INTO t1 VALUES (); +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ INTO t1 VALUES ()' at line 1 +DELETE /*+ NO_ICP() */ FROM t1 WHERE 1; +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ FROM t1 WHERE 1' at line 1 +SELECT /*+ BKA(a b) */ 1 FROM t1 a, t1 b; +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'b) */ 1 FROM t1 a, t1 b' at line 1 +SELECT /*+ NO_ICP(i1) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `i1`@`select#1` for NO_ICP hint +SELECT /*+ NO_ICP(i1 i2) */ 1 FROM t1; +1 +Warnings: +Warning 4213 Unresolved index name `i1`@`select#1` `i2` for NO_ICP hint +SELECT /*+ NO_ICP(@qb ident) */ 1 FROM t1; +1 +Warnings: +Warning 4211 Query block name `qb` is not found for NO_ICP hint + +# valid hint sequences, no warnings expected: + +SELECT /*+ BKA(t1) */ 1 FROM t1; +1 +EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) */ 1 UNION SELECT /*+ QB_NAME(qb2) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select /*+ QB_NAME(`qb1`) */ 1 AS `1` union /* select#2 */ select /*+ QB_NAME(`qb2`) */ 1 AS `1` +EXPLAIN EXTENDED (SELECT /*+ QB_NAME(qb1) */ 1) UNION (SELECT /*+ QB_NAME(qb2) */ 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 (/* select#1 */ select /*+ QB_NAME(`qb1`) */ 1 AS `1`) union (/* select#2 */ select /*+ QB_NAME(`qb2`) */ 1 AS `1`) +# +# test explainable statements for hint support: +# they should warn with a hint syntax error near "test */" +# +EXPLAIN EXTENDED SELECT /*+ test */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Warning 1064 Optimizer hint syntax error near 'test */ 1' at line 1 +Note 1003 select 1 AS `1` +EXPLAIN EXTENDED INSERT /*+ test */ INTO t1 VALUES (10, 10); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 INSERT t1 ALL NULL NULL NULL NULL NULL 100.00 NULL +Warnings: +Warning 1064 Optimizer hint syntax error near 'test */ INTO t1 VALUES (10, 10)' at line 1 +Note 1003 insert into `test`.`t1` values (10,10) +EXPLAIN EXTENDED UPDATE /*+ test */ t1 SET i = 10 WHERE j = 10; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +Warnings: +Warning 1064 Optimizer hint syntax error near 'test */ t1 SET i = 10 WHERE j = 10' at line 1 +Note 1003 update `test`.`t1` set `test`.`t1`.`i` = 10 where `test`.`t1`.`j` = 10 +EXPLAIN EXTENDED DELETE /*+ test */ FROM t1 WHERE i = 10; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +Warnings: +Warning 1064 Optimizer hint syntax error near 'test */ FROM t1 WHERE i = 10' at line 1 +Note 1003 delete from `test`.`t1` using dual where `test`.`t1`.`i` = 10 + +# non-alphabetic and non-ASCII identifiers, should warn: + +CREATE INDEX 3rd_index ON t1(i, j); +SELECT /*+ NO_ICP(3rd_index) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `3rd_index`@`select#1` for NO_ICP hint +CREATE INDEX $index ON t1(j, i); +SELECT /*+ NO_ICP($index) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `$index`@`select#1` for NO_ICP hint +CREATE TABLE ` quoted name test` (i INT); +SELECT /*+ BKA(` quoted name test`) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name ` quoted name test`@`select#1` for BKA hint +SELECT /*+ BKA(` quoted name test`@`select#1`) */ 1 FROM t1; +1 +Warnings: +Warning 4211 Query block name `select#1` is not found for BKA hint +DROP TABLE ` quoted name test`; +SET SQL_MODE = 'ANSI_QUOTES'; +CREATE TABLE " quoted name test" (i INT); +SELECT /*+ BKA(" quoted name test") */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name " quoted name test"@"select#1" for BKA hint +SELECT /*+ BKA(" quoted name test"@"select#1") */ 1 FROM t1; +1 +Warnings: +Warning 4211 Query block name "select#1" is not found for BKA hint +CREATE TABLE `test1``test2``` (i INT); +SELECT /*+ BKA(`test1``test2```) */ 1; +1 +1 +Warnings: +Warning 4212 Unresolved table name "test1`test2`"@"select#1" for BKA hint +SELECT /*+ BKA("test1""test2""") */ 1; +1 +1 +Warnings: +Warning 4212 Unresolved table name "test1""test2"""@"select#1" for BKA hint +SET SQL_MODE = ''; +# should warn: +SELECT /*+ BKA(" quoted name test") */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name ` quoted name test`@`select#1` for BKA hint +DROP TABLE ` quoted name test`; +DROP TABLE `test1``test2```; +# Valid hints, no warning: +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`*`) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select /*+ QB_NAME(`*`) */ 1 AS `1` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a*`) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select /*+ QB_NAME(`a*`) */ 1 AS `1` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`*b`) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select /*+ QB_NAME(`*b`) */ 1 AS `1` +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a +b`) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select /*+ QB_NAME(`a +b`) */ 1 AS `1` +# Identifiers starting with digits must be supported: +CREATE OR REPLACE TABLE 0a (8a INT, KEY 6a(8a)); +EXPLAIN EXTENDED SELECT /*+ NO_MRR(0a 6a) BKA(0a)*/ 8a FROM 0a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE 0a system NULL NULL NULL NULL 0 0.00 Const row not found +Warnings: +Note 1003 select /*+ BKA(`0a`@`select#1`) NO_MRR(`0a`@`select#1` `6a`) */ NULL AS `8a` from `test`.`0a` +DROP TABLE 0a; +# hint syntax error: empty quoted identifier +EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Warning 1064 Optimizer hint syntax error near '``) */ 1' at line 1 +Note 1003 select 1 AS `1` +SET NAMES utf8; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`\BF``\BF`) */ 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select /*+ QB_NAME(`\BF``\BF`) */ 1 AS `1` +CREATE TABLE tableТ (i INT); +# invalid hints, should warn: +SELECT /*+ BKA(tableТ) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `tableТ`@`select#1` for BKA hint +SELECT /*+ BKA(test@tableТ) */ 1 FROM t1; +1 +Warnings: +Warning 4211 Query block name `tableТ` is not found for BKA hint +DROP TABLE tableТ; +CREATE TABLE таблица (i INT); +SELECT /*+ BKA(`таблица`) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `таблица`@`select#1` for BKA hint +SELECT /*+ BKA(таблица) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `таблица`@`select#1` for BKA hint +SELECT /*+ BKA(test@таблица) */ 1 FROM t1; +1 +Warnings: +Warning 4211 Query block name `таблица` is not found for BKA hint +SELECT /*+ NO_ICP(`\D1`) */ 1 FROM t1; +1 +Warnings: +Warning 4212 Unresolved table name `\D1`@`select#1` for NO_ICP hint +DROP TABLE таблица; + +# derived tables and other subqueries: + +SELECT * FROM (SELECT /*+ DEBUG_HINT3 */ 1) a; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'DEBUG_HINT3 */ 1) a' at line 1 +SELECT (SELECT /*+ DEBUG_HINT3 */ 1); +(SELECT /*+ DEBUG_HINT3 */ 1) +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'DEBUG_HINT3 */ 1)' at line 1 +SELECT 1 FROM DUAL WHERE 1 IN (SELECT /*+ DEBUG_HINT3 */ 1); +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'DEBUG_HINT3 */ 1)' at line 1 + +# invalid hint sequences (should warn): + +SELECT /*+ 10 */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '10 */ 1' at line 1 +SELECT /*+ NO_ICP() */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ NO_ICP(10) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '10) */ 1' at line 1 +SELECT /*+ NO_ICP( */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1 +SELECT /*+ NO_ICP) */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1 +SELECT /*+ NO_ICP(t1 */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1 +SELECT /*+ NO_ICP(t1 ( */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '( */ 1' at line 1 +(SELECT 1) UNION (SELECT /*+ NO_ICP() */ 1); +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1)' at line 1 +INSERT INTO t1 VALUES (1, 1), (2, 2); + +# wrong place for hint, so recognize that stuff as a regular commentary: + +SELECT 1 FROM /*+ regular commentary, not a hint! */ t1; +1 +1 +1 +SELECT 1 FROM /*+ #1 */ t1 WHERE /*+ #2 */ 1 /*+ #3 */; +1 +1 +1 +SELECT 1 FROM /*+ QB_NAME(q1) */ t1 /*+ NO_ICP() */WHERE /*+ NO_MRR(t1) */ 1 /*+ #3 */; +1 +1 +1 +# Warnings expected: +SELECT /*+ NO_ICP() */ 1 +FROM /*+ regular commentary, not a hint! */ t1; +1 +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ 1 +FROM /*+ regular commentary, not a hint! */ t1' at line 1 +SELECT /*+ NO_ICP(t1) bad_hint */ 1 FROM t1; +1 +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near 'bad_hint */ 1 FROM t1' at line 1 +SELECT /*+ +NO_ICP(@qb ident) +*/ 1 FROM t1; +1 +1 +1 +Warnings: +Warning 4211 Query block name `qb` is not found for NO_ICP hint +SELECT /*+ +? bad syntax +*/ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '? bad syntax +*/ 1' at line 2 +SELECT +/*+ ? bad syntax */ 1; +1 +1 +Warnings: +Warning 1064 Optimizer hint syntax error near '? bad syntax */ 1' at line 2 +DROP TABLE t1; +set optimizer_switch = DEFAULT; +set join_cache_level = DEFAULT; +# +# End of 11.7 tests +# diff --git a/mysql-test/main/opt_hints.test b/mysql-test/main/opt_hints.test new file mode 100644 index 0000000000000..d04a892249636 --- /dev/null +++ b/mysql-test/main/opt_hints.test @@ -0,0 +1,1026 @@ +--enable_prepare_warnings +--disable_view_protocol # Since optimizer hints are not supported inside views +SET NAMES utf8mb4; + +--echo # Testing that index names in hints are accent sensitive case insensitive +CREATE TABLE t1 (a INT, ä INT, INDEX idx_a(a), INDEX idx_ä(ä)); +INSERT INTO t1 VALUES (1,1),(2,2); +SELECT /*+ NO_MRR(t1 idx_a) */ a FROM t1; +SELECT /*+ NO_MRR(t1 idx_A) */ a FROM t1; + +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +SELECT /*+ NO_MRR(t1 idx_å) */ a FROM t1; +SELECT /*+ NO_MRR(t1 idx_a, idx_å, idx_A) */ a FROM t1; +--enable_ps_protocol +DROP TABLE t1; + +--echo # Testing that query block names are accent sensitive case insensitive +CREATE TABLE t1 (a INT); +SELECT /*+ QB_NAME(a) BKA(t1@a) BKA(t1@A) */ * FROM t1; +SELECT /*+ QB_NAME(a) BKA(t1@a) BKA(t1@å) */ * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1(f1 INT, f2 INT); +INSERT INTO t1 VALUES +(1,1),(2,2),(3,3); + +CREATE TABLE t2(f1 INT NOT NULL, f2 INT NOT NULL, f3 CHAR(200), KEY(f1, f2)); +INSERT INTO t2 VALUES +(1,1, 'qwerty'),(1,2, 'qwerty'),(1,3, 'qwerty'), +(2,1, 'qwerty'),(2,2, 'qwerty'),(2,3, 'qwerty'), (2,4, 'qwerty'),(2,5, 'qwerty'), +(3,1, 'qwerty'),(3,4, 'qwerty'), +(4,1, 'qwerty'),(4,2, 'qwerty'),(4,3, 'qwerty'), (4,4, 'qwerty'), +(1,1, 'qwerty'),(1,2, 'qwerty'),(1,3, 'qwerty'), +(2,1, 'qwerty'),(2,2, 'qwerty'),(2,3, 'qwerty'), (2,4, 'qwerty'),(2,5, 'qwerty'), +(3,1, 'qwerty'),(3,4, 'qwerty'), +(4,1, 'qwerty'),(4,2, 'qwerty'),(4,3, 'qwerty'), (4,4, 'qwerty'); + +CREATE TABLE t3 (f1 INT NOT NULL, f2 INT, f3 VARCHAR(32), + PRIMARY KEY(f1), KEY f2_idx(f1), KEY f3_idx(f3)); +INSERT INTO t3 VALUES +(1, 1, 'qwerty'), (2, 1, 'ytrewq'), +(3, 2, 'uiop'), (4, 2, 'poiu'), (5, 2, 'lkjh'), +(6, 2, 'uiop'), (7, 2, 'poiu'), (8, 2, 'lkjh'), +(9, 2, 'uiop'), (10, 2, 'poiu'), (11, 2, 'lkjh'), +(12, 2, 'uiop'), (13, 2, 'poiu'), (14, 2, 'lkjh'); +INSERT INTO t3 SELECT f1 + 20, f2, f3 FROM t3; +INSERT INTO t3 SELECT f1 + 40, f2, f3 FROM t3; + +ANALYZE TABLE t1; +ANALYZE TABLE t2; +ANALYZE TABLE t3; + +--echo # NO_RANGE_OPTIMIZATION hint testing +set optimizer_switch=default; + +--disable_ps2_protocol +--disable_cursor_protocol +--echo # Check statistics with no hint +FLUSH STATUS; +SELECT f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +SHOW STATUS LIKE 'handler_read%'; + +--echo # Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +SHOW STATUS LIKE 'handler_read%'; +--enable_ps2_protocol +--enable_cursor_protocol + +EXPLAIN EXTENDED SELECT f1 FROM t3 WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for PRIMARY key +--echo # Should use range access by f2_idx key +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) */ f1 + FROM t3 WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for PRIMARY & f2_idx keys +--echo # Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1 + FROM t3 WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for all keys +--echo # Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3) */ f1 + FROM t3 WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for PRIMARY & f2_idx keys +--echo # Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) NO_RANGE_OPTIMIZATION(t3 f2_idx) */ f1 + FROM t3 WHERE f1 > 30 AND f1 < 33; + +--echo # Create a clone of t3 with cyrillic names +CREATE TABLE таблица (f1 INT NOT NULL, поле2 INT, поле3 VARCHAR(32), + PRIMARY KEY(f1), KEY f2_индекс(f1), KEY f3_индекс(поле3)) + AS SELECT * FROM t3; +ANALYZE TABLE таблица; +--echo # Turn off range access for PRIMARY key +--echo # Should use range access by f2_индекс key +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY) */ f1 + FROM таблица WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for PRIMARY & f2_индекс keys +--echo # Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY, f2_индекс) */ f1 + FROM таблица WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for all keys +--echo # Should use index access +EXPLAIN EXTENDED SELECT /*+ NO_RANGE_OPTIMIZATION(таблица) */ f1 + FROM таблица WHERE f1 > 30 AND f1 < 33; +--echo # Turn off range access for PRIMARY & f2_индекс keys +--echo # Should use index access +EXPLAIN EXTENDED + SELECT /*+ NO_RANGE_OPTIMIZATION(таблица PRIMARY) NO_RANGE_OPTIMIZATION(таблица f2_индекс) */ f1 + FROM таблица WHERE f1 > 30 AND f1 < 33; +DROP TABLE таблица; + +--echo # NO_ICP hint testing +set optimizer_switch='index_condition_pushdown=on'; + +CREATE TABLE t4 (x INT, y INT, KEY x_idx(x), KEY y_idx(y)); +INSERT INTO t4 (x) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13); +UPDATE t4 SET y=x; + +EXPLAIN EXTENDED SELECT * FROM + (SELECT t4.x, t5.y FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; + +EXPLAIN EXTENDED SELECT * FROM + (SELECT /*+ NO_ICP(t5 x_idx, y_idx) */ t4.x, t5.y FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; + +EXPLAIN EXTENDED SELECT /*+ NO_ICP(t5@qb1 x_idx) */ * FROM + (SELECT /*+ QB_NAME(QB1) */ t4.x, t5.y FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; + +--echo # Cyrillic query block name +EXPLAIN EXTENDED SELECT /*+ NO_ICP(t5@блок1 x_idx) */ * FROM + (SELECT /*+ QB_NAME(блок1) */ t4.x, t5.y FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; + + +--echo # Expected warning for z_idx key, unresolved name. +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED SELECT * FROM + (SELECT /*+ NO_ICP(t5 y_idx, x_idx, z_idx) */ t4.x, t5.y FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; +--enable_ps_protocol + +--echo # ICP should still be used +EXPLAIN EXTENDED SELECT * FROM + (SELECT /*+ NO_ICP(t5 y_idx) */ t4.x, t5.y FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0) AS TD; + +--echo # BKA & NO_BKA hint testing +set optimizer_switch = DEFAULT; +set optimizer_switch = 'mrr=on,mrr_cost_based=off'; +set join_cache_level = 8; + +CREATE TABLE t10(a INT); +INSERT INTO t10 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +CREATE TABLE t11(a INT); +INSERT INTO t11 SELECT A.a + B.a* 10 + C.a * 100 from t10 A, t10 B, t10 C; +CREATE TABLE t12(a INT, b INT); +INSERT INTO t12 SELECT a,a from t10; +CREATE TABLE t13(a INT, b INT, c INT, filler CHAR(100), key (a,b)); +INSERT INTO t13 select a,a,a, 'filler-data' FROM t11; + +--echo # Make sure BKA is expected to be used when there are no hints +EXPLAIN +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +--echo # Disable BKA +set optimizer_switch='join_cache_bka=off'; +EXPLAIN EXTENDED +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +--disable_ps2_protocol +--disable_cursor_protocol +--echo # Check statistics without hint +FLUSH STATUS; +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +SHOW STATUS LIKE 'handler_read%'; + +--echo # Check statistics with hint +FLUSH STATUS; +SELECT /*+ BKA() */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +SHOW STATUS LIKE 'handler_read%'; +--enable_ps2_protocol +--enable_cursor_protocol + +EXPLAIN EXTENDED SELECT /*+ BKA(t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ BKA() */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ BKA(t12) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(QB1) BKA(t13@QB1) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +--echo # Hint overrides both join_cache_level and optimizer switch +set join_cache_level = 0; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +set join_cache_level = 2; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +set join_cache_level = 4; +EXPLAIN EXTENDED SELECT /*+ BKA(t12, t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +set join_cache_level = 8; + +--echo # Enable BKA +set optimizer_switch='join_cache_bka=on'; + +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ NO_BKA() */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t12, t13) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ NO_BKA(t12) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(QB1) NO_BKA(t13@QB1) */ * FROM t12, t13 + WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +--echo # UPDATE|DELETE|INSERT|REPLACE hint testing +EXPLAIN EXTENDED UPDATE t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + +--echo # Turn off range access for PRIMARY key. +--echo # Range access should be used for f2_idx key. +EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY) */ t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT /*+ BKA(t2) NO_BNL(t1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + +--echo # Turn off range access for all keys. +EXPLAIN EXTENDED UPDATE /*+ NO_RANGE_OPTIMIZATION(t3) */ t3 +SET f3 = 'mnbv' WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + +EXPLAIN EXTENDED DELETE FROM t3 +WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + +--echo # Turn off range access. Range access should not be used. +EXPLAIN EXTENDED +DELETE /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) NO_BNL(t1@QB1) */ FROM t3 +WHERE f1 > 30 AND f1 < 33 AND (t3.f1, t3.f2, t3.f3) IN + (SELECT /*+ QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND + t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1); + +--echo # Make sure ICP is expected to be used when there are no hints +EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) + (SELECT t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP. ICP should not be used. +EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3) + (SELECT /*+ NO_ICP(t5) */t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP for a particular table +EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3) + (SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP for a particular table and a key +EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3) + (SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Make sure ICP is expected to be used when there are no hints +EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3) + (SELECT t4.x, t5.y, 'filler' FROM t4, t4 t5 WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP. ICP should not be used. +EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3) + (SELECT /*+ NO_ICP(t5) */t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP for a particular table +EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3) + (SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Turn off ICP for a particular table and a key +EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3) + (SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5 + WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0); + +--echo # Misc tests + +--echo # Should issue warning +EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) QB_NAME(qb1 ) */ * FROM t2; +--echo # Should issue warning +EXPLAIN EXTENDED SELECT /*+ BKA(@qb1) QB_NAME(qb1) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1; + +--echo # Should not crash +PREPARE stmt1 FROM "SELECT /*+ BKA(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2 +WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1 and t1.f2 and t2.f2 + 1 >= t1.f1 + 1"; +EXECUTE stmt1; +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; + +--echo # Check use of alias +set optimizer_switch='join_cache_bka=off'; +EXPLAIN EXTENDED +SELECT * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); + +--echo # Turn on BKA for multiple tables. BKA should be used for tbl13. +EXPLAIN EXTENDED SELECT /*+ BKA(tbl12, tbl13) */ * FROM t12 tbl12, t13 tbl13 +WHERE tbl12.a=tbl13.a AND (tbl13.b+1 <= tbl13.b+1); + +--echo # Print warnings for nonexistent names +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED +SELECT /*+ BKA(t2) NO_BNL(t1) BKA(t3) NO_RANGE_OPTIMIZATION(t3 idx1) NO_RANGE_OPTIMIZATION(t3) */ +t2.f1, t2.f2, t2.f3 FROM t1,t2 WHERE t1.f1=t2.f1 AND +t2.f2 BETWEEN t1.f1 AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1; +--enable_ps_protocol + +--echo # Check illegal syntax +EXPLAIN EXTENDED SELECT /*+ BKA(qb1 t3@qb1) */ f2 FROM + (SELECT /*+ QB_NAME(qb1) */ f2, f3, f1 FROM t3 WHERE f1 > 2 AND f3 = 'poiu') AS TD + WHERE TD.f1 > 2 AND TD.f3 = 'poiu'; + +--echo # Check illegal syntax +EXPLAIN EXTENDED SELECT * FROM + (SELECT /*+ QB_NAME(qb1) BKA(@qb1 t1@qb1, t2@qb1, t3) */ t2.f1, t2.f2, t2.f3 FROM t1,t2,t3) tt; + +--echo # Check '@qb_name table_name' syntax +EXPLAIN EXTENDED SELECT /*+ BKA(@qb1 t13) */ * FROM (SELECT /*+ QB_NAME(QB1) */ t12.a, t13.b FROM t12, t13 +WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1)) AS s1; + +--echo # Check that original table name is not recognized if alias is used. +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED SELECT /*+ BKA(tbl2) */ * FROM t12 tbl12, t13 tbl13 +WHERE tbl12.a=tbl13.a AND (tbl13.b+1 <= tbl13.b+1); +--enable_ps_protocol + +--disable_ps2_protocol +--disable_cursor_protocol +--echo # Check that PS and conventional statements give the same result. +FLUSH STATUS; +SELECT /*+ BKA(t13) */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1); +SHOW STATUS LIKE 'handler_read%'; +--enable_ps2_protocol +--enable_cursor_protocol + +PREPARE stmt1 FROM "SELECT /*+ BKA(t13) */ * FROM t12, t13 WHERE t12.a=t13.a AND (t13.b+1 <= t13.b+1)"; +FLUSH STATUS; +EXECUTE stmt1; +SHOW STATUS LIKE 'handler_read%'; + +FLUSH STATUS; +EXECUTE stmt1; +SHOW STATUS LIKE 'handler_read%'; + +DEALLOCATE PREPARE stmt1; + +DROP TABLE t1, t2, t3, t10, t11, t12, t13; + +--echo # +--echo # BNL and NO_BNL hint testing +--echo # + +set optimizer_switch=default; + +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(2,2); +CREATE TABLE t2 (a INT, b INT); +INSERT INTO t2 VALUES (1,1),(2,2); +CREATE TABLE t3 (a INT, b INT); +INSERT INTO t3 VALUES (1,1),(2,2); + +--disable_ps2_protocol +--disable_cursor_protocol +--echo # Check statistics without hint +FLUSH STATUS; +SELECT t1.* FROM t1,t2,t3; +SHOW STATUS LIKE 'handler_read%'; + +--echo # Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_BNL() */t1.* FROM t1,t2,t3; +SHOW STATUS LIKE 'handler_read%'; +--enable_ps2_protocol +--enable_cursor_protocol + +EXPLAIN EXTENDED SELECT t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ NO_BNL() */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ NO_BNL(t2, t3) */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ NO_BNL(t1, t3) */t1.* FROM t1,t2,t3; + +--echo # Make sure query results are the same for any hints combination +--sorted_result +SELECT * FROM t1,t2,t3; +--sorted_result +SELECT /*+ NO_BNL(t1, t2) */* FROM t1,t2,t3; +--sorted_result +SELECT /*+ NO_BNL(t1, t3) */* FROM t1,t2,t3; +--sorted_result +SELECT /*+ NO_BNL(t2, t3) */* FROM t1,t2,t3; + +--echo # BNL() overrides current join_cache_level setting +set join_cache_level = 0; +EXPLAIN EXTENDED SELECT t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ BNL() */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ BNL(t1, t2) */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ BNL(t1, t3) */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ BNL(t2, t3) */t1.* FROM t1,t2,t3; +EXPLAIN EXTENDED SELECT /*+ BNL(t2) BNL(t3) */t1.* FROM t1,t2,t3; + +DROP TABLE t1, t2, t3; +set join_cache_level = 8; + +--echo # BNL in subquery +set optimizer_switch = DEFAULT; +CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INT); +CREATE TABLE t3 (a INT, b INT, INDEX a (a,b)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (20,4), (30,5); +ANALYZE TABLE t1, t2, t3; + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) */ * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL() */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t1, t2) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t4@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(t3@subq1,t4@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) NO_BNL(@subq1 t3, t4) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +EXPLAIN EXTENDED SELECT /*+ QB_NAME(q) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) NO_BNL(t3, t4) */ t3.b + FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +--echo # Make sure query results are the same for any hints combination +# --sorted_result +SELECT * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +# --sorted_result +SELECT /*+ QB_NAME(q) */ * FROM t1 JOIN t2 ON t1.a = t2.a WHERE +t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL() */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL(t1, t2) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL(@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL(t4@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL(t3@subq1,t4@subq1) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) NO_BNL(@subq1 t3, t4) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ t3.b FROM t3 JOIN t1 t4 ON t3.a = t4.b); +--sorted_result +SELECT /*+ QB_NAME(q) */ * + FROM t1 JOIN t2 ON t1.a = t2.a + WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) NO_BNL(t3, t4) */ t3.b + FROM t3 JOIN t1 t4 ON t3.a = t4.b); + +DROP TABLE t1, t2, t3, t4; + +--echo # +--echo # BNL() and NO_BNL() for LEFT JOINs +--echo # + +CREATE TABLE t1 (a int); +CREATE TABLE t2 (a int); +CREATE TABLE t3 (a int); +CREATE TABLE t4 (a int); + +INSERT INTO t1 VALUES (null), (2), (null), (1); + +set join_cache_level = 8; + +--echo # Two join buffers are employed by default (without hints): +EXPLAIN SELECT t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 + WHERE t1.a OR t3.a; + +--echo # Disabling either of join buffers disables another one automatically due +--echo # to join buffer employment rules: +EXPLAIN SELECT /*+ NO_BNL(t2) */ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 + WHERE t1.a OR t3.a; + +EXPLAIN SELECT /*+ NO_BNL(t3) */ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a) ON 0 + WHERE t1.a OR t3.a; + +--echo # Three join buffers are employed here by default (without hints): +EXPLAIN SELECT t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; + +--echo # Disabling either of join buffers disables others automatically due +--echo # to join buffer employment rules: +EXPLAIN SELECT /*+ NO_BNL(t2)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; +EXPLAIN SELECT /*+ NO_BNL(t3)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; +EXPLAIN SELECT /*+ NO_BNL(t4)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; + +set join_cache_level=0; +--echo # It is not allowed to enable join buffers on some subset of inner tables +--echo # of an outer join. Either all tables should use join buffers or none: +EXPLAIN SELECT /*+ BNL(t2)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; +EXPLAIN SELECT /*+ BNL(t2, t3)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; +EXPLAIN SELECT /*+ BNL(t2, t3, t4)*/ t1.a + FROM t1 LEFT JOIN (t2 LEFT JOIN (t3 LEFT JOIN t4 ON 1) ON t2.a) ON 0 + WHERE t1.a OR t4.a; + +set join_cache_level=8; +INSERT INTO t2 VALUES (1), (2), (null), (1); +--echo # BNLH buffer is used when allowed by JCL setting +EXPLAIN SELECT * FROM t1, t2 WHERE t1.a=t2.a; + +--echo # Make sure the hint enables BNL even when JCL=0: +set join_cache_level=0; +EXPLAIN SELECT /*+BNL(t2) */ * FROM t1, t2 WHERE t1.a=t2.a; + +set join_cache_level=2; +EXPLAIN SELECT /*+BNL(t2) */ * FROM t1, t2 WHERE t1.a=t2.a; + +DROP TABLE t1,t2,t3,t4; + + +--echo # +--echo # Mix of BNL/BKA flat and incremental join buffers for OUTER JOINs +--echo # + +set optimizer_switch='outer_join_with_cache=on,mrr=on,mrr_sort_keys=on,index_condition_pushdown=on'; + +CREATE TABLE t1 (a1 varchar(32)) ; +INSERT INTO t1 VALUES ('s'),('k'); + +CREATE TABLE t2 (a2 int PRIMARY KEY, b2 varchar(32)) ; +INSERT INTO t2 VALUES (7,'s'); + +CREATE TABLE t3 (a3 int PRIMARY KEY, b3 varchar(32)) ; +INSERT INTO t3 VALUES (7,'s'); + +CREATE TABLE t4 (a4 int); +INSERT INTO t4 VALUES (9); + +CREATE TABLE t5(a5 int PRIMARY KEY, b5 int) ; +INSERT INTO t5 VALUES (7,0); +--disable_result_log +ANALYZE TABLES t1, t2, t3, t4, t5; +--enable_result_log + +--echo # Disable join buffering to enable it selectively on particular tables +SET SESSION join_cache_level = 0; +EXPLAIN +SELECT t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +EXPLAIN +SELECT /*+ BNL(t4) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT /*+ BNL(t4) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +--echo # BNL() hint overrides join_cache_levels from 0 to 3 increasing it to 4 +set join_cache_level = 1; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +set join_cache_level = 2; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +set join_cache_level = 3; +EXPLAIN +SELECT /*+ BNL(t4, t5) */ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +--echo # Enable all join buffering capabilities: +SET SESSION join_cache_level = 8; +EXPLAIN +SELECT t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +EXPLAIN +SELECT /*+ NO_BNL(t4)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT /*+ NO_BNL(t4)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +--echo # Disable BKA so the BNLH join buffer will be employed: +EXPLAIN +SELECT /*+ NO_BKA(t5)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT /*+ NO_BKA(t5)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +EXPLAIN +SELECT /*+ NO_BKA(t5) NO_BNL(t5)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; +SELECT /*+ NO_BKA(t5) NO_BNL(t5)*/ t4.a4, t5.b5 + FROM ((t1 LEFT JOIN (t2 JOIN t3 ON t2.a2 = t3.a3) ON t2.b2 = t1.a1) + LEFT JOIN t4 ON t4.a4 <> 0) LEFT JOIN t5 ON t5.a5 = t2.a2; + +DROP TABLE t1,t2,t3,t4,t5; + + +--echo # MRR & NO_MRR hint testing +set optimizer_switch=default; +set join_cache_level = 8; + +CREATE TABLE t1 +( + f1 int NOT NULL DEFAULT '0', + f2 int NOT NULL DEFAULT '0', + f3 int NOT NULL DEFAULT '0', + INDEX idx1(f2, f3), INDEX idx2(f3) +); + +INSERT INTO t1(f1) VALUES (1), (2), (3), (4), (5), (6), (7), (8); +INSERT INTO t1(f2, f3) VALUES (3,4), (3,4); +ANALYZE TABLE t1; + +set optimizer_switch='mrr=on,mrr_cost_based=off'; + +--disable_ps2_protocol +--disable_cursor_protocol +--echo # Check statistics without hint +FLUSH STATUS; +SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +SHOW STATUS LIKE 'handler_read%'; + +--echo # Check statistics with hint +FLUSH STATUS; +SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +SHOW STATUS LIKE 'handler_read%'; + +--echo # Make sure hints are preserved in a stored procedure body +CREATE PROCEDURE p() SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +SHOW CREATE PROCEDURE p; +FLUSH STATUS; +CALL p(); +SHOW STATUS LIKE 'handler_read%'; + +DROP PROCEDURE p; +--enable_ps2_protocol +--enable_cursor_protocol + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn off MRR. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn off MRR. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1 idx2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn off MRR for unused key. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1 idx1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; + +set optimizer_switch='mrr=off,mrr_cost_based=off'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 IDX2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR for unused key. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 idx1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; + +set optimizer_switch='mrr=off,mrr_cost_based=on'; + +EXPLAIN EXTENDED SELECT * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR. MRR should be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 idx2) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; +--echo # Turn on MRR for unused key. MRR should not be used. +EXPLAIN EXTENDED SELECT /*+ MRR(t1 IDX1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3; + +DROP TABLE t1; + +set optimizer_switch=default; + +--echo # +--echo # Duplicate hints +--echo # + +CREATE TABLE t1 (i INT PRIMARY KEY); + +SELECT /*+ BKA() BKA() */ 1; +SELECT /*+ BKA(t1) BKA(t1) */ * FROM t1; +SELECT /*+ QB_NAME(q1) BKA(t1@q1) BKA(t1@q1) */ * FROM t1; +SELECT /*+ QB_NAME(q1) NO_ICP(@q1 t1 PRIMARY) NO_ICP(@q1 t1 PRIMARY) */ * FROM t1; + +DROP TABLE t1; + +--echo # +--echo # Hints inside views are not supported +--echo # +CREATE TABLE t1 (a INT, INDEX idx_a(a)); +INSERT INTO t1 VALUES (1),(2); + +CREATE VIEW v1 AS SELECT /*+ NO_MRR(t1 idx_a) */ a FROM t1; +SELECT * FROM v1; +--echo # Make sure hints are not present inside the view definition: +SHOW CREATE VIEW v1; +EXPLAIN EXTENDED SELECT * FROM v1; + +CREATE OR REPLACE VIEW v1 AS SELECT /*+ NO_MRR(t1 idx_a) BKA(t1)*/ a FROM t1; +SHOW CREATE VIEW v1; + +ALTER VIEW v1 AS SELECT /*+ QB_NAME(q1)*/ a FROM t1; +SHOW CREATE VIEW v1; +SELECT * FROM v1; + +--echo # Wrong place for the hint, must be simply ignored: +CREATE OR REPLACE VIEW v1 AS SELECT a /*+ NO_ICP(t1 idx_a)*/ FROM t1; +SHOW CREATE VIEW v1; + +--echo # Incorrect hint does not prevent view creation, only a warning generated: +CREATE VIEW v2 AS SELECT /*+ BAD HINT*/ a+10 FROM t1; +SELECT * FROM v2; +EXPLAIN EXTENDED SELECT * FROM v2; +SHOW CREATE VIEW v2; + +DROP VIEW v1, v2; +DROP TABLE t1; + +--echo # +--echo # Tests of parser for optimizer hints +--echo # + +CREATE TABLE t1 (i INT, j INT); +CREATE INDEX i1 ON t1(i); +CREATE INDEX i2 ON t1(j); + +--echo +--echo # invalid hint sequences, must issue warnings: +--echo +SELECT /*+*/ 1; +SELECT /*+ */ 1; +SELECT /*+ * ** / // /* */ 1; +SELECT /*+ @ */ 1; +SELECT /*+ @foo */ 1; +SELECT /*+ foo@bar */ 1; +SELECT /*+ foo @bar */ 1; +SELECT /*+ `@` */ 1; +SELECT /*+ `@foo` */ 1; +SELECT /*+ `foo@bar` */ 1; +SELECT /*+ `foo @bar` */ 1; +SELECT /*+ BKA( @) */ 1; +SELECT /*+ BKA( @) */ 1; +SELECT /*+ BKA(t1 @) */ 1; + +--echo +--echo # We don't support "*/" inside quoted identifiers (syntax error): +--echo + +--error ER_PARSE_ERROR +SELECT /*+ BKA(`test*/`) */ 1; + +--echo +--echo # invalid hint sequences, must issue warnings: +--echo +SELECT /*+ NO_ICP() */ 1; +SELECT /*+NO_ICP()*/ 1; +SELECT /*+ NO_ICP () */ 1; +SELECT /*+ NO_ICP ( ) */ 1; + +SELECT /*+ NO_ICP() */ 1 UNION SELECT 1; +(SELECT /*+ NO_ICP() */ 1) UNION (SELECT 1); + +((SELECT /* + NO_ICP() */ 1)); + +UPDATE /*+ NO_ICP() */ t1 SET i = 10; +INSERT /*+ NO_ICP() */ INTO t1 VALUES (); +DELETE /*+ NO_ICP() */ FROM t1 WHERE 1; + + +SELECT /*+ BKA(a b) */ 1 FROM t1 a, t1 b; + +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +SELECT /*+ NO_ICP(i1) */ 1 FROM t1; +SELECT /*+ NO_ICP(i1 i2) */ 1 FROM t1; +--enable_ps_protocol +SELECT /*+ NO_ICP(@qb ident) */ 1 FROM t1; + +--echo +--echo # valid hint sequences, no warnings expected: +--echo +SELECT /*+ BKA(t1) */ 1 FROM t1; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) */ 1 UNION SELECT /*+ QB_NAME(qb2) */ 1; +EXPLAIN EXTENDED (SELECT /*+ QB_NAME(qb1) */ 1) UNION (SELECT /*+ QB_NAME(qb2) */ 1); + +--echo # +--echo # test explainable statements for hint support: +--echo # they should warn with a hint syntax error near "test */" +--echo # + +EXPLAIN EXTENDED SELECT /*+ test */ 1; +EXPLAIN EXTENDED INSERT /*+ test */ INTO t1 VALUES (10, 10); +EXPLAIN EXTENDED UPDATE /*+ test */ t1 SET i = 10 WHERE j = 10; +EXPLAIN EXTENDED DELETE /*+ test */ FROM t1 WHERE i = 10; + +--echo +--echo # non-alphabetic and non-ASCII identifiers, should warn: +--echo + +CREATE INDEX 3rd_index ON t1(i, j); + +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +SELECT /*+ NO_ICP(3rd_index) */ 1 FROM t1; + +CREATE INDEX $index ON t1(j, i); +SELECT /*+ NO_ICP($index) */ 1 FROM t1; + +CREATE TABLE ` quoted name test` (i INT); +SELECT /*+ BKA(` quoted name test`) */ 1 FROM t1; +SELECT /*+ BKA(` quoted name test`@`select#1`) */ 1 FROM t1; +DROP TABLE ` quoted name test`; + +SET SQL_MODE = 'ANSI_QUOTES'; + +CREATE TABLE " quoted name test" (i INT); +SELECT /*+ BKA(" quoted name test") */ 1 FROM t1; +SELECT /*+ BKA(" quoted name test"@"select#1") */ 1 FROM t1; + +CREATE TABLE `test1``test2``` (i INT); + +SELECT /*+ BKA(`test1``test2```) */ 1; +SELECT /*+ BKA("test1""test2""") */ 1; + +SET SQL_MODE = ''; +--echo # should warn: +SELECT /*+ BKA(" quoted name test") */ 1 FROM t1; + +DROP TABLE ` quoted name test`; +DROP TABLE `test1``test2```; +--enable_ps_protocol + +--echo # Valid hints, no warning: +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`*`) */ 1; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a*`) */ 1; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`*b`) */ 1; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a +b`) */ 1; + +--echo # Identifiers starting with digits must be supported: +CREATE OR REPLACE TABLE 0a (8a INT, KEY 6a(8a)); +EXPLAIN EXTENDED SELECT /*+ NO_MRR(0a 6a) BKA(0a)*/ 8a FROM 0a; +DROP TABLE 0a; + +--echo # hint syntax error: empty quoted identifier +EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1; + +SET NAMES utf8; +EXPLAIN EXTENDED SELECT /*+ QB_NAME(`\BF``\BF`) */ 1; + +CREATE TABLE tableТ (i INT); + +--echo # invalid hints, should warn: + +# Warnings "Unresolved table/index name..." are generated during both prepare +# and execution stages. So disable PS protocol to avoid duplication +--disable_ps_protocol +SELECT /*+ BKA(tableТ) */ 1 FROM t1; +SELECT /*+ BKA(test@tableТ) */ 1 FROM t1; +DROP TABLE tableТ; + +CREATE TABLE таблица (i INT); + +SELECT /*+ BKA(`таблица`) */ 1 FROM t1; +SELECT /*+ BKA(таблица) */ 1 FROM t1; +SELECT /*+ BKA(test@таблица) */ 1 FROM t1; + +SELECT /*+ NO_ICP(`\D1`) */ 1 FROM t1; + +DROP TABLE таблица; +--enable_ps_protocol + +--echo +--echo # derived tables and other subqueries: +--echo + +SELECT * FROM (SELECT /*+ DEBUG_HINT3 */ 1) a; +SELECT (SELECT /*+ DEBUG_HINT3 */ 1); +SELECT 1 FROM DUAL WHERE 1 IN (SELECT /*+ DEBUG_HINT3 */ 1); + +--echo +--echo # invalid hint sequences (should warn): +--echo +SELECT /*+ 10 */ 1; +SELECT /*+ NO_ICP() */ 1; +--disable_ps_protocol # to avoid warnings duplication, see details above +SELECT /*+ NO_ICP(10) */ 1; +--enable_ps_protocol +SELECT /*+ NO_ICP( */ 1; +SELECT /*+ NO_ICP) */ 1; +SELECT /*+ NO_ICP(t1 */ 1; +SELECT /*+ NO_ICP(t1 ( */ 1; +(SELECT 1) UNION (SELECT /*+ NO_ICP() */ 1); + +INSERT INTO t1 VALUES (1, 1), (2, 2); + +--echo +--echo # wrong place for hint, so recognize that stuff as a regular commentary: +--echo + +SELECT 1 FROM /*+ regular commentary, not a hint! */ t1; +SELECT 1 FROM /*+ #1 */ t1 WHERE /*+ #2 */ 1 /*+ #3 */; +SELECT 1 FROM /*+ QB_NAME(q1) */ t1 /*+ NO_ICP() */WHERE /*+ NO_MRR(t1) */ 1 /*+ #3 */; + +--echo # Warnings expected: +SELECT /*+ NO_ICP() */ 1 + FROM /*+ regular commentary, not a hint! */ t1; + +SELECT /*+ NO_ICP(t1) bad_hint */ 1 FROM t1; + +SELECT /*+ + NO_ICP(@qb ident) +*/ 1 FROM t1; + +SELECT /*+ + ? bad syntax +*/ 1; + +SELECT +/*+ ? bad syntax */ 1; + +DROP TABLE t1; +set optimizer_switch = DEFAULT; +set join_cache_level = DEFAULT; +--echo # +--echo # End of 11.7 tests +--echo # diff --git a/mysql-test/main/opt_hints_join_order.result b/mysql-test/main/opt_hints_join_order.result new file mode 100644 index 0000000000000..bc96885e9945b --- /dev/null +++ b/mysql-test/main/opt_hints_join_order.result @@ -0,0 +1,1326 @@ +set default_storage_engine=innodb; +CREATE TABLE t1 (f1 INT NOT NULL); +INSERT INTO t1 VALUES (9),(0), (7); +CREATE TABLE t2 (f1 INT NOT NULL); +INSERT INTO t2 VALUES +(5),(3),(0),(3),(1),(0),(1),(7),(1),(0),(0),(8),(4),(9),(0),(2),(0),(8),(5),(1); +CREATE TABLE t3 (f1 INT NOT NULL); +INSERT INTO t3 VALUES (9),(0), (7), (4), (5); +CREATE TABLE t4 (f1 INT NOT NULL); +INSERT INTO t4 VALUES (0), (7); +CREATE TABLE t5 (f1 INT NOT NULL, PRIMARY KEY(f1)); +INSERT INTO t5 VALUES (7); +CREATE TABLE t6(f1 INT NOT NULL, PRIMARY KEY(f1)); +INSERT INTO t6 VALUES (7); +ANALYZE TABLE t1, t2, t3, t4, t5, t6; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +test.t4 analyze status Engine-independent statistics collected +test.t4 analyze status OK +test.t5 analyze status Engine-independent statistics collected +test.t5 analyze status OK +test.t6 analyze status Engine-independent statistics collected +test.t6 analyze status OK +set join_cache_level = 8; +# Warning expected: hint must be ignored as malformed (no table names) +EXPLAIN EXTENDED +SELECT /*+ JOIN_PREFIX()*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4215 Hint JOIN_PREFIX() is ignored as malformed +Note 1003 select count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4215 Hint JOIN_PREFIX(@`q1` ) is ignored as malformed +Note 1003 select /*+ QB_NAME(`q1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Hint must be ignored as malformed (table names are not allowed for the fixed order) +EXPLAIN EXTENDED +SELECT /*+ JOIN_FIXED_ORDER(t2)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4215 Hint JOIN_FIXED_ORDER(`t2`) is ignored as malformed +Note 1003 select count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Hint is applied: QB name is allowed for the fixed order +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qb1)*/ count(*) FROM t2, t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) JOIN_FIXED_ORDER(@`qb1`) */ straight_join count(0) AS `count(*)` from `test`.`t2` join `test`.`t1` +# Invalid QB name for the fixed order +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qb3)*/ count(*) FROM t2, t1; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4211 Query block name `qb3` is not found for JOIN_FIXED_ORDER hint +Note 1003 select /*+ QB_NAME(`qb1`) */ count(0) AS `count(*)` from `test`.`t2` join `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ JOIN_FIXED_ORDER()*/ STRAIGHT_JOIN count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ JOIN_FIXED_ORDER(@`select#1`) */ straight_join count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# JOIN_FIXED_ORDER() cannot be combined with other join order hints +EXPLAIN EXTENDED +SELECT /*+ JOIN_PREFIX(t2) JOIN_FIXED_ORDER()*/ count(*) FROM t1, t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4210 Hint JOIN_FIXED_ORDER() is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ JOIN_FIXED_ORDER() JOIN_PREFIX(t2)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_FIXED_ORDER(@`select#1`) */ straight_join count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Multiple join order hints of same type are not allowed (except JOIN_ORDER()) +EXPLAIN EXTENDED +SELECT /*+ JOIN_PREFIX(t1) JOIN_PREFIX(t2)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ JOIN_SUFFIX(t1) JOIN_SUFFIX(t2)*/ count(*) FROM t1, t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Multiple JOIN_ORDER() hints are applied +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2,t1) JOIN_ORDER(@q1 t3,t2)*/ count(*) +FROM t1, t2, t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 5 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_ORDER(@`q1` `t2`,`t1`) JOIN_ORDER(@`q1` `t3`,`t2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Variation of last syntax +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2@q1, t1) JOIN_ORDER(t3, t2@q1)*/ count(*) +FROM t1, t2, t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 5 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_ORDER(@`q1` `t2`@`q1`,`t1`) JOIN_ORDER(@`q1` `t3`,`t2`@`q1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Invalid mix of notations +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1 t2, t1@q1)*/ count(*) +FROM t1, t2, t3; +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 +1 SIMPLE t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (incremental, BNL join) +Warnings: +Warning 1064 Optimizer hint syntax error near '@q1)*/ count(*) +FROM t1, t2, t3' at line 2 +Note 1003 select count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Hint is correct +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(t1@qb1, t2)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) JOIN_SUFFIX(@`qb1` `t1`@`qb1`,`t2`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_PREFIX(@qb1, t2)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 1064 Optimizer hint syntax error near ', t2)*/ count(*) FROM t1, t2' at line 2 +Note 1003 select count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Invalid table name +EXPLAIN EXTENDED +SELECT /*+ JOIN_SUFFIX(t3)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4212 Unresolved table name `t3` for JOIN_SUFFIX hint +Note 1003 select count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +# Invalid query block name +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(@qbXXX t1)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4211 Query block name `qbXXX` is not found for JOIN_SUFFIX hint +Note 1003 select /*+ QB_NAME(`qb1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(t1, t2@qb3)*/ count(*) FROM t1, t2; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4212 Unresolved table name `t2`@`qb3` for JOIN_SUFFIX hint +Note 1003 select /*+ QB_NAME(`qb1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qbXXX)*/ count(*) FROM t2, t1; +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 +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +Warnings: +Warning 4211 Query block name `qbXXX` is not found for JOIN_FIXED_ORDER hint +Note 1003 select /*+ QB_NAME(`qb1`) */ count(0) AS `count(*)` from `test`.`t2` join `test`.`t1` +# Warning expected as the hint must not confuse `t2` in the subquery +# with `t2` in the join +EXPLAIN EXTENDED +SELECT /*+ JOIN_PREFIX(t2@subq)*/ (SELECT /*+ QB_NAME(subq)*/ max(f1) FROM t2) as SQ, f1 +FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +2 SUBQUERY t2 ALL NULL NULL NULL NULL 20 100.00 +Warnings: +Warning 4212 Unresolved table name `t2`@`subq` for JOIN_PREFIX hint +Note 1003 /* select#1 */ select (/* select#2 */ select /*+ QB_NAME(`subq`) */ max(`test`.`t2`.`f1`) from `test`.`t2`) AS `SQ`,`test`.`t2`.`f1` AS `f1` from `test`.`t2` +# No more than 64 join order hints are allowed. It is hard to construct +# a test case where 64 hints will be applicable and only one, exceeding +# the maximum, will produce a warning. Usually so many hints will conflict +# with each other and generate warnings. +# But in the case below we can observe that the first warning +# is about JOIN_ORDER(t1,t2,t3,t4,t5,t6) which is actually +# the last hint in the list, and it is the 65th in order. +# This happens because the check for exceeding the maximum is performed +# during the preparation phase, while other checks are performed during +# hints application. So, if JOIN_ORDER(t1,t2,t3,t4,t5,t6) +# is mentioned first in the list of warnings, the test case passes. +EXPLAIN EXTENDED +SELECT /*+ +JOIN_PREFIX(t2,t1) JOIN_SUFFIX(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) +JOIN_ORDER(t1,t2,t3,t4,t5,t6) */ +count(*) FROM t1, t2, t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 20 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Warning 4210 Hint JOIN_ORDER(`t1`,`t2`,`t3`,`t4`,`t5`,`t6`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_SUFFIX(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_ORDER(`t3`,`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t1`) */ count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` +# Original query with no hints +SELECT count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +count(*) +10 +explain extended SELECT count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# Check name resolving +SELECT /*+ QB_NAME(q1) JOIN_PREFIX(t3, t2, t2@subq2) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t2) AND +t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t2); +count(*) +300 +explain extended SELECT /*+ QB_NAME(q1) JOIN_PREFIX(t3, t2, t2@subq2) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t2) AND +t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t2.f1 20 5.00 Using where; FirstMatch(t2); Using join buffer (incremental, BNLH join) +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t2 ALL NULL NULL NULL NULL 20 100.00 +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_PREFIX(@`q1` `t3`,`t2`,`t2`@`subq2`) */ count(0) AS `count(*)` from `test`.`t1` semi join (`test`.`t2`) semi join (`test`.`t2`) join `test`.`t2` join `test`.`t3` where `test`.`t2`.`f1` = `test`.`t2`.`f1` +# Check conflicting hints +# Second JOIN_PREFIX is conflicting +SELECT /*+ JOIN_PREFIX(t3, t2, t1) JOIN_PREFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`,`t1`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_PREFIX(t3, t2, t1) JOIN_PREFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t4 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`,`t1`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t3`,`t2`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Second JOIN_SUFFIX is conflicting +SELECT /*+ JOIN_SUFFIX(t3, t2) JOIN_SUFFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t2`,`t1`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_SUFFIX(t3, t2) JOIN_SUFFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t2`,`t1`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t3`,`t2`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# Both JOIN_ORDERs applicable +SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t1, t2, t5@subq2) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t1, t2, t5@subq2) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (flat, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t3`,`t2`) JOIN_ORDER(@`select#1` `t1`,`t2`,`t5`@`subq2`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Unresolved table name t7 in JOIN_ORDER hint, hint ignored +SELECT /*+ JOIN_ORDER(t1, t7, t5) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4212 Unresolved table name `t7` for JOIN_ORDER hint +explain extended SELECT /*+ JOIN_ORDER(t1, t7, t5) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Warning 4212 Unresolved table name `t7` for JOIN_ORDER hint +Note 1003 select count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# All hints are applicable +SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1) +JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1) +JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`) JOIN_ORDER(@`select#1` `t4`@`subq1`,`t3`) JOIN_SUFFIX(@`select#1` `t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Second JOIN_ORDER is ignored +SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t2, t3) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t3`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t2, t3) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t3`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t3`,`t2`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# JOIN_SUFFIX is ignored +SELECT /*+ JOIN_ORDER(t3, t2) JOIN_SUFFIX(t3) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t3`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_ORDER(t3, t2) JOIN_SUFFIX(t3) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t3`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t3`,`t2`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# JOIN_PREFIX is ignored +SELECT /*+ JOIN_ORDER(t3, t2) JOIN_PREFIX(t2) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_ORDER(t3, t2) JOIN_PREFIX(t2) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t3`,`t2`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# All hints are applicable +SELECT /*+ JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) JOIN_PREFIX(t2, t5@subq2, t4@subq1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) JOIN_PREFIX(t2, t5@subq2, t4@subq1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t4`@`subq1`,`t3`) JOIN_SUFFIX(@`select#1` `t1`) JOIN_PREFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# JOIN_PREFIX with all tables. +SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# JOIN_SUFFIX with all tables. +SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# JOIN_ORDER with all tables. +SELECT /*+ JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# JOIN_PREFIX, JOIN_ORDER, JOIN_SUFFIX with all tables. +SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) +JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) +JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) +JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) +JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (incremental, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) JOIN_ORDER(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) JOIN_PREFIX(@`select#1` `t2`,`t5`@`subq2`,`t4`@`subq1`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# JOIN_ORDER is ignored due to STRAIGHT_JOIN. +SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2, t1) */ STRAIGHT_JOIN count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t1`) is ignored as conflicting/duplicated +explain extended SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2, t1) */ STRAIGHT_JOIN count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +3 MATERIALIZED t5 ALL PRIMARY NULL NULL NULL 1 100.00 +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t1`) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ QB_NAME(`q1`) */ straight_join count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` where <`test`.`t1`.`f1`>((`test`.`t1`.`f1`,(/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`t4`.`f1` from `test`.`t4` where (`test`.`t1`.`f1`) = `test`.`t4`.`f1`))) and <`test`.`t2`.`f1`>((`test`.`t2`.`f1`,`test`.`t2`.`f1` in ( (/* select#3 */ select /*+ QB_NAME(`subq2`) */ `test`.`t5`.`f1` from `test`.`t5` ), (`test`.`t2`.`f1` in on distinct_key where `test`.`t2`.`f1` = ``.`f1`)))) +# Test JOIN_FIXED_ORDER. +SELECT /*+ QB_NAME(q1) JOIN_FIXED_ORDER(@q1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ QB_NAME(q1) JOIN_FIXED_ORDER(@q1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +3 MATERIALIZED t5 ALL PRIMARY NULL NULL NULL 1 100.00 +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ QB_NAME(`q1`) JOIN_FIXED_ORDER(@`q1`) */ straight_join count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` where <`test`.`t1`.`f1`>((`test`.`t1`.`f1`,(/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`t4`.`f1` from `test`.`t4` where (`test`.`t1`.`f1`) = `test`.`t4`.`f1`))) and <`test`.`t2`.`f1`>((`test`.`t2`.`f1`,`test`.`t2`.`f1` in ( (/* select#3 */ select /*+ QB_NAME(`subq2`) */ `test`.`t5`.`f1` from `test`.`t5` ), (`test`.`t2`.`f1` in on distinct_key where `test`.`t2`.`f1` = ``.`f1`)))) +SELECT /*+ JOIN_FIXED_ORDER() */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_FIXED_ORDER() */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +3 MATERIALIZED t5 ALL PRIMARY NULL NULL NULL 1 100.00 +2 DEPENDENT SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select /*+ JOIN_FIXED_ORDER(@`select#1`) */ straight_join count(0) AS `count(*)` from `test`.`t1` join `test`.`t2` join `test`.`t3` where <`test`.`t1`.`f1`>((`test`.`t1`.`f1`,(/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`t4`.`f1` from `test`.`t4` where (`test`.`t1`.`f1`) = `test`.`t4`.`f1`))) and <`test`.`t2`.`f1`>((`test`.`t2`.`f1`,`test`.`t2`.`f1` in ( (/* select#3 */ select /*+ QB_NAME(`subq2`) */ `test`.`t5`.`f1` from `test`.`t5` ), (`test`.`t2`.`f1` in on distinct_key where `test`.`t2`.`f1` = ``.`f1`)))) +SELECT /*+ QB_NAME(q1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ QB_NAME(q1) */ count(*) FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# Testing STRAIGHT_JOIN +SELECT count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +count(*) +10 +explain extended SELECT count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` straight_join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# t3 can not be first +SELECT /*+ JOIN_PREFIX(t3, t1) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +count(*) +10 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t3`,`t1`) is ignored as conflicting/duplicated +explain extended SELECT /*+ JOIN_PREFIX(t3, t1) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t3`,`t1`) is ignored as conflicting/duplicated +Note 1003 select count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` straight_join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# Hint is applicable +SELECT /*+ JOIN_PREFIX(t1, t2, t3) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_PREFIX(t1, t2, t3) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t4 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t1`,`t2`,`t3`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` straight_join `test`.`t3` where `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Hint is applicable +SELECT /*+ JOIN_SUFFIX(t4, t5) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ JOIN_SUFFIX(t4, t5) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 +WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t3); Using join buffer (incremental, BNLH join) +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t4`,`t5`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` straight_join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Alternative syntax +SELECT /*+ QB_NAME(q1) JOIN_ORDER(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ QB_NAME(q1) JOIN_ORDER(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (flat, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 4 test.t1.f1 2 50.00 Using where; FirstMatch(t1); Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_ORDER(@`q1` `t2`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t4`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +# Alternative syntax +SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 20 100.00 +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (flat, BNL join) +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t4 ALL NULL NULL NULL NULL 2 100.00 +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_PREFIX(@`q1` `t2`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t5`.`f1` = `test`.`t2`.`f1` +# Alternative syntax +SELECT /*+ QB_NAME(q1) JOIN_SUFFIX(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +count(*) +10 +explain extended SELECT /*+ QB_NAME(q1) JOIN_SUFFIX(@q1 t2, t3, t1) */ count(*) +FROM t1 JOIN t2 JOIN t3 +WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) +AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t5 ALL PRIMARY NULL NULL NULL 1 100.00 +1 PRIMARY t4 ALL NULL NULL NULL NULL 2 100.00 Start temporary; Using join buffer (flat, BNL join) +1 PRIMARY t2 hash_ALL NULL #hash#$hj 4 test.t5.f1 20 11.11 Using where; Using join buffer (incremental, BNLH join) +1 PRIMARY t3 ALL NULL NULL NULL NULL 5 100.00 Using join buffer (incremental, BNL join) +1 PRIMARY t1 hash_ALL NULL #hash#$hj 4 test.t4.f1 3 16.67 Using where; End temporary; Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select /*+ QB_NAME(`q1`) JOIN_SUFFIX(@`q1` `t2`,`t3`,`t1`) */ count(0) AS `count(*)` from `test`.`t5` semi join (`test`.`t4`) join `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t1`.`f1` = `test`.`t4`.`f1` and `test`.`t2`.`f1` = `test`.`t5`.`f1` +DROP TABLE t1, t2, t3, t4 ,t5, t6; +# Testing embedded join +CREATE TABLE t1 (f1 INT); +CREATE TABLE t2 (f1 INT); +CREATE TABLE t3 (f1 INT); +CREATE TABLE t4 (f1 INT); +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t4, t1) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t4`,`t1`) is ignored as conflicting/duplicated +Note 1003 select 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t1, t4) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t2`,`t1`,`t4`) */ 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t4, t1, t2) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t4`,`t1`,`t2`) */ 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t3, t4) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t3`,`t4`) */ 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +EXPLAIN SELECT /*+ JOIN_ORDER(t4, t3) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 Using join buffer (flat, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 Using join buffer (incremental, BNL join) +EXPLAIN EXTENDED SELECT /*+ JOIN_SUFFIX(t1) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t1`) */ 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +EXPLAIN EXTENDED SELECT /*+ JOIN_SUFFIX(t2, t1) */ 1 FROM t1 +JOIN t2 ON 1 +RIGHT JOIN t3 ON 1 +JOIN t4 ON 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t4 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (incremental, BNL join) +Warnings: +Note 1003 select /*+ JOIN_SUFFIX(@`select#1` `t2`,`t1`) */ 1 AS `1` from `test`.`t3` left join (`test`.`t1` join `test`.`t2`) on(1 and 1) join `test`.`t4` where 1 +DROP TABLE t1, t2, t3, t4; +CREATE TABLE t1 +( +f1 INT(11) NOT NULL AUTO_INCREMENT, +PRIMARY KEY (f1) +); +INSERT INTO t1 VALUES (1),(2),(3); +CREATE TABLE t2 +( +f1 INT(11) DEFAULT NULL +); +INSERT INTO t2 VALUES (0),(1),(2); +CREATE TABLE t3 +( +f1 INT(11) DEFAULT NULL +); +INSERT INTO t3 VALUES (0),(1),(2); +# Original query +EXPLAIN EXTENDED DELETE +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 33.33 Using where; Start temporary; End temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 Using where +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +# Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 +2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +# Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta1, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 Using where +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 +2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta1`,`ta2`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +# Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2, ta1) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 +2 MATERIALIZED t3 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 delete /*+ JOIN_PREFIX(@`select#1` `t2`,`t3`,`ta2`,`ta1`) */ from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +# Hint should be ignored +EXPLAIN EXTENDED DELETE /*+ JOIN_SUFFIX(ta2, t3, ta1) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 33.33 Using where; Start temporary; End temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 Using where +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`ta2`,`t3`,`ta1`) is ignored as conflicting/duplicated +Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +# Both hints are ignored +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(ta1, t2, t3) JOIN_SUFFIX(t3, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 +RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 33.33 Using where; Start temporary; End temporary +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY ta1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 100.00 Using where +1 PRIMARY ta2 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`ta1`,`t2`,`t3`) is ignored as conflicting/duplicated +Warning 4210 Hint JOIN_SUFFIX(`t3`,`ta2`) is ignored as conflicting/duplicated +Note 1003 delete from `test`.`t1` `ta1` using `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta1`.`f1` = `test`.`t2`.`f1` and 1 and `test`.`t2`.`f1` is not null) where `test`.`t3`.`f1` = 9 +DROP TABLE t1, t2, t3; +# Const table behavior, table order is not changed, hint is applicable. +# Note: Const tables are excluded from the process of dependency setting +# since they are always first in the table order. Note that it +# does not prevent the hint from being applied to the non-const +# tables of the hint. +CREATE TABLE t1(f1 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1); +CREATE TABLE t2(f1 INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t1, t2) */ 1 FROM t1 JOIN t2 ON t1.f1 = t2.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using where +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t1`,`t2`) */ 1 AS `1` from `test`.`t2` where `test`.`t2`.`f1` = 1 +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t2, t1) */ 1 FROM t1 JOIN t2 ON t1.f1 = t2.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 100.00 Using where +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`,`t1`) */ 1 AS `1` from `test`.`t2` where `test`.`t2`.`f1` = 1 +DROP TABLE t1, t2; +# +# Bug#23144274 WL9158:ASSERTION `JOIN->BEST_READ < DOUBLE(1.79769313486231570815E+308L)' FAILED +# +CREATE TABLE t1 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 varchar(255) DEFAULT NULL, +PRIMARY KEY (f1)); +CREATE TABLE t2 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 varchar(255) CHARACTER SET utf8mb3 DEFAULT NULL, +f3 varchar(10) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f3(f3)); +EXPLAIN SELECT /*+ JOIN_SUFFIX(t1, t2) */ t2.f3 FROM t2 LEFT JOIN t1 +ON t2.f1 = t1.f1 WHERE t1.f2 NOT LIKE ('FMGAU') OR t2.f2 > 't'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 1 +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.f1 1 Using where +Warnings: +Warning 4210 Hint JOIN_SUFFIX(`t1`,`t2`) is ignored as conflicting/duplicated +DROP TABLE t1, t2; +CREATE TABLE t1 +( +f1 int(11) DEFAULT NULL, +KEY f1 (f1) +); +CREATE TABLE t2 +( +f1 int(11) DEFAULT NULL, +f2 varchar(255) CHARACTER SET utf8mb3 DEFAULT NULL, +KEY f2 (f2), +KEY f1 (f1) +); +CREATE TABLE t3 ( +f1 int(11) DEFAULT NULL, +f2 varchar(255) CHARACTER SET cp932 DEFAULT NULL, +KEY f1 (f1), +KEY f2 (f2) +); +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t3) JOIN_ORDER(t1, t2) */ t3.f1 +FROM ( t2 INNER JOIN t3 ON t3.f2 = t2.f2 LEFT JOIN t1 ON t1.f1 = t3.f1 ) +WHERE NOT (t2.f1 >= 7); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 range f2,f1 f1 5 NULL 1 100.00 Using index condition +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t1 ref f1 f1 5 test.t3.f1 1 100.00 Using where; Using index +Warnings: +Warning 4210 Hint JOIN_ORDER(`t1`,`t2`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t2`,`t3`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t2` join `test`.`t3` left join `test`.`t1` on(`test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t3`.`f1` is not null) where `test`.`t2`.`f1` < 7 and convert(`test`.`t3`.`f2` using utf8mb3) = `test`.`t2`.`f2` +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t1, t2) JOIN_ORDER(t2, t3) */ t3.f1 +FROM ( t2 INNER JOIN t3 ON t3.f2 = t2.f2 LEFT JOIN t1 ON t1.f1 = t3.f1 ) +WHERE NOT (t2.f1 >= 7); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 ALL NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ref f1 f1 5 test.t3.f1 1 100.00 Using where; Using index +1 SIMPLE t2 ref f2,f1 f2 768 func 1 100.00 Using index condition; Using where +Warnings: +Warning 4210 Hint JOIN_ORDER(`t2`,`t3`) is ignored as conflicting/duplicated +Note 1003 select /*+ JOIN_ORDER(@`select#1` `t1`,`t2`) */ `test`.`t3`.`f1` AS `f1` from `test`.`t2` join `test`.`t3` left join `test`.`t1` on(`test`.`t1`.`f1` = `test`.`t3`.`f1` and `test`.`t3`.`f1` is not null) where `test`.`t2`.`f1` < 7 and convert(`test`.`t3`.`f2` using utf8mb3) = `test`.`t2`.`f2` +DROP TABLE t1, t2, t3; +CREATE TABLE t1 ( +f1 INT(11) NOT NULL AUTO_INCREMENT, +f2 INT(11) DEFAULT NULL, +PRIMARY KEY (f1) +); +CREATE TABLE t2 ( +f1 INT(11) NOT NULL AUTO_INCREMENT, +PRIMARY KEY (f1) +); +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t1, t1) */ t2.f1 FROM t1 JOIN t2 ON t1.f2 = t2.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1 100.00 Using where +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.f2 1 100.00 +Warnings: +Warning 4210 Hint JOIN_PREFIX(`t1`,`t1`) is ignored as conflicting/duplicated +Note 1003 select `test`.`t2`.`f1` AS `f1` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f2` +DROP TABLE t1, t2; +CREATE TABLE t1 +( +f1 DATETIME, +f2 DATE, +f3 VARCHAR(1), +KEY (f1) +) ENGINE=myisam; +CREATE TABLE t2 +( +f1 VARCHAR(1), +f2 INT, +f3 VARCHAR(1), +KEY (f1) +) ENGINE=innodb; +CREATE TABLE t3 +( +f1 VARCHAR(1), +f2 DATE, +f3 DATETIME, +f4 INT +) ENGINE=myisam; +EXPLAIN EXTENDED +UPDATE /*+ JOIN_ORDER(t2, als1, als3) JOIN_FIXED_ORDER() */ t3 AS als1 +JOIN t1 AS als2 ON (als1.f3 = als2 .f1) +JOIN t1 AS als3 ON (als1.f1 = als3.f3) +RIGHT OUTER JOIN t3 AS als4 ON (als1.f3 = als4.f2) +SET als1.f4 = 'eogqjvbhzodzimqahyzlktkbexkhdwxwgifikhcgblhgswxyutepc' +WHERE ('i','b') IN (SELECT f3, f1 FROM t2 WHERE f2 <> f2 AND als2.f2 IS NULL); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +Warnings: +Warning 4210 Hint JOIN_FIXED_ORDER() is ignored as conflicting/duplicated +Note 1276 Field or reference 'test.als2.f2' of SELECT #2 was resolved in SELECT #1 +Note 1003 update /*+ JOIN_ORDER(@`select#1` `t2`,`als1`,`als3`) */ (`test`.`t2`) join `test`.`t3` `als4` left join (`test`.`t3` `als1` join `test`.`t1` `als2` join `test`.`t1` `als3`) on(`test`.`als1`.`f3` = `test`.`als4`.`f2` and multiple equal(`test`.`als1`.`f1`, `test`.`als3`.`f3`) and multiple equal(`test`.`als1`.`f3`, `test`.`als2`.`f1`)) set `test`.`als1`.`f4` = 'eogqjvbhzodzimqahyzlktkbexkhdwxwgifikhcgblhgswxyutepc' where 0 +DROP TABLE t1, t2, t3; +CREATE TABLE t1( +f1 VARCHAR(1)) ENGINE=myisam; +CREATE TABLE t2( +f1 VARCHAR(1), +f2 VARCHAR(1), +f3 DATETIME, +KEY(f2)) ENGINE=innodb; +CREATE TABLE t3( +f1 INT, +f2 DATE, +f3 VARCHAR(1), +KEY(f3)) ENGINE=myisam; +CREATE TABLE t4( +f1 VARCHAR(1), +KEY(f1)) ENGINE=innodb; +ALTER TABLE t4 DISABLE KEYS; +Warnings: +Note 1031 Storage engine InnoDB of the table `test`.`t4` doesn't have this option +INSERT INTO t4 VALUES ('x'), (NULL), ('d'), ('x'), ('u'); +ALTER TABLE t4 ENABLE KEYS; +Warnings: +Note 1031 Storage engine InnoDB of the table `test`.`t4` doesn't have this option +CREATE TABLE t5( +f1 VARCHAR(1), +KEY(f1) ) ENGINE=myisam; +INSERT INTO t5 VALUES (NULL), ('s'), ('c'), ('x'), ('z'); +EXPLAIN EXTENDED UPDATE /*+ JOIN_ORDER(t4, alias1, alias3) */ t3 AS alias1 +JOIN t5 ON (alias1.f3 = t5.f1) +JOIN t3 AS alias3 ON (alias1.f2 = alias3.f2 ) +RIGHT OUTER JOIN t1 ON (alias1.f3 = t1.f1) +SET alias1.f1 = -1 +WHERE ( 'v', 'o' ) IN +(SELECT DISTINCT t2.f1, t2.f2 FROM t4 RIGHT OUTER JOIN t2 ON (t4.f1 = t2.f1) +WHERE t2.f3 BETWEEN '2001-10-04' AND '2003-05-15'); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1003 update /*+ JOIN_ORDER(@`select#1` `t4`,`alias1`,`alias3`) */ `test`.`t1` semi join (`test`.`t2` left join `test`.`t4` on(`test`.`t4`.`f1` = `test`.`t2`.`f1` and `test`.`t2`.`f1` is not null)) left join (`test`.`t3` `alias1` join `test`.`t5` join `test`.`t3` `alias3`) on(`test`.`alias1`.`f3` = NULL and `test`.`t5`.`f1` = NULL and `test`.`alias3`.`f2` = `test`.`alias1`.`f2` and NULL is not null and `test`.`alias1`.`f2` is not null and NULL is not null) set `test`.`alias1`.`f1` = -1 where 0 +DROP TABLE t1, t2, t3, t4, t5; +CREATE TABLE t1 ( +f1 INT(11) DEFAULT NULL, +f3 VARCHAR(1) DEFAULT NULL, +f2 INT(11) DEFAULT NULL, +KEY (f1) +) ENGINE=MyISAM; +CREATE TABLE t2( +f1 INT(11) DEFAULT NULL +) ENGINE=MyISAM; +CREATE TABLE t3 ( +f1 VARCHAR(1) DEFAULT NULL, +f2 VARCHAR(1) DEFAULT NULL, +KEY (f2) +) ENGINE=InnoDB; +EXPLAIN EXTENDED UPDATE /*+ JOIN_SUFFIX(ta1, t2) */ +t1 AS ta1 JOIN t1 AS ta2 ON ta1.f1 = ta2.f1 RIGHT JOIN t2 ON (ta1.f1 = t2.f1) +SET ta1.f2 = '', ta2.f3 = '' +WHERE ('n', 'r') IN (SELECT f2, f1 FROM t3 WHERE f1 <> f2 XOR ta2.f3 IS NULL); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1276 Field or reference 'test.ta2.f3' of SELECT #2 was resolved in SELECT #1 +Note 1003 update /*+ JOIN_SUFFIX(@`select#1` `ta1`,`t2`) */ `test`.`t2` semi join (`test`.`t3`) left join (`test`.`t1` `ta1` join `test`.`t1` `ta2`) on(`test`.`ta2`.`f1` = NULL and `test`.`ta1`.`f1` = NULL and NULL is not null and NULL is not null) set `test`.`ta1`.`f2` = '',`test`.`ta2`.`f3` = '' where 0 +DROP TABLE t1, t2, t3; +CREATE TABLE t2(f1 VARCHAR(255) DEFAULT NULL, f2 INT(11) DEFAULT NULL, +KEY (f1), KEY (f2)) charset latin1 ENGINE=MyISAM; +CREATE TABLE t4(f1 INT(11) DEFAULT NULL, f2 INT(11) DEFAULT NULL, KEY (f1)) +charset latin1 ENGINE=MyISAM; +CREATE TABLE t5(f1 INT(11) NOT NULL AUTO_INCREMENT, f2 INT(11) DEFAULT NULL, PRIMARY KEY (f1)) +charset latin1 ENGINE=InnoDB; +CREATE TABLE t6(f1 INT(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (f1)) +charset latin1 ENGINE=InnoDB; +CREATE TABLE t7 (f1 VARCHAR(255) DEFAULT NULL) +charset latin1 ENGINE=InnoDB; +CREATE TABLE t10(f1 INT(11) NOT NULL AUTO_INCREMENT,f2 INT(11) DEFAULT NULL,f3 VARCHAR(10) DEFAULT NULL, +PRIMARY KEY (f1),KEY (f2),KEY (f3)) charset latin1 ENGINE=MyISAM; +CREATE TABLE t11(f1 INT(11) DEFAULT NULL,f2 VARCHAR(10) DEFAULT NULL, +KEY (f1),KEY (f2)) charset latin1 ENGINE=InnoDB; +EXPLAIN EXTENDED +SELECT /*+ JOIN_ORDER(alias11, alias8) */ 1 +FROM t4 AS alias4 +LEFT JOIN t5 AS alias5 JOIN t6 AS alias6 ON alias5.f2 = alias6.f1 +LEFT JOIN t7 AS alias7 JOIN t2 AS alias8 ON alias7.f1 = alias8.f1 +ON alias5.f1 = alias8.f2 ON alias4.f2 = alias6.f1 +JOIN t10 AS alias10 JOIN t11 AS alias11 ON alias10.f1 = alias11.f1 +ON alias4.f2 = alias11.f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1105 Cannot use key `f2` part[0] for lookup: `test`.`t11`.`f2` of type `varchar` = "`alias4`.`f2`" of type `int` +Note 1003 select /*+ JOIN_ORDER(@`select#1` `alias11`,`alias8`) */ 1 AS `1` from `test`.`t4` `alias4` left join (`test`.`t5` `alias5` join `test`.`t6` `alias6` left join (`test`.`t7` `alias7` join `test`.`t2` `alias8`) on(`test`.`alias8`.`f2` = `test`.`alias5`.`f1` and `test`.`alias8`.`f1` = `test`.`alias7`.`f1` and `test`.`alias7`.`f1` is not null)) on(`test`.`alias5`.`f2` = NULL and `test`.`alias6`.`f1` = NULL and NULL is not null and `test`.`alias5`.`f2` is not null) join `test`.`t10` `alias10` join `test`.`t11` `alias11` where 0 +EXPLAIN EXTENDED +SELECT /*+ JOIN_ORDER(alias11, alias10, alias8, alias7) */ 1 +FROM t4 AS alias4 +LEFT JOIN t5 AS alias5 JOIN t6 AS alias6 ON alias5.f2 = alias6.f1 +LEFT JOIN t7 AS alias7 JOIN t2 AS alias8 ON alias7.f1 = alias8.f1 +ON alias5.f1 = alias8.f2 ON alias4.f2 = alias6.f1 +JOIN t10 AS alias10 JOIN t11 AS alias11 ON alias10.f1 = alias11.f1 +ON alias4.f2 = alias11.f2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1105 Cannot use key `f2` part[0] for lookup: `test`.`t11`.`f2` of type `varchar` = "`alias4`.`f2`" of type `int` +Note 1003 select /*+ JOIN_ORDER(@`select#1` `alias11`,`alias10`,`alias8`,`alias7`) */ 1 AS `1` from `test`.`t4` `alias4` left join (`test`.`t5` `alias5` join `test`.`t6` `alias6` left join (`test`.`t7` `alias7` join `test`.`t2` `alias8`) on(`test`.`alias8`.`f2` = `test`.`alias5`.`f1` and `test`.`alias7`.`f1` = `test`.`alias8`.`f1` and `test`.`alias5`.`f1` is not null and `test`.`alias8`.`f1` is not null)) on(`test`.`alias5`.`f2` = NULL and `test`.`alias6`.`f1` = NULL and NULL is not null and `test`.`alias5`.`f2` is not null) join `test`.`t10` `alias10` join `test`.`t11` `alias11` where 0 +DROP TABLES t2, t4, t5, t6, t7, t10, t11; +CREATE TABLE t1 (f1 VARCHAR(255) DEFAULT NULL, f2 VARCHAR(255) DEFAULT NULL, +KEY (f1), KEY (f2)) ENGINE=InnoDB; +CREATE TABLE t2(f1 VARCHAR(255) DEFAULT NULL, f2 INT(11) DEFAULT NULL, +KEY (f1), KEY (f2)) ENGINE=InnoDB; +CREATE TABLE t3( +f1 INT(11) NOT NULL AUTO_INCREMENT, f2 VARCHAR(255) DEFAULT NULL, +PRIMARY KEY (f1), KEY (f2)) ENGINE=InnoDB; +CREATE TABLE t4(f1 INT(11) DEFAULT NULL, f2 INT(11) DEFAULT NULL, KEY (f1)) ENGINE=InnoDB; +CREATE TABLE t6(f1 INT(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (f1)) ENGINE=InnoDB; +CREATE TABLE t7 (f1 VARCHAR(255) DEFAULT NULL) ENGINE=InnoDB; +CREATE TABLE t10(f1 INT(11) NOT NULL AUTO_INCREMENT,f2 INT(11) DEFAULT NULL,f3 VARCHAR(10) DEFAULT NULL, +PRIMARY KEY (f1),KEY (f2),KEY (f3)) ENGINE=InnoDB; +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(alias8, alias6) */ 1 +FROM t1 AS alias1 +LEFT JOIN t7 alias7 +JOIN t2 AS alias2 +LEFT JOIN t3 AS alias3 JOIN t4 AS alias4 ON alias4.f2 = alias3.f1 +ON alias4.f1 = alias2.f1 +ON alias2.f2 = alias7.f1 +JOIN t10 AS alias5 +LEFT JOIN t6 AS alias6 JOIN t2 AS alias8 ON alias6.f1 = alias8.f2 +ON alias6.f1 = alias5.f1 +ON alias5.f3 = alias7.f1 +ON alias1.f2 = alias7.f1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE alias1 index NULL f2 1023 NULL 1 100.00 Using index +1 SIMPLE alias7 ALL NULL NULL NULL NULL 1 10.00 Using where +1 SIMPLE alias2 ref f2 f2 5 test.alias7.f1 1 100.00 Using where +1 SIMPLE alias4 ref f1 f1 5 test.alias2.f1 1 100.00 Using where +1 SIMPLE alias3 eq_ref PRIMARY PRIMARY 4 test.alias4.f2 1 100.00 +1 SIMPLE alias5 ref f3 f3 43 test.alias7.f1 1 100.00 Using where; Using index +1 SIMPLE alias8 ref f2 f2 5 test.alias5.f1 1 100.00 Using where; Using index +1 SIMPLE alias6 eq_ref PRIMARY PRIMARY 4 test.alias8.f2 1 100.00 Using where +Warnings: +Note 1003 select /*+ JOIN_ORDER(@`select#1` `alias8`,`alias6`) */ 1 AS `1` from `test`.`t1` `alias1` left join (`test`.`t7` `alias7` join `test`.`t2` `alias2` left join (`test`.`t3` `alias3` join `test`.`t4` `alias4`) on(`test`.`alias3`.`f1` = `test`.`alias4`.`f2` and `test`.`alias4`.`f1` = `test`.`alias2`.`f1` and `test`.`alias2`.`f1` is not null and `test`.`alias4`.`f2` is not null) join `test`.`t10` `alias5` left join (`test`.`t6` `alias6` join `test`.`t2` `alias8`) on(`test`.`alias8`.`f2` = `test`.`alias5`.`f1` and `test`.`alias6`.`f1` = `test`.`alias5`.`f1` and `test`.`alias5`.`f1` is not null and `test`.`alias8`.`f2` is not null)) on(`test`.`alias7`.`f1` = `test`.`alias1`.`f2` and `test`.`alias5`.`f3` = `test`.`alias1`.`f2` and `test`.`alias2`.`f2` = `test`.`alias7`.`f1` and `test`.`alias1`.`f2` is not null and `test`.`alias7`.`f1` is not null and `test`.`alias7`.`f1` is not null) where 1 +DROP TABLES t1, t2, t3, t4, t6, t7, t10; +# +# Bug#23144230 WL#9158 : OPT_HINTS_QB::APPLY_JOIN_ORDER_HINTS - MYSQLD GOT SIGNAL 11 +# +CREATE TABLE t1 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 int(11) DEFAULT NULL, +f3 int(11) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f2 (f2)) +ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +CREATE TABLE t2 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 int(11) DEFAULT NULL, +f3 int(11) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f2 (f2)) +ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(alias1) */ alias2.f3 AS field1 +FROM ( SELECT * FROM t1 ) AS alias1 JOIN ( SELECT * FROM t2 ) AS alias2 +ON alias1.f2 = alias2.f3 +WHERE alias2.f2 IS NULL HAVING (field1 != 3 AND field1 >= 8); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 ref f2 f2 5 const 1 100.00 Using index condition; Using where +1 SIMPLE t1 ref f2 f2 5 test.t2.f3 1 100.00 Using index +Warnings: +Warning 4212 Unresolved table name `alias1` for JOIN_PREFIX hint +Note 1003 select `test`.`t2`.`f3` AS `field1` from `test`.`t1` join `test`.`t2` where `test`.`t1`.`f2` = `test`.`t2`.`f3` and `test`.`t2`.`f2` is null having `f3` <> 3 and `f3` >= 8 +DROP TABLE t1, t2; +# +# Bug#23651098 WL#9158 : ASSERTION `!(SJ_NEST->SJ_INNER_TABLES & JOIN->CONST_TABLE_MAP)' FAILED +# +CREATE TABLE t1 +( +f1 INT(11) NOT NULL AUTO_INCREMENT, +PRIMARY KEY (f1) +) ENGINE=InnoDB; +CREATE TABLE t2 +( +f1 VARCHAR(1) DEFAULT NULL +) ENGINE=MyISAM; +CREATE TABLE t3 +( +f1 VARCHAR(1) DEFAULT NULL +) ENGINE=MyISAM; +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t2, t1) */ t1.f1 FROM t1, t2 +WHERE t2.f1 IN (SELECT t3.f1 FROM t3) AND t1.f1 = 183; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`,`t1`) */ NULL AS `f1` from `test`.`t1` semi join (`test`.`t3`) join `test`.`t2` where 0 +DROP TABLE t1, t2, t3; +# +# Bug23715779 SELECT QUERY WITH JOIN_PREFIX() HINT RETURNS INCORRECT RESULT +# +CREATE TABLE t1(f1 INT(11) NOT NULL); +INSERT INTO t1 VALUES (10); +CREATE TABLE t2 +( +f1 INT(11) NOT NULL AUTO_INCREMENT, +f2 INT(11) DEFAULT NULL, +PRIMARY KEY (f1), +KEY (f2) +); +INSERT INTO t2 VALUES (1, 7), (2, 1), (4, 7); +CREATE TABLE t4(f1 INT DEFAULT NULL); +INSERT INTO t4 VALUES (2); +ANALYZE TABLE t1, t2, t4; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t4 analyze status Engine-independent statistics collected +test.t4 analyze status OK +SELECT +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND +ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +COUNT(*) +3 +explain extended SELECT +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND +ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 1 100.00 +1 PRIMARY t2 index f2 f2 5 NULL 3 100.00 Using where; Using index; LooseScan +1 PRIMARY ta3 ref f2 f2 5 test.t2.f2 1 33.33 Using index +1 PRIMARY ta4 index PRIMARY f2 5 NULL 3 100.00 Using index; Using join buffer (flat, BNL join) +1 PRIMARY t4 hash_ALL NULL #hash#$hj 5 test.ta4.f1 1 100.00 Using where; FirstMatch(ta4); Using join buffer (incremental, BNLH join) +Warnings: +Note 1003 select count(0) AS `COUNT(*)` from `test`.`t1` semi join (`test`.`t4`) semi join (`test`.`t2`) join `test`.`t2` `ta3` join `test`.`t2` `ta4` where `test`.`t4`.`f1` = `test`.`ta4`.`f1` and `test`.`ta3`.`f2` = `test`.`t2`.`f2` +SELECT /*+ JOIN_PREFIX(t2@qb2, t4@qb1, ta3, ta4) */ +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND +ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +COUNT(*) +3 +explain extended SELECT /*+ JOIN_PREFIX(t2@qb2, t4@qb1, ta3, ta4) */ +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND +ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index f2 f2 5 NULL 3 100.00 Using where; Using index; Start temporary +1 PRIMARY t4 ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (flat, BNL join) +1 PRIMARY ta3 ref f2 f2 5 test.t2.f2 1 33.33 Using index +1 PRIMARY ta4 eq_ref PRIMARY PRIMARY 4 test.t4.f1 1 33.33 End temporary +1 PRIMARY t1 ALL NULL NULL NULL NULL 1 100.00 Using join buffer (flat, BNL join) +Warnings: +Note 1003 select /*+ JOIN_PREFIX(@`select#1` `t2`@`qb2`,`t4`@`qb1`,`ta3`,`ta4`) */ count(0) AS `COUNT(*)` from `test`.`t1` semi join (`test`.`t4`) semi join (`test`.`t2`) join `test`.`t2` `ta3` join `test`.`t2` `ta4` where `test`.`ta4`.`f1` = `test`.`t4`.`f1` and `test`.`ta3`.`f2` = `test`.`t2`.`f2` +DROP TABLE t1, t2, t4; diff --git a/mysql-test/main/opt_hints_join_order.test b/mysql-test/main/opt_hints_join_order.test new file mode 100644 index 0000000000000..fabe20ea1e471 --- /dev/null +++ b/mysql-test/main/opt_hints_join_order.test @@ -0,0 +1,791 @@ +--source include/have_innodb.inc +--enable_prepare_warnings +--disable_view_protocol # Since optimizer hints are not supported inside views + +set default_storage_engine=innodb; + +CREATE TABLE t1 (f1 INT NOT NULL); +INSERT INTO t1 VALUES (9),(0), (7); + +CREATE TABLE t2 (f1 INT NOT NULL); +INSERT INTO t2 VALUES +(5),(3),(0),(3),(1),(0),(1),(7),(1),(0),(0),(8),(4),(9),(0),(2),(0),(8),(5),(1); + +CREATE TABLE t3 (f1 INT NOT NULL); +INSERT INTO t3 VALUES (9),(0), (7), (4), (5); + +CREATE TABLE t4 (f1 INT NOT NULL); +INSERT INTO t4 VALUES (0), (7); + +CREATE TABLE t5 (f1 INT NOT NULL, PRIMARY KEY(f1)); +INSERT INTO t5 VALUES (7); + +CREATE TABLE t6(f1 INT NOT NULL, PRIMARY KEY(f1)); +INSERT INTO t6 VALUES (7); + +ANALYZE TABLE t1, t2, t3, t4, t5, t6; + +set join_cache_level = 8; + +--echo # Warning expected: hint must be ignored as malformed (no table names) +EXPLAIN EXTENDED + SELECT /*+ JOIN_PREFIX()*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1)*/ count(*) FROM t1, t2; + +--echo # Hint must be ignored as malformed (table names are not allowed for the fixed order) +EXPLAIN EXTENDED + SELECT /*+ JOIN_FIXED_ORDER(t2)*/ count(*) FROM t1, t2; + +--echo # Hint is applied: QB name is allowed for the fixed order +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qb1)*/ count(*) FROM t2, t1; + +--echo # Invalid QB name for the fixed order +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qb3)*/ count(*) FROM t2, t1; + +EXPLAIN EXTENDED + SELECT /*+ JOIN_FIXED_ORDER()*/ STRAIGHT_JOIN count(*) FROM t1, t2; + +--echo # JOIN_FIXED_ORDER() cannot be combined with other join order hints +EXPLAIN EXTENDED + SELECT /*+ JOIN_PREFIX(t2) JOIN_FIXED_ORDER()*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ JOIN_FIXED_ORDER() JOIN_PREFIX(t2)*/ count(*) FROM t1, t2; + +--echo # Multiple join order hints of same type are not allowed (except JOIN_ORDER()) +EXPLAIN EXTENDED + SELECT /*+ JOIN_PREFIX(t1) JOIN_PREFIX(t2)*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ JOIN_SUFFIX(t1) JOIN_SUFFIX(t2)*/ count(*) FROM t1, t2; + +--echo # Multiple JOIN_ORDER() hints are applied +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2,t1) JOIN_ORDER(@q1 t3,t2)*/ count(*) + FROM t1, t2, t3; + +--echo # Variation of last syntax +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2@q1, t1) JOIN_ORDER(t3, t2@q1)*/ count(*) + FROM t1, t2, t3; + +--echo # Invalid mix of notations +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1 t2, t1@q1)*/ count(*) + FROM t1, t2, t3; + +--echo # Hint is correct +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(t1@qb1, t2)*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_PREFIX(@qb1, t2)*/ count(*) FROM t1, t2; + +--echo # Invalid table name +EXPLAIN EXTENDED + SELECT /*+ JOIN_SUFFIX(t3)*/ count(*) FROM t1, t2; + +--echo # Invalid query block name +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(@qbXXX t1)*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_SUFFIX(t1, t2@qb3)*/ count(*) FROM t1, t2; + +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) JOIN_FIXED_ORDER(@qbXXX)*/ count(*) FROM t2, t1; + +--echo # Warning expected as the hint must not confuse `t2` in the subquery +--echo # with `t2` in the join +EXPLAIN EXTENDED + SELECT /*+ JOIN_PREFIX(t2@subq)*/ (SELECT /*+ QB_NAME(subq)*/ max(f1) FROM t2) as SQ, f1 + FROM t2; + +--echo # No more than 64 join order hints are allowed. It is hard to construct +--echo # a test case where 64 hints will be applicable and only one, exceeding +--echo # the maximum, will produce a warning. Usually so many hints will conflict +--echo # with each other and generate warnings. +--echo # But in the case below we can observe that the first warning +--echo # is about JOIN_ORDER(t1,t2,t3,t4,t5,t6) which is actually +--echo # the last hint in the list, and it is the 65th in order. +--echo # This happens because the check for exceeding the maximum is performed +--echo # during the preparation phase, while other checks are performed during +--echo # hints application. So, if JOIN_ORDER(t1,t2,t3,t4,t5,t6) +--echo # is mentioned first in the list of warnings, the test case passes. +EXPLAIN EXTENDED + SELECT /*+ + JOIN_PREFIX(t2,t1) JOIN_SUFFIX(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) JOIN_ORDER(t2,t1) JOIN_ORDER(t3,t2) + JOIN_ORDER(t1,t2,t3,t4,t5,t6) */ + count(*) FROM t1, t2, t3; + +--echo # Original query with no hints +let $query= SELECT count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Check name resolving +let $query= SELECT /*+ QB_NAME(q1) JOIN_PREFIX(t3, t2, t2@subq2) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t2) AND + t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t2); +eval $query; +eval explain extended $query; + +--echo # Check conflicting hints +--echo # Second JOIN_PREFIX is conflicting +let $query= SELECT /*+ JOIN_PREFIX(t3, t2, t1) JOIN_PREFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Second JOIN_SUFFIX is conflicting +let $query= SELECT /*+ JOIN_SUFFIX(t3, t2) JOIN_SUFFIX(t2, t1) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Both JOIN_ORDERs applicable +let $query= SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t1, t2, t5@subq2) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Unresolved table name t7 in JOIN_ORDER hint, hint ignored +let $query= SELECT /*+ JOIN_ORDER(t1, t7, t5) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # All hints are applicable +let $query= SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1) + JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Second JOIN_ORDER is ignored +let $query= SELECT /*+ JOIN_ORDER(t3, t2) JOIN_ORDER(t2, t3) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_SUFFIX is ignored +let $query= SELECT /*+ JOIN_ORDER(t3, t2) JOIN_SUFFIX(t3) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_PREFIX is ignored +let $query= SELECT /*+ JOIN_ORDER(t3, t2) JOIN_PREFIX(t2) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # All hints are applicable +let $query= SELECT /*+ JOIN_ORDER(t4@subq1, t3) JOIN_SUFFIX(t1) JOIN_PREFIX(t2, t5@subq2, t4@subq1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_PREFIX with all tables. +let $query= SELECT /*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_SUFFIX with all tables. +let $query= SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_ORDER with all tables. +let $query= SELECT /*+ JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_PREFIX, JOIN_ORDER, JOIN_SUFFIX with all tables. +let $query= SELECT /*+ JOIN_SUFFIX(t2, t5@subq2, t4@subq1, t3, t1) + JOIN_ORDER(t2, t5@subq2, t4@subq1, t3, t1) + JOIN_PREFIX(t2, t5@subq2, t4@subq1, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # JOIN_ORDER is ignored due to STRAIGHT_JOIN. +let $query= SELECT /*+ QB_NAME(q1) JOIN_ORDER(t2, t1) */ STRAIGHT_JOIN count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Test JOIN_FIXED_ORDER. +let $query= SELECT /*+ QB_NAME(q1) JOIN_FIXED_ORDER(@q1) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +let $query= SELECT /*+ JOIN_FIXED_ORDER() */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +let $query= SELECT /*+ QB_NAME(q1) */ count(*) FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Testing STRAIGHT_JOIN + +let $query= SELECT count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 + WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # t3 can not be first +let $query= SELECT /*+ JOIN_PREFIX(t3, t1) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 + WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Hint is applicable +let $query= SELECT /*+ JOIN_PREFIX(t1, t2, t3) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 + WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Hint is applicable +let $query= SELECT /*+ JOIN_SUFFIX(t4, t5) */ count(*) FROM t1 JOIN t2 STRAIGHT_JOIN t3 + WHERE t1.f1 IN (SELECT f1 FROM t4) AND t2.f1 IN (SELECT f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Alternative syntax +let $query= SELECT /*+ QB_NAME(q1) JOIN_ORDER(@q1 t2, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Alternative syntax +let $query= SELECT /*+ QB_NAME(q1) JOIN_PREFIX(@q1 t2, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + +--echo # Alternative syntax +let $query= SELECT /*+ QB_NAME(q1) JOIN_SUFFIX(@q1 t2, t3, t1) */ count(*) + FROM t1 JOIN t2 JOIN t3 + WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4) + AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5); +eval $query; +eval explain extended $query; + + +DROP TABLE t1, t2, t3, t4 ,t5, t6; + +--echo # Testing embedded join + +CREATE TABLE t1 (f1 INT); +CREATE TABLE t2 (f1 INT); +CREATE TABLE t3 (f1 INT); +CREATE TABLE t4 (f1 INT); + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t4, t1) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t1, t4) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t4, t1, t2) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t3, t4) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN SELECT /*+ JOIN_ORDER(t4, t3) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_SUFFIX(t1) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_SUFFIX(t2, t1) */ 1 FROM t1 + JOIN t2 ON 1 + RIGHT JOIN t3 ON 1 + JOIN t4 ON 1; + +DROP TABLE t1, t2, t3, t4; + +CREATE TABLE t1 +( + f1 INT(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (f1) +); +INSERT INTO t1 VALUES (1),(2),(3); + +CREATE TABLE t2 +( + f1 INT(11) DEFAULT NULL +); +INSERT INTO t2 VALUES (0),(1),(2); + +CREATE TABLE t3 +( + f1 INT(11) DEFAULT NULL +); +INSERT INTO t3 VALUES (0),(1),(2); + +--echo # Original query +EXPLAIN EXTENDED DELETE +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +--echo # Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +--echo # Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta1, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +--echo # Applicable hint +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(t2, t3, ta2, ta1) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +--echo # Hint should be ignored +EXPLAIN EXTENDED DELETE /*+ JOIN_SUFFIX(ta2, t3, ta1) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +--echo # Both hints are ignored +EXPLAIN EXTENDED DELETE /*+ JOIN_PREFIX(ta1, t2, t3) JOIN_SUFFIX(t3, ta2) */ +FROM ta1.* USING t1 AS ta1 JOIN t1 AS ta2 ON 1 + RIGHT OUTER JOIN t2 ON (ta1.f1 = t2.f1) +WHERE (9) IN (SELECT f1 FROM t3); + +DROP TABLE t1, t2, t3; + +--echo # Const table behavior, table order is not changed, hint is applicable. +--echo # Note: Const tables are excluded from the process of dependency setting +--echo # since they are always first in the table order. Note that it +--echo # does not prevent the hint from being applied to the non-const +--echo # tables of the hint. + +CREATE TABLE t1(f1 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1); + +CREATE TABLE t2(f1 INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); + +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t1, t2) */ 1 FROM t1 JOIN t2 ON t1.f1 = t2.f1; +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t2, t1) */ 1 FROM t1 JOIN t2 ON t1.f1 = t2.f1; + +DROP TABLE t1, t2; + + +--echo # +--echo # Bug#23144274 WL9158:ASSERTION `JOIN->BEST_READ < DOUBLE(1.79769313486231570815E+308L)' FAILED +--echo # + +CREATE TABLE t1 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 varchar(255) DEFAULT NULL, +PRIMARY KEY (f1)); + +CREATE TABLE t2 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 varchar(255) CHARACTER SET utf8mb3 DEFAULT NULL, +f3 varchar(10) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f3(f3)); + +EXPLAIN SELECT /*+ JOIN_SUFFIX(t1, t2) */ t2.f3 FROM t2 LEFT JOIN t1 + ON t2.f1 = t1.f1 WHERE t1.f2 NOT LIKE ('FMGAU') OR t2.f2 > 't'; + +DROP TABLE t1, t2; + + +CREATE TABLE t1 +( + f1 int(11) DEFAULT NULL, + KEY f1 (f1) +); + +CREATE TABLE t2 +( + f1 int(11) DEFAULT NULL, + f2 varchar(255) CHARACTER SET utf8mb3 DEFAULT NULL, + KEY f2 (f2), + KEY f1 (f1) +); + +CREATE TABLE t3 ( + f1 int(11) DEFAULT NULL, + f2 varchar(255) CHARACTER SET cp932 DEFAULT NULL, + KEY f1 (f1), + KEY f2 (f2) +); + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t2, t3) JOIN_ORDER(t1, t2) */ t3.f1 + FROM ( t2 INNER JOIN t3 ON t3.f2 = t2.f2 LEFT JOIN t1 ON t1.f1 = t3.f1 ) + WHERE NOT (t2.f1 >= 7); + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(t1, t2) JOIN_ORDER(t2, t3) */ t3.f1 + FROM ( t2 INNER JOIN t3 ON t3.f2 = t2.f2 LEFT JOIN t1 ON t1.f1 = t3.f1 ) + WHERE NOT (t2.f1 >= 7); + +DROP TABLE t1, t2, t3; + +CREATE TABLE t1 ( + f1 INT(11) NOT NULL AUTO_INCREMENT, + f2 INT(11) DEFAULT NULL, + PRIMARY KEY (f1) +); + +CREATE TABLE t2 ( + f1 INT(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (f1) +); + +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t1, t1) */ t2.f1 FROM t1 JOIN t2 ON t1.f2 = t2.f1; + +DROP TABLE t1, t2; + +CREATE TABLE t1 +( + f1 DATETIME, + f2 DATE, + f3 VARCHAR(1), + KEY (f1) +) ENGINE=myisam; + +CREATE TABLE t2 +( + f1 VARCHAR(1), + f2 INT, + f3 VARCHAR(1), + KEY (f1) +) ENGINE=innodb; + +CREATE TABLE t3 +( + f1 VARCHAR(1), + f2 DATE, + f3 DATETIME, + f4 INT +) ENGINE=myisam; + +# Warning "Field or reference 'test.als2.f2' of SELECT #2 was resolved in SELECT #1" +# is generated during both PREPARE and EXECUTE of a prepared statement, so disable +# the PS protocol for this test +--disable_ps_protocol +EXPLAIN EXTENDED +UPDATE /*+ JOIN_ORDER(t2, als1, als3) JOIN_FIXED_ORDER() */ t3 AS als1 + JOIN t1 AS als2 ON (als1.f3 = als2 .f1) + JOIN t1 AS als3 ON (als1.f1 = als3.f3) + RIGHT OUTER JOIN t3 AS als4 ON (als1.f3 = als4.f2) +SET als1.f4 = 'eogqjvbhzodzimqahyzlktkbexkhdwxwgifikhcgblhgswxyutepc' +WHERE ('i','b') IN (SELECT f3, f1 FROM t2 WHERE f2 <> f2 AND als2.f2 IS NULL); +--enable_ps_protocol + +DROP TABLE t1, t2, t3; + + +CREATE TABLE t1( +f1 VARCHAR(1)) ENGINE=myisam; + +CREATE TABLE t2( +f1 VARCHAR(1), +f2 VARCHAR(1), +f3 DATETIME, +KEY(f2)) ENGINE=innodb; + +CREATE TABLE t3( +f1 INT, +f2 DATE, +f3 VARCHAR(1), +KEY(f3)) ENGINE=myisam; + +CREATE TABLE t4( +f1 VARCHAR(1), +KEY(f1)) ENGINE=innodb; +ALTER TABLE t4 DISABLE KEYS; +INSERT INTO t4 VALUES ('x'), (NULL), ('d'), ('x'), ('u'); +ALTER TABLE t4 ENABLE KEYS; + +CREATE TABLE t5( +f1 VARCHAR(1), +KEY(f1) ) ENGINE=myisam; +INSERT INTO t5 VALUES (NULL), ('s'), ('c'), ('x'), ('z'); + +EXPLAIN EXTENDED UPDATE /*+ JOIN_ORDER(t4, alias1, alias3) */ t3 AS alias1 + JOIN t5 ON (alias1.f3 = t5.f1) + JOIN t3 AS alias3 ON (alias1.f2 = alias3.f2 ) + RIGHT OUTER JOIN t1 ON (alias1.f3 = t1.f1) +SET alias1.f1 = -1 +WHERE ( 'v', 'o' ) IN +(SELECT DISTINCT t2.f1, t2.f2 FROM t4 RIGHT OUTER JOIN t2 ON (t4.f1 = t2.f1) + WHERE t2.f3 BETWEEN '2001-10-04' AND '2003-05-15'); + +DROP TABLE t1, t2, t3, t4, t5; + +CREATE TABLE t1 ( + f1 INT(11) DEFAULT NULL, + f3 VARCHAR(1) DEFAULT NULL, + f2 INT(11) DEFAULT NULL, + KEY (f1) +) ENGINE=MyISAM; + +CREATE TABLE t2( + f1 INT(11) DEFAULT NULL +) ENGINE=MyISAM; + +CREATE TABLE t3 ( + f1 VARCHAR(1) DEFAULT NULL, + f2 VARCHAR(1) DEFAULT NULL, + KEY (f2) +) ENGINE=InnoDB; + + +# Warning "Field or reference 'test.ta2.f3' of SELECT #2 was resolved in SELECT #1" +# is generated during both PREPARE and EXECUTE of a prepared statement, so disable +# the PS protocol for this test +--disable_ps_protocol +EXPLAIN EXTENDED UPDATE /*+ JOIN_SUFFIX(ta1, t2) */ + t1 AS ta1 JOIN t1 AS ta2 ON ta1.f1 = ta2.f1 RIGHT JOIN t2 ON (ta1.f1 = t2.f1) +SET ta1.f2 = '', ta2.f3 = '' +WHERE ('n', 'r') IN (SELECT f2, f1 FROM t3 WHERE f1 <> f2 XOR ta2.f3 IS NULL); +--enable_ps_protocol + +DROP TABLE t1, t2, t3; + + +CREATE TABLE t2(f1 VARCHAR(255) DEFAULT NULL, f2 INT(11) DEFAULT NULL, + KEY (f1), KEY (f2)) charset latin1 ENGINE=MyISAM; + +CREATE TABLE t4(f1 INT(11) DEFAULT NULL, f2 INT(11) DEFAULT NULL, KEY (f1)) +charset latin1 ENGINE=MyISAM; +CREATE TABLE t5(f1 INT(11) NOT NULL AUTO_INCREMENT, f2 INT(11) DEFAULT NULL, PRIMARY KEY (f1)) +charset latin1 ENGINE=InnoDB; +CREATE TABLE t6(f1 INT(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (f1)) +charset latin1 ENGINE=InnoDB; +CREATE TABLE t7 (f1 VARCHAR(255) DEFAULT NULL) +charset latin1 ENGINE=InnoDB; + +CREATE TABLE t10(f1 INT(11) NOT NULL AUTO_INCREMENT,f2 INT(11) DEFAULT NULL,f3 VARCHAR(10) DEFAULT NULL, + PRIMARY KEY (f1),KEY (f2),KEY (f3)) charset latin1 ENGINE=MyISAM; + +CREATE TABLE t11(f1 INT(11) DEFAULT NULL,f2 VARCHAR(10) DEFAULT NULL, + KEY (f1),KEY (f2)) charset latin1 ENGINE=InnoDB; + +EXPLAIN EXTENDED +SELECT /*+ JOIN_ORDER(alias11, alias8) */ 1 +FROM t4 AS alias4 + LEFT JOIN t5 AS alias5 JOIN t6 AS alias6 ON alias5.f2 = alias6.f1 + LEFT JOIN t7 AS alias7 JOIN t2 AS alias8 ON alias7.f1 = alias8.f1 + ON alias5.f1 = alias8.f2 ON alias4.f2 = alias6.f1 + JOIN t10 AS alias10 JOIN t11 AS alias11 ON alias10.f1 = alias11.f1 + ON alias4.f2 = alias11.f2; + +EXPLAIN EXTENDED +SELECT /*+ JOIN_ORDER(alias11, alias10, alias8, alias7) */ 1 +FROM t4 AS alias4 + LEFT JOIN t5 AS alias5 JOIN t6 AS alias6 ON alias5.f2 = alias6.f1 + LEFT JOIN t7 AS alias7 JOIN t2 AS alias8 ON alias7.f1 = alias8.f1 + ON alias5.f1 = alias8.f2 ON alias4.f2 = alias6.f1 + JOIN t10 AS alias10 JOIN t11 AS alias11 ON alias10.f1 = alias11.f1 + ON alias4.f2 = alias11.f2; + +DROP TABLES t2, t4, t5, t6, t7, t10, t11; + +CREATE TABLE t1 (f1 VARCHAR(255) DEFAULT NULL, f2 VARCHAR(255) DEFAULT NULL, + KEY (f1), KEY (f2)) ENGINE=InnoDB; + +CREATE TABLE t2(f1 VARCHAR(255) DEFAULT NULL, f2 INT(11) DEFAULT NULL, + KEY (f1), KEY (f2)) ENGINE=InnoDB; + +CREATE TABLE t3( + f1 INT(11) NOT NULL AUTO_INCREMENT, f2 VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (f1), KEY (f2)) ENGINE=InnoDB; + +CREATE TABLE t4(f1 INT(11) DEFAULT NULL, f2 INT(11) DEFAULT NULL, KEY (f1)) ENGINE=InnoDB; +CREATE TABLE t6(f1 INT(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (f1)) ENGINE=InnoDB; +CREATE TABLE t7 (f1 VARCHAR(255) DEFAULT NULL) ENGINE=InnoDB; + +CREATE TABLE t10(f1 INT(11) NOT NULL AUTO_INCREMENT,f2 INT(11) DEFAULT NULL,f3 VARCHAR(10) DEFAULT NULL, + PRIMARY KEY (f1),KEY (f2),KEY (f3)) ENGINE=InnoDB; + +EXPLAIN EXTENDED SELECT /*+ JOIN_ORDER(alias8, alias6) */ 1 +FROM t1 AS alias1 + LEFT JOIN t7 alias7 + JOIN t2 AS alias2 + LEFT JOIN t3 AS alias3 JOIN t4 AS alias4 ON alias4.f2 = alias3.f1 + ON alias4.f1 = alias2.f1 + ON alias2.f2 = alias7.f1 + JOIN t10 AS alias5 + LEFT JOIN t6 AS alias6 JOIN t2 AS alias8 ON alias6.f1 = alias8.f2 + ON alias6.f1 = alias5.f1 + ON alias5.f3 = alias7.f1 + ON alias1.f2 = alias7.f1; + +DROP TABLES t1, t2, t3, t4, t6, t7, t10; + + +--echo # +--echo # Bug#23144230 WL#9158 : OPT_HINTS_QB::APPLY_JOIN_ORDER_HINTS - MYSQLD GOT SIGNAL 11 +--echo # + +CREATE TABLE t1 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 int(11) DEFAULT NULL, +f3 int(11) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f2 (f2)) +ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; + +CREATE TABLE t2 ( +f1 int(11) NOT NULL AUTO_INCREMENT, +f2 int(11) DEFAULT NULL, +f3 int(11) DEFAULT NULL, +PRIMARY KEY (f1), +KEY f2 (f2)) +ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; + +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(alias1) */ alias2.f3 AS field1 + FROM ( SELECT * FROM t1 ) AS alias1 JOIN ( SELECT * FROM t2 ) AS alias2 + ON alias1.f2 = alias2.f3 +WHERE alias2.f2 IS NULL HAVING (field1 != 3 AND field1 >= 8); + +DROP TABLE t1, t2; + + +--echo # +--echo # Bug#23651098 WL#9158 : ASSERTION `!(SJ_NEST->SJ_INNER_TABLES & JOIN->CONST_TABLE_MAP)' FAILED +--echo # + +CREATE TABLE t1 +( + f1 INT(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (f1) +) ENGINE=InnoDB; + +CREATE TABLE t2 +( + f1 VARCHAR(1) DEFAULT NULL +) ENGINE=MyISAM; + +CREATE TABLE t3 +( + f1 VARCHAR(1) DEFAULT NULL +) ENGINE=MyISAM; + +EXPLAIN EXTENDED SELECT /*+ JOIN_PREFIX(t2, t1) */ t1.f1 FROM t1, t2 +WHERE t2.f1 IN (SELECT t3.f1 FROM t3) AND t1.f1 = 183; + +DROP TABLE t1, t2, t3; + +--echo # +--echo # Bug23715779 SELECT QUERY WITH JOIN_PREFIX() HINT RETURNS INCORRECT RESULT +--echo # + +CREATE TABLE t1(f1 INT(11) NOT NULL); +INSERT INTO t1 VALUES (10); + +CREATE TABLE t2 +( + f1 INT(11) NOT NULL AUTO_INCREMENT, + f2 INT(11) DEFAULT NULL, + PRIMARY KEY (f1), + KEY (f2) +); +INSERT INTO t2 VALUES (1, 7), (2, 1), (4, 7); + +CREATE TABLE t4(f1 INT DEFAULT NULL); +INSERT INTO t4 VALUES (2); + +ANALYZE TABLE t1, t2, t4; + +let $query= +SELECT +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND + ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +eval $query; +eval explain extended $query; + +let $query= +SELECT /*+ JOIN_PREFIX(t2@qb2, t4@qb1, ta3, ta4) */ +COUNT(*) FROM t1 JOIN t2 AS ta3 JOIN t2 AS ta4 +WHERE ta4.f1 IN (SELECT /*+ QB_NAME(qb1) */ f1 FROM t4) AND + ta3.f2 IN (SELECT /*+ QB_NAME(qb2) */ f2 FROM t2); +eval $query; +eval explain extended $query; + +DROP TABLE t1, t2, t4; diff --git a/mysql-test/main/opt_hints_subquery.result b/mysql-test/main/opt_hints_subquery.result new file mode 100644 index 0000000000000..d37f7cc77ca79 --- /dev/null +++ b/mysql-test/main/opt_hints_subquery.result @@ -0,0 +1,1856 @@ +CREATE TABLE t1 (a INTEGER NOT NULL, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INTEGER NOT NULL, KEY (a)); +CREATE TABLE t3 (a INTEGER NOT NULL, b INT, KEY (a)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (20,4), (30,5); +ANALYZE TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +# Parser tests +# Correct hints (no warnings): +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN() */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) SEMIJOIN(@`qb1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) NO_SEMIJOIN(@qb1 firstmatch) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) NO_SEMIJOIN(@`qb1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) SEMIJOIN( @qb1 firstmatch, dupsweedout ) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) SEMIJOIN(@`qb1` FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN( FIRSTMATCH, LOOSESCAN,materialization, DUPSWEEDOUT ) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`select#1` FIRSTMATCH, LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb2) NO_SEMIJOIN(@qb2 FIRSTMATCH,LOOSESCAN, materialization, DUPSWEEDOUT) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ QB_NAME(`qb2`) NO_SEMIJOIN(@`qb2` FIRSTMATCH, LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +set optimizer_switch='derived_merge=off'; +# Correct 'cause hints refer to different query blocks: +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@qb1) SEMIJOIN(loosescan)*/ a +FROM (SELECT /*+ QB_NAME(qb1)*/ * FROM t2) AS tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t2 index NULL a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`qb1`) SEMIJOIN(@`select#1` LOOSESCAN) */ `tt`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`a` AS `a` from `test`.`t2`) `tt` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN()*/ a +FROM (SELECT /*+ SEMIJOIN(loosescan)*/ * FROM t2) AS tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t2 index NULL a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`select#2` LOOSESCAN) SEMIJOIN(@`select#1`) */ `tt`.`a` AS `a` from (/* select#2 */ select `test`.`t2`.`a` AS `a` from `test`.`t2`) `tt` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(materialization) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SUBQUERY(@`select#1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY( INTOEXISTS ) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SUBQUERY(@`select#1` INTOEXISTS) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME (qb1) SUBQUERY(@qb1 materialization) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ QB_NAME(`qb1`) SUBQUERY(@`qb1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# Incorrect hints (warnings) +SELECT /*+ SEMIJOIN(loosescan @qb1) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb1) */ a FROM t1' at line 1 +SELECT /*+ SEMIJOIN(@qb1 @qb2) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb2) */ a FROM t1' at line 1 +SELECT /*+ SEMIJOIN(@qb1 LOOSESCAN,materialization, unknown_strategy) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near 'unknown_strategy) */ a FROM t1' at line 1 +SELECT /*+ NO_SEMIJOIN(@qb1, @qb2) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near ', @qb2) */ a FROM t1' at line 1 +SELECT /*+ NO_SEMIJOIN(FIRSTMATCH, ,LOOSESCAN, materialization) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near ',LOOSESCAN, materialization) */ a FROM t1' at line 1 +SELECT /*+ NO_SEMIJOIN(FIRSTMATCH, @qb2,LOOSESCAN) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb2,LOOSESCAN) */ a FROM t1' at line 1 +SELECT /*+ SUBQUERY(wrong_strat) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near 'wrong_strat) */ a FROM t1' at line 1 +SELECT /*+ SUBQUERY(materialization, intoexists) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near ', intoexists) */ a FROM t1' at line 1 +SELECT /*+ SUBQUERY(@qb1 materialization) */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 4211 Query block name `qb1` is not found for SUBQUERY hint +SELECT /*+ SUBQUERY() */ a FROM t1; +a +1 +2 +3 +4 +Warnings: +Warning 1064 Optimizer hint syntax error near ') */ a FROM t1' at line 1 +# Mix of correct and incorrect hints: +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(firstmatch ) SEMIJOIN(loosescan @qb1) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb1) */ a FROM t1' at line 2 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@qb1, @qb2) SEMIJOIN()*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 1064 Optimizer hint syntax error near ', @qb2) SEMIJOIN()*/ a FROM t1' at line 2 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN() NO_SEMIJOIN(FIRSTMATCH, @qb2,LOOSESCAN) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 1064 Optimizer hint syntax error near '@qb2,LOOSESCAN) */ a FROM t1' at line 2 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` +# Conflicting hints: +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN() SEMIJOIN(dupsweedout) NO_SEMIJOIN(firstmatch)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(DUPSWEEDOUT) is ignored as conflicting/duplicated +Warning 4210 Hint NO_SEMIJOIN(FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(loosescan,materialization) SEMIJOIN(dupsweedout)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(DUPSWEEDOUT) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`select#1` LOOSESCAN, MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(firstmatch,loosescan,materialization) SEMIJOIN() NO_SEMIJOIN()*/ a +FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN() is ignored as conflicting/duplicated +Warning 4210 Hint NO_SEMIJOIN() is ignored as conflicting/duplicated +Note 1003 select /*+ NO_SEMIJOIN(@`select#1` FIRSTMATCH, LOOSESCAN, MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) SEMIJOIN(loosescan) NO_SEMIJOIN(@qb1 dupsweedout)*/ a +FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(LOOSESCAN) is ignored as conflicting/duplicated +Warning 4210 Hint NO_SEMIJOIN(@`qb1` DUPSWEEDOUT) is ignored as conflicting/duplicated +Note 1003 select /*+ QB_NAME(`qb1`) SEMIJOIN(@`qb1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED SELECT /*+ SEMIJOIN(firstmatch) NO_SEMIJOIN()*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint NO_SEMIJOIN() is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`select#1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ SUBQUERY(@`select#1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN() SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(MATERIALIZATION) is ignored as conflicting/duplicated +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ SUBQUERY(@`select#1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists) SUBQUERY(materialization)*/ a +FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Warning 4210 Hint SUBQUERY(MATERIALIZATION) is ignored as conflicting/duplicated +Note 1003 select /*+ SUBQUERY(@`select#1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(materialization) SEMIJOIN(firstmatch) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(FIRSTMATCH) is ignored as conflicting/duplicated +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ SUBQUERY(@`select#1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) SUBQUERY(@qb1 materialization) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(@`qb1` MATERIALIZATION) is ignored as conflicting/duplicated +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ QB_NAME(`qb1`) SEMIJOIN(@`qb1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ QB_NAME(qb1) SUBQUERY(@qb1 materialization) SEMIJOIN(@qb1 firstmatch) SUBQUERY(intoexists)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(@`qb1` FIRSTMATCH) is ignored as conflicting/duplicated +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 select /*+ QB_NAME(`qb1`) SUBQUERY(@`qb1` MATERIALIZATION) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@qb1) SEMIJOIN(loosescan) NO_SEMIJOIN(@qb1 dupsweedout)*/ a +FROM (SELECT /*+ QB_NAME(qb1)*/ * FROM t2) AS tt; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t2 index NULL a 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint NO_SEMIJOIN(@`qb1` DUPSWEEDOUT) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`qb1`) SEMIJOIN(@`select#1` LOOSESCAN) */ `tt`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`a` AS `a` from `test`.`t2`) `tt` +DROP TABLE t1, t2 ,t3; +set optimizer_switch=default; +# End of parser tests +CREATE TABLE t1 (a INTEGER NOT NULL, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INTEGER NOT NULL, KEY (a)); +CREATE TABLE t3 (a INTEGER NOT NULL, b INT, KEY (a)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (2), (3), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (15,3), (20,4), (30,5); +ANALYZE TABLE t1, t2, t3; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +test.t2 analyze status Engine-independent statistics collected +test.t2 analyze status OK +test.t3 analyze status Engine-independent statistics collected +test.t3 analyze status OK +# This query will normally use Table Pull-out +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +# Check that we can disable SEMIJOIN transformation +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#2`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Same with hint in outer query +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Query with two sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) +AND t3.b IN (SELECT a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and `test`.`ty`.`a` = `test`.`t3`.`b` +# No SEMIJOIN transformation for first subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 tx) +AND t3.b IN (SELECT a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t3` where `test`.`ty`.`a` = `test`.`t3`.`b` and <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(`subq1`) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t3` where `test`.`ty`.`a` = `test`.`t3`.`b` and <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) +# No SEMIJOIN transformation for latter subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) +AND t3.b IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#3`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and <`test`.`t3`.`b`>((`test`.`t3`.`b`,(((`test`.`t3`.`b`) in t1 on PRIMARY)))) +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@`subq2`) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and <`test`.`t3`.`b`>((`test`.`t3`.`b`,(((`test`.`t3`.`b`) in t1 on PRIMARY)))) +# No SEMIJOIN transformation for any subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#2`) NO_SEMIJOIN(@`select#3`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) and <`test`.`t3`.`b`>((`test`.`t3`.`b`,(((`test`.`t3`.`b`) in t1 on PRIMARY)))) +EXPLAIN +SELECT /*+ NO_SEMIJOIN(@subq1) NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 Using index +# Query with nested sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using where +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.tx.b 1 100.00 Using index +Warnings: +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`ty`.`a` = `test`.`tx`.`b` and `test`.`tx`.`a` = `test`.`t3`.`a` +# No SEMIJOIN transformation for outer subquery +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY tx eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where +2 DEPENDENT SUBQUERY ty eq_ref PRIMARY PRIMARY 4 test.tx.b 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`tx`.`a` from `test`.`t1` `ty` join `test`.`t1` `tx` where `test`.`ty`.`a` = `test`.`tx`.`b` and (`test`.`t3`.`a`) = `test`.`tx`.`a`))) +# No SEMIJOIN transformation for inner-most subquery +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and <`test`.`tx`.`b`>((`test`.`tx`.`b`,(((`test`.`tx`.`b`) in t1 on PRIMARY)))) +# No SEMIJOIN transformation at all +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq2`) NO_SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY where (`test`.`tx`.`b`,(((`test`.`tx`.`b`) in t1 on PRIMARY))) and (`test`.`t3`.`a`) = `test`.`tx`.`a`)))) +# This query does not support SEMIJOIN. SEMIJOIN hint is ignored +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL distinct_key NULL NULL NULL 4 100.00 +1 PRIMARY t2 ref a a 4 .min(a) 1 100.00 Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`subq`) */ min(`test`.`t1`.`a`) from `test`.`t1` group by `test`.`t1`.`a`) join `test`.`t2` where `test`.`t2`.`a` = ``.`min(a)` +# This query will get LooseScan by default +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Let's turn off LooseScan, FirstMatch is then SELECTed +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# Let's also turn off FirstMatch, DupsWeedout is then used. +# (StartTemporary, EndTemporary) in the output indicate DupsWeedout usage +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Materialization is used here +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where 1 +# Turn off all strategies, DuplicateWeedout should be used as a fallback +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, MATERIALIZATION, +DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Turn off non-used strategies, nothing should change. Still Loosescan +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Test same query with SEMIJOIN hint +# Forcing LooseScan, should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Force FirstMatch +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# Force Materialization +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where 1 +# Force DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# If LooseScan is among candidates, it will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION, +DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Drop LooseScan from list of strategies, FirstMatch will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +For this query LooseScan and Materialization is not applicable +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.b 1 100.00 Using where; FirstMatch(t1) +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t3`.`a` = `test`.`t1`.`b` +# Turn off all applicable strategies. DuplicateWeedout should be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +# Similar with SEMIJOIN hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, MATERIALIZATION) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +# Test multiple subqueries. +# Default for this query is Loosecan for first and FirstMatch for latter +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Forcing the default strategy should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) SEMIJOIN(@`subq2` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Forcing a strategy for one, may change the other due to cost changes +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t3) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Forcing same strategy for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t3) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) SEMIJOIN(@`subq2` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Loosescan for both is not possible, ends up with DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) SEMIJOIN(@`subq2` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Swap strategies compared to default +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) SEMIJOIN(@`subq2` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Different subsets of strategies for different subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) +SEMIJOIN(@subq2 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) SEMIJOIN(@`subq2` MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Vice versa +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) +SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION, DUPSWEEDOUT) SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Another combination +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, FIRSTMATCH) +SEMIJOIN(@subq2 LOOSESCAN, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION, FIRSTMATCH) SEMIJOIN(@`subq2` LOOSESCAN, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Turn off default +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) +NO_SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Also turn off 2nd choice. Gives DuplicateWeedout over both +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) +NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Also turn off DuplicateWeedout. Materialization is only one left. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) +NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where 1 +# Force materialization with SEMIJOIN hints instead +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) +SEMIJOIN(@subq2 MATERIALIZATION) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) SEMIJOIN(@`subq2` MATERIALIZATION) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where 1 +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) +SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) SEMIJOIN(@`subq2` DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t1`.`b` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) +SEMIJOIN(@subq2 LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch((sj-nest)) +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) SEMIJOIN(@`subq2` LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t1`.`b` +# A query with nested subqueries which are joined together +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using where +1 PRIMARY t2 ref a a 4 test.t3.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t3`.`a` = `test`.`t1`.`a` +# Let's turn off FirtMatch, DuplicateWeedout is then chosen +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 +1 PRIMARY t2 ref a a 4 test.t3.b 1 16.67 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t1`.`a` = `test`.`t3`.`a` +# If we turn off all strategies, DuplicateWeedout should still be used +EXPLAIN +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, MATERIALIZATION, +DUPSWEEDOUT) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 +1 PRIMARY t2 ref a a 4 test.t3.b 1 Using index; End temporary +# Test the same query with SEMIJOIN hint +# Force FirstMatch, should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using where +1 PRIMARY t2 ref a a 4 test.t3.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t3`.`a` = `test`.`t1`.`a` +# Force LooseScan, will fall back to DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 +1 PRIMARY t2 ref a a 4 test.t3.b 1 16.67 Using index; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t1`.`a` = `test`.`t3`.`a` +# Force DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 +1 PRIMARY t2 ref a a 4 test.t3.b 1 16.67 Using index; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t1`.`a` = `test`.`t3`.`a` +# If FirstMatch is among candidates, it will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using where +1 PRIMARY t2 ref a a 4 test.t3.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t3`.`a` = `test`.`t1`.`a` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, FIRSTMATCH, LOOSESCAN, +DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 +WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using where +1 PRIMARY t2 ref a a 4 test.t3.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION, FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`b` and `test`.`t3`.`a` = `test`.`t1`.`a` +# Test hints with prepared statements +PREPARE stmt1 FROM "EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +DEALLOCATE PREPARE stmt1; +SET optimizer_switch = default; +# Tests with non-default optimizer_switch settings +SET optimizer_switch = 'semijoin=off'; +# No table pull-out for this query +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# This should not change anything +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Force semijoin, table pull-out is performed +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +# Setting strategy should still force semijoin +# Strategy is ignored since table pull-out is done +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq` FIRSTMATCH) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +# Query with two sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) +AND t3.b IN (SELECT a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) and <`test`.`t3`.`b`>((`test`.`t3`.`b`,(((`test`.`t3`.`b`) in t1 on PRIMARY)))) +# SEMIJOIN transformation for first subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and <`test`.`t3`.`b`>((`test`.`t3`.`b`,(((`test`.`t3`.`b`) in t1 on PRIMARY)))) +# SEMIJOIN transformation for latter subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`subq2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t3` where `test`.`ty`.`a` = `test`.`t3`.`b` and <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) +# SEMIJOIN transformation for both subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1`) SEMIJOIN(@`subq2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and `test`.`ty`.`a` = `test`.`t3`.`b` +# Query with nested sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY where (`test`.`tx`.`b`,(((`test`.`tx`.`b`) in t1 on PRIMARY))) and (`test`.`t3`.`a`) = `test`.`tx`.`a`)))) +# SEMIJOIN transformation for outer subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and <`test`.`tx`.`b`>((`test`.`tx`.`b`,(((`test`.`tx`.`b`) in t1 on PRIMARY)))) +# SEMIJOIN transformation for inner-most subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY tx eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where +2 DEPENDENT SUBQUERY ty eq_ref PRIMARY PRIMARY 4 test.tx.b 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SEMIJOIN(@`subq2`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`tx`.`a` from `test`.`t1` `ty` join `test`.`t1` `tx` where `test`.`ty`.`a` = `test`.`tx`.`b` and (`test`.`t3`.`a`) = `test`.`tx`.`a`))) +# SEMIJOIN transformation for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using where +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.tx.b 1 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq2`) SEMIJOIN(@`subq1`) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`ty`.`a` = `test`.`tx`.`b` and `test`.`tx`.`a` = `test`.`t3`.`a` +Test strategies when some are disabled by optimizer_switch +SET optimizer_switch='semijoin=on'; +SET optimizer_switch='loosescan=off'; +# This query will get LooseScan by default. FirstMatch now +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# Let's turn off LooseScan also by hint, FirstMatch is then selected +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# Let's also turn off FirstMatch, DupsWeedout is then used. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Turn off DupsWeedout, Materialization is used here +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where 1 +# Let's force LooseScan back on +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Forcing another strategy +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where 1 +# If LooseScan is among candidates, it is used even if originally disabled +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION, +DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +Disable another strategy +SET optimizer_switch='firstmatch=off'; +# Turn on FirstMatch, but not LooseScan on with hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# Drop all remaining strategies with hint, should use DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# For this query LooseScan and Materialization is not applicable +# Should use DuplicateWeedout since FirstMatch is disabled +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +Turn off all applicable strategies. DuplicateWeedout should still be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +Reverse which strategies are allowed with hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.b 1 100.00 Using where; FirstMatch(t1) +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t3`.`a` = `test`.`t1`.`b` +# Default for this query is Loosecan for first and FirstMatch for latter +# Since both strategies are disabled, will now use DuplicateWeedout +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Allowing LooseScan and FirstMatch and optimizer_switch is ignored +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) +SEMIJOIN(@subq2 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH) SEMIJOIN(@`subq2` LOOSESCAN, FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Forcing a disabled strategy for one +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Forcing same strategy for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t3) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) SEMIJOIN(@`subq2` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Swap strategies compared to default +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH) SEMIJOIN(@`subq2` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Different subsets of strategies for different subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) +SEMIJOIN(@subq2 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) SEMIJOIN(@`subq2` MATERIALIZATION, DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Turn off DuplicateWeedout for both. Materialization is left +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 DUPSWEEDOUT) +NO_SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` DUPSWEEDOUT) NO_SEMIJOIN(@`subq2` DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where 1 +# Forcing materialization should have same effect +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) +SEMIJOIN(@subq2 MATERIALIZATION) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` MATERIALIZATION) SEMIJOIN(@`subq2` MATERIALIZATION) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where 1 +# Turn off DuplicateWeedout for first. MatLookup is used for both +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t1`.`b` +# Turn off DuplicateWeedout for second. Same effect. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 End temporary +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq2` DUPSWEEDOUT) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` +Enable all strategies except DuplicateWeedout +SET optimizer_switch='firstmatch=on,loosescan=on,materialization=on,duplicateweedout=off'; +# If we turn off all other strategies, DuplicateWeedout will be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH, MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# LooseScan and Materialization is not applicable, FirstMatch is used +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.b 1 100.00 Using where; FirstMatch(t1) +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t3`.`a` = `test`.`t1`.`b` +# Turn off all applicable strategies. DuplicateWeedout should be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +# Similar with SEMIJOIN hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, MATERIALIZATION) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +# Disable all strategies +SET optimizer_switch='firstmatch=off,loosescan=off,materialization=off,duplicateweedout=off'; +# DuplicateWeedout is then used +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Turning off extra strategies should not change anything +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t2 ref a a 4 test.t3.a 1 25.00 Using index; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN, DUPSWEEDOUT) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t2`.`a` = `test`.`t3`.`a` +# Turning on some strategies should give one of those +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index a a 4 NULL 6 100.00 Using index +1 PRIMARY t3 ref a a 4 test.t2.a 1 100.00 Using index; FirstMatch(t2) +Warnings: +Note 1003 select /*+ SEMIJOIN(@`subq1` FIRSTMATCH, MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` semi join (`test`.`t3`) where `test`.`t3`.`a` = `test`.`t2`.`a` +# For this query that cannot use LooseScan or Materialization, +# turning those on will still give DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.b 1 25.00 Using where; End temporary +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, MATERIALIZATION) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t1`.`a` = `test`.`t3`.`b` and `test`.`t1`.`b` = `test`.`t3`.`a` +# Turning on FirstMatch should give FirstMatch +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.b 1 100.00 Using where; FirstMatch(t1) +Warnings: +Note 1276 Field or reference 'test.t1.a' of SELECT #2 was resolved in SELECT #1 +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN, FIRSTMATCH) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) where `test`.`t3`.`b` = `test`.`t1`.`a` and `test`.`t3`.`a` = `test`.`t1`.`b` +SET optimizer_switch = default; +Test that setting optimizer_switch after prepare will change strategy +PREPARE stmt1 FROM "EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +SET optimizer_switch = 'duplicateweedout=off'; +Will now use materialization +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 100.00 +2 MATERIALIZED t3 index a a 4 NULL 4 100.00 Using index +3 MATERIALIZED t2 index a a 4 NULL 6 100.00 Using index +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where 1 +SET optimizer_switch = 'duplicateweedout=on'; +Turn DuplicateWeedout back on +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; Start temporary +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where; End temporary +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` FIRSTMATCH, LOOSESCAN) NO_SEMIJOIN(@`subq2` FIRSTMATCH, LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +DEALLOCATE PREPARE stmt1; +SET optimizer_switch = default; +# Specifying two SEMIJOIN/NO_SEMIJOIN for same query block gives warning +# First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN() is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#2`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SEMIJOIN() NO_SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Warning 4210 Hint NO_SEMIJOIN() is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`select#2`) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +# Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Warning 4210 Hint NO_SEMIJOIN(@`subq` ) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +# Specify at different levels, opposite order +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq` ) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Duplicate hints also gives warning, but hint has effect +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq` ) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint NO_SEMIJOIN(@`subq` ) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Multiple subqueries with conflicting hints +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) NO_SEMIJOIN() */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) SEMIJOIN(LOOSESCAN) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 100.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +2 DEPENDENT SUBQUERY t3 index_subquery a a 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq1` LOOSESCAN) is ignored as conflicting/duplicated +Warning 4210 Hint SEMIJOIN(@`subq2` FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq1`) SEMIJOIN(@`subq2` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t2`) where `test`.`t2`.`a` = `test`.`t1`.`b` and <`test`.`t1`.`a`>((`test`.`t1`.`a`,(((`test`.`t1`.`a`) in t3 on a)))) +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) NO_SEMIJOIN(LOOSESCAN) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) SEMIJOIN(LOOSESCAN) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; Start temporary; End temporary +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq1` LOOSESCAN) is ignored as conflicting/duplicated +Warning 4210 Hint SEMIJOIN(@`subq2` FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN) SEMIJOIN(@`subq2` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Conflicting hints in same hint comment +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq1` FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) NO_SEMIJOIN(@subq1 LOOSESCAN) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Warning 4210 Hint NO_SEMIJOIN(@`subq1` LOOSESCAN) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 4 100.00 Using where +1 PRIMARY t3 ref a a 4 test.t1.a 1 100.00 Using index; FirstMatch(t1) +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t3) +Warnings: +Warning 4210 Hint NO_SEMIJOIN(@`subq1` FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 select /*+ NO_SEMIJOIN(@`subq1` LOOSESCAN) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t3`.`a` = `test`.`t1`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# Non-supported strategies should give warnings +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 INTOEXISTS) NO_SEMIJOIN(@subq2 INTOEXISTS) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) +AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 index a a 4 NULL 4 100.00 Using index; LooseScan +1 PRIMARY t1 eq_ref PRIMARY PRIMARY 4 test.t3.a 1 25.00 Using where +1 PRIMARY t2 ref a a 4 test.t1.b 1 66.67 Using index; FirstMatch(t1) +Warnings: +Warning 1064 Optimizer hint syntax error near 'INTOEXISTS) NO_SEMIJOIN(@subq2 INTOEXISTS) */ * +FROM t1 +WHERE t1.a IN (SELECT...' at line 2 +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` semi join (`test`.`t3`) semi join (`test`.`t2`) where `test`.`t1`.`a` = `test`.`t3`.`a` and `test`.`t2`.`a` = `test`.`t1`.`b` +# SUBQUERY tests +# SUBQUERY should disable SEMIJOIN and use specified subquery strategy +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`select#2` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +EXPLAIN +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 index NULL a 4 NULL 6 Using where; Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 Using index +# Query with two subqueries +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 INTOEXISTS) SUBQUERY(@subq2 MATERIALIZATION) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +3 MATERIALIZED ty index NULL PRIMARY 4 NULL 4 100.00 Using index +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq1` INTOEXISTS) SUBQUERY(@`subq2` MATERIALIZATION) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY)))) and <`test`.`t3`.`b`>((`test`.`t3`.`b`,`test`.`t3`.`b` in ( (/* select#3 */ select /*+ QB_NAME(`subq2`) */ `test`.`ty`.`a` from `test`.`t1` `ty` ), (`test`.`t3`.`b` in on distinct_key where `test`.`t3`.`b` = ``.`a`)))) +# Query with nested sub-queries +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 INTOEXISTS) SUBQUERY(@subq2 MATERIALIZATION) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 DEPENDENT SUBQUERY tx unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using where +3 MATERIALIZED ty index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq2` MATERIALIZATION) SUBQUERY(@`subq1` INTOEXISTS) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,(((`test`.`t3`.`a`) in t1 on PRIMARY where (`test`.`tx`.`b`,`test`.`tx`.`b` in ( (/* select#3 */ select /*+ QB_NAME(`subq2`) */ `test`.`ty`.`a` from `test`.`t1` `ty` ), (`test`.`tx`.`b` in on distinct_key where `test`.`tx`.`b` = ``.`a`))) and (`test`.`t3`.`a`) = `test`.`tx`.`a`)))) +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION) SUBQUERY(@subq2 INTOEXISTS) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx +WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL NULL NULL NULL NULL 4 100.00 Using where +2 MATERIALIZED tx ALL NULL NULL NULL NULL 4 100.00 Using where +3 DEPENDENT SUBQUERY ty unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq2` INTOEXISTS) SUBQUERY(@`subq1` MATERIALIZATION) */ `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t3` where <`test`.`t3`.`a`>((`test`.`t3`.`a`,`test`.`t3`.`a` in ( (/* select#2 */ select /*+ QB_NAME(`subq1`) */ `test`.`tx`.`a` from `test`.`t1` `tx` where <`test`.`tx`.`b`>((`test`.`tx`.`b`,(((`test`.`tx`.`b`) in t1 on PRIMARY)))) ), (`test`.`t3`.`a` in on distinct_key where `test`.`t3`.`a` = ``.`a`)))) +# This query does not support SEMIJOIN. Materialization is default +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL distinct_key NULL NULL NULL 4 100.00 +1 PRIMARY t2 ref a a 4 .min(a) 1 100.00 Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`subq`) */ min(`test`.`t1`.`a`) from `test`.`t1` group by `test`.`t1`.`a`) join `test`.`t2` where `test`.`t2`.`a` = ``.`min(a)` +# Use In-to-exists instead +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 index NULL PRIMARY 4 NULL 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq`) */ min(`test`.`t1`.`a`) from `test`.`t1` group by `test`.`t1`.`a` having (`test`.`t2`.`a`) = (min(`test`.`t1`.`a`))))) +# For this query In-to-exists is default +EXPLAIN EXTENDED +SELECT a, a IN (SELECT a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a`,<`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) AS `a IN (SELECT a FROM t1)` from `test`.`t2` +# Force Subquery Materialization +EXPLAIN EXTENDED +SELECT a, a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`select#2` MATERIALIZATION) */ `test`.`t2`.`a` AS `a`,<`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`a`)))) AS `a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1)` from `test`.`t2` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ a, +a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` MATERIALIZATION) */ `test`.`t2`.`a` AS `a`,<`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select /*+ QB_NAME(`subq`) */ `test`.`t1`.`a` from `test`.`t1` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`a`)))) AS `a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1)` from `test`.`t2` +# This query does not support Subquery Materialization due to type mismatch +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ sum(b) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 index NULL PRIMARY 4 NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq`) */ sum(`test`.`t1`.`b`) from `test`.`t1` group by `test`.`t1`.`a` having (`test`.`t2`.`a`) = (sum(`test`.`t1`.`b`))))) +# Trying to force Subquery Materialization will not change anything +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ sum(b) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 index NULL PRIMARY 4 NULL 1 100.00 +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq`) */ sum(`test`.`t1`.`b`) from `test`.`t1` group by `test`.`t1`.`a` having (`test`.`t2`.`a`) = (sum(`test`.`t1`.`b`))))) +# Test hints with prepared statements +PREPARE stmt1 FROM "EXPLAIN +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION) + SUBQUERY(@subq2 INTOEXISTS) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +3 DEPENDENT SUBQUERY t2 index_subquery a a 4 func 1 Using index +2 MATERIALIZED t3 index NULL a 4 NULL 4 Using index +EXECUTE stmt1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +3 DEPENDENT SUBQUERY t2 index_subquery a a 4 func 1 Using index +2 MATERIALIZED t3 index NULL a 4 NULL 4 Using index +DEALLOCATE PREPARE stmt1; +# Test optimizer_switch settings with SUBQUERY hint +SET optimizer_switch='materialization=off'; +This query will now use In-to-exist +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 index NULL PRIMARY 4 NULL 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(/* select#2 */ select /*+ QB_NAME(`subq`) */ min(`test`.`t1`.`a`) from `test`.`t1` group by `test`.`t1`.`a` having (`test`.`t2`.`a`) = (min(`test`.`t1`.`a`))))) +# Force it to use Materialization +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select /*+ QB_NAME(`subq`) */ min(`test`.`t1`.`a`) from `test`.`t1` group by `test`.`t1`.`a` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`min(a)`)))) +SET optimizer_switch='materialization=on'; +# This query will now use In-to_exists +EXPLAIN EXTENDED +SELECT a, a IN (SELECT a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a`,<`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) AS `a IN (SELECT a FROM t1)` from `test`.`t2` +Force Materialization +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ a, +a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1) FROM t2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` MATERIALIZATION) */ `test`.`t2`.`a` AS `a`,<`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select /*+ QB_NAME(`subq`) */ `test`.`t1`.`a` from `test`.`t1` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`a`)))) AS `a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1)` from `test`.`t2` +# Specifying both strategies should give a warning +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION, INTOEXISTS) +SUBQUERY(@subq2 MATERIALIZATION, INTOEXISTS) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +Warnings: +Warning 1064 Optimizer hint syntax error near ', INTOEXISTS) +SUBQUERY(@subq2 MATERIALIZATION, INTOEXISTS) */ * +FROM t3 +WHERE...' at line 2 +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and `test`.`ty`.`a` = `test`.`t3`.`b` +# Non-supported strategies should give warnings +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 FIRSTMATCH) SUBQUERY(@subq2 LOOSESCAN) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) +AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t3 ALL a NULL NULL NULL 4 100.00 Using where +1 PRIMARY tx eq_ref PRIMARY PRIMARY 4 test.t3.a 1 100.00 Using index +1 PRIMARY ty eq_ref PRIMARY PRIMARY 4 test.t3.b 1 100.00 Using index +Warnings: +Warning 1064 Optimizer hint syntax error near 'FIRSTMATCH) SUBQUERY(@subq2 LOOSESCAN) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+...' at line 2 +Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b` from `test`.`t1` `ty` join `test`.`t1` `tx` join `test`.`t3` where `test`.`tx`.`a` = `test`.`t3`.`a` and `test`.`ty`.`a` = `test`.`t3`.`b` +SET optimizer_switch= default; +# Specifying two SUBQUERY for same query block gives warning +# First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) SUBQUERY(INTOEXISTS) */ a +FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`select#2` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select `test`.`t1`.`a` from `test`.`t1` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`a`)))) +# Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) SUBQUERY(MATERIALIZATION) */ a +FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(MATERIALIZATION) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`select#2` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(INTOEXISTS) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(@`subq` MATERIALIZATION) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Specify at different levels, opposite order +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(MATERIALIZATION) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 4 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(@`subq` INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` MATERIALIZATION) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,`test`.`t2`.`a` in ( (/* select#2 */ select /*+ QB_NAME(`subq`) */ `test`.`t1`.`a` from `test`.`t1` ), (`test`.`t2`.`a` in on distinct_key where `test`.`t2`.`a` = ``.`a`)))) +# Specifying combinations of SUBQUERY and SEMIJOIN/NO_SEMIJOIN +# for same query block gives warning +# First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN() is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`select#2` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() SUBQUERY(MATERIALIZATION) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(MATERIALIZATION) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`select#2`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +# Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 4 100.00 Using index +1 PRIMARY t2 ref a a 4 test.t1.a 1 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(@`subq` MATERIALIZATION) is ignored as conflicting/duplicated +Note 1003 select /*+ SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`a` = `test`.`t1`.`a` +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SUBQUERY(@`subq` INTOEXISTS) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ NO_SEMIJOIN(@`subq`) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(@subq INTOEXISTS) */ a FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 index NULL a 4 NULL 6 100.00 Using where; Using index +2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index +Warnings: +Warning 4210 Hint SEMIJOIN(@`subq` FIRSTMATCH) is ignored as conflicting/duplicated +Note 1003 /* select#1 */ select /*+ SUBQUERY(@`subq` INTOEXISTS) */ `test`.`t2`.`a` AS `a` from `test`.`t2` where <`test`.`t2`.`a`>((`test`.`t2`.`a`,(((`test`.`t2`.`a`) in t1 on PRIMARY)))) +DROP TABLE t1,t2,t3; diff --git a/mysql-test/main/opt_hints_subquery.test b/mysql-test/main/opt_hints_subquery.test new file mode 100644 index 0000000000000..9a7d18ddb8ad3 --- /dev/null +++ b/mysql-test/main/opt_hints_subquery.test @@ -0,0 +1,863 @@ +--enable_prepare_warnings +--disable_view_protocol # Since optimizer hints are not supported inside views +CREATE TABLE t1 (a INTEGER NOT NULL, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INTEGER NOT NULL, KEY (a)); +CREATE TABLE t3 (a INTEGER NOT NULL, b INT, KEY (a)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (20,4), (30,5); + +ANALYZE TABLE t1, t2, t3; + +--echo # Parser tests +--echo # Correct hints (no warnings): +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN() */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) NO_SEMIJOIN(@qb1 firstmatch) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) SEMIJOIN( @qb1 firstmatch, dupsweedout ) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ NO_SEMIJOIN( FIRSTMATCH, LOOSESCAN,materialization, DUPSWEEDOUT ) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb2) NO_SEMIJOIN(@qb2 FIRSTMATCH,LOOSESCAN, materialization, DUPSWEEDOUT) */ a FROM t1; + +set optimizer_switch='derived_merge=off'; +--echo # Correct 'cause hints refer to different query blocks: +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN(@qb1) SEMIJOIN(loosescan)*/ a + FROM (SELECT /*+ QB_NAME(qb1)*/ * FROM t2) AS tt; +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN()*/ a + FROM (SELECT /*+ SEMIJOIN(loosescan)*/ * FROM t2) AS tt; + +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY(materialization) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY( INTOEXISTS ) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME (qb1) SUBQUERY(@qb1 materialization) */ a FROM t1; + +--echo # Incorrect hints (warnings) +SELECT /*+ SEMIJOIN(loosescan @qb1) */ a FROM t1; +SELECT /*+ SEMIJOIN(@qb1 @qb2) */ a FROM t1; +SELECT /*+ SEMIJOIN(@qb1 LOOSESCAN,materialization, unknown_strategy) */ a FROM t1; +SELECT /*+ NO_SEMIJOIN(@qb1, @qb2) */ a FROM t1; +SELECT /*+ NO_SEMIJOIN(FIRSTMATCH, ,LOOSESCAN, materialization) */ a FROM t1; +SELECT /*+ NO_SEMIJOIN(FIRSTMATCH, @qb2,LOOSESCAN) */ a FROM t1; +SELECT /*+ SUBQUERY(wrong_strat) */ a FROM t1; +SELECT /*+ SUBQUERY(materialization, intoexists) */ a FROM t1; +SELECT /*+ SUBQUERY(@qb1 materialization) */ a FROM t1; +SELECT /*+ SUBQUERY() */ a FROM t1; + +--echo # Mix of correct and incorrect hints: +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN(firstmatch ) SEMIJOIN(loosescan @qb1) */ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ NO_SEMIJOIN(@qb1, @qb2) SEMIJOIN()*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ NO_SEMIJOIN() NO_SEMIJOIN(FIRSTMATCH, @qb2,LOOSESCAN) */ a FROM t1; + +--echo # Conflicting hints: +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN() SEMIJOIN(dupsweedout) NO_SEMIJOIN(firstmatch)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN(loosescan,materialization) SEMIJOIN(dupsweedout)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ NO_SEMIJOIN(firstmatch,loosescan,materialization) SEMIJOIN() NO_SEMIJOIN()*/ a + FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) SEMIJOIN(loosescan) NO_SEMIJOIN(@qb1 dupsweedout)*/ a + FROM t1; +EXPLAIN EXTENDED SELECT /*+ SEMIJOIN(firstmatch) NO_SEMIJOIN()*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN() SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY(materialization) SUBQUERY(intoexists) SUBQUERY(materialization)*/ a + FROM t1; +EXPLAIN EXTENDED + SELECT /*+ SUBQUERY(materialization) SEMIJOIN(firstmatch) SUBQUERY(intoexists)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) SEMIJOIN(@qb1) SUBQUERY(@qb1 materialization) SUBQUERY(intoexists)*/ a FROM t1; +EXPLAIN EXTENDED + SELECT /*+ QB_NAME(qb1) SUBQUERY(@qb1 materialization) SEMIJOIN(@qb1 firstmatch) SUBQUERY(intoexists)*/ a FROM t1; + +EXPLAIN EXTENDED + SELECT /*+ SEMIJOIN(@qb1) SEMIJOIN(loosescan) NO_SEMIJOIN(@qb1 dupsweedout)*/ a + FROM (SELECT /*+ QB_NAME(qb1)*/ * FROM t2) AS tt; + +DROP TABLE t1, t2 ,t3; +set optimizer_switch=default; +--echo # End of parser tests + +CREATE TABLE t1 (a INTEGER NOT NULL, b INT, PRIMARY KEY (a)); +CREATE TABLE t2 (a INTEGER NOT NULL, KEY (a)); +CREATE TABLE t3 (a INTEGER NOT NULL, b INT, KEY (a)); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30), (4,40); +INSERT INTO t2 VALUES (2), (2), (3), (3), (4), (5); +INSERT INTO t3 VALUES (10,3), (15,3), (20,4), (30,5); + +ANALYZE TABLE t1, t2, t3; + +--echo # This query will normally use Table Pull-out +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT a FROM t1); +--echo # Check that we can disable SEMIJOIN transformation +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1); +--echo # Same with hint in outer query +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); + +--echo # Query with two sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) + AND t3.b IN (SELECT a FROM t1 ty); +--echo # No SEMIJOIN transformation for first subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 tx) + AND t3.b IN (SELECT a FROM t1 ty); +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(`subq1`) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # No SEMIJOIN transformation for latter subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) + AND t3.b IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 ty); +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@`subq2`) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # No SEMIJOIN transformation for any subquery +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ NO_SEMIJOIN() */ a FROM t1 ty); +EXPLAIN +SELECT /*+ NO_SEMIJOIN(@subq1) NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); + +--echo # Query with nested sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # No SEMIJOIN transformation for outer subquery +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # No SEMIJOIN transformation for inner-most subquery +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # No SEMIJOIN transformation at all +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1) NO_SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); + +--echo # This query does not support SEMIJOIN. SEMIJOIN hint is ignored +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); + +--echo # This query will get LooseScan by default +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Let's turn off LooseScan, FirstMatch is then SELECTed +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Let's also turn off FirstMatch, DupsWeedout is then used. +--echo # (StartTemporary, EndTemporary) in the output indicate DupsWeedout usage +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Materialization is used here +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Turn off all strategies, DuplicateWeedout should be used as a fallback +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, MATERIALIZATION, + DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Turn off non-used strategies, nothing should change. Still Loosescan +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); + +--echo # Test same query with SEMIJOIN hint +--echo # Forcing LooseScan, should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Force FirstMatch +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Force Materialization +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Force DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # If LooseScan is among candidates, it will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION, + DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Drop LooseScan from list of strategies, FirstMatch will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); + +--echo For this query LooseScan and Materialization is not applicable +# Warnings "Field or reference of SELECT #n was resolved in SELECT #m" +# are generated during both prepare and execution stages. So disable PS protocol +# to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo # Turn off all applicable strategies. DuplicateWeedout should be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo # Similar with SEMIJOIN hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--enable_ps_protocol + +--echo # Test multiple subqueries. +--echo # Default for this query is Loosecan for first and FirstMatch for latter +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing the default strategy should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing a strategy for one, may change the other due to cost changes +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing same strategy for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Loosescan for both is not possible, ends up with DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Swap strategies compared to default +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Different subsets of strategies for different subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + SEMIJOIN(@subq2 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Vice versa +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) + SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Another combination +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, FIRSTMATCH) + SEMIJOIN(@subq2 LOOSESCAN, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Turn off default +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) + NO_SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Also turn off 2nd choice. Gives DuplicateWeedout over both +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Also turn off DuplicateWeedout. Materialization is only one left. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Force materialization with SEMIJOIN hints instead +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) + SEMIJOIN(@subq2 MATERIALIZATION) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) + SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) + SEMIJOIN(@subq2 LOOSESCAN, FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +--echo # A query with nested subqueries which are joined together +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +--echo # Let's turn off FirtMatch, DuplicateWeedout is then chosen +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +--echo # If we turn off all strategies, DuplicateWeedout should still be used +EXPLAIN +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN, MATERIALIZATION, + DUPSWEEDOUT) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); + +--echo # Test the same query with SEMIJOIN hint +--echo # Force FirstMatch, should not change anything +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +--echo # Force LooseScan, will fall back to DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +--echo # Force DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +--echo # If FirstMatch is among candidates, it will be used +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, FIRSTMATCH, LOOSESCAN, + DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 + WHERE t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)); + +--echo # Test hints with prepared statements +PREPARE stmt1 FROM "EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; + +SET optimizer_switch = default; + +--echo # Tests with non-default optimizer_switch settings + +SET optimizer_switch = 'semijoin=off'; +--echo # No table pull-out for this query +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT a FROM t1); +--echo # This should not change anything +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +--echo # Force semijoin, table pull-out is performed +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +--echo # Setting strategy should still force semijoin +--echo # Strategy is ignored since table pull-out is done +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); + +--echo # Query with two sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT a FROM t1 tx) + AND t3.b IN (SELECT a FROM t1 ty); +--echo # SEMIJOIN transformation for first subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # SEMIJOIN transformation for latter subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # SEMIJOIN transformation for both subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); + +--echo # Query with nested sub-queries +EXPLAIN EXTENDED +SELECT * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # SEMIJOIN transformation for outer subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # SEMIJOIN transformation for inner-most subquery +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +--echo # SEMIJOIN transformation for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1) SEMIJOIN(@subq2) */ * FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); + +--echo Test strategies when some are disabled by optimizer_switch +SET optimizer_switch='semijoin=on'; +SET optimizer_switch='loosescan=off'; +--echo # This query will get LooseScan by default. FirstMatch now +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Let's turn off LooseScan also by hint, FirstMatch is then selected +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Let's also turn off FirstMatch, DupsWeedout is then used. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Turn off DupsWeedout, Materialization is used here +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); + +--echo # Let's force LooseScan back on +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Forcing another strategy +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # If LooseScan is among candidates, it is used even if originally disabled +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION, + DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); + +--echo Disable another strategy +SET optimizer_switch='firstmatch=off'; + +--echo # Turn on FirstMatch, but not LooseScan on with hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Drop all remaining strategies with hint, should use DuplicateWeedout +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); + +--echo # For this query LooseScan and Materialization is not applicable +--echo # Should use DuplicateWeedout since FirstMatch is disabled +# Warnings "Field or reference of SELECT #n was resolved in SELECT #m" +# are generated during both prepare and execution stages. So disable PS protocol +# to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo Turn off all applicable strategies. DuplicateWeedout should still be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo Reverse which strategies are allowed with hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--enable_ps_protocol + +--echo # Default for this query is Loosecan for first and FirstMatch for latter +--echo # Since both strategies are disabled, will now use DuplicateWeedout +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Allowing LooseScan and FirstMatch and optimizer_switch is ignored +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) + SEMIJOIN(@subq2 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing a disabled strategy for one +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing same strategy for both +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Swap strategies compared to default +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH) SEMIJOIN(@subq2 LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Different subsets of strategies for different subqueries +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + SEMIJOIN(@subq2 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Turn off DuplicateWeedout for both. Materialization is left +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 DUPSWEEDOUT) + NO_SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Forcing materialization should have same effect +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION) + SEMIJOIN(@subq2 MATERIALIZATION) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Turn off DuplicateWeedout for first. MatLookup is used for both +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +--echo # Turn off DuplicateWeedout for second. Same effect. +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq2 DUPSWEEDOUT) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +--echo Enable all strategies except DuplicateWeedout +SET optimizer_switch='firstmatch=on,loosescan=on,materialization=on,duplicateweedout=off'; + +--echo # If we turn off all other strategies, DuplicateWeedout will be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH, MATERIALIZATION) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # LooseScan and Materialization is not applicable, FirstMatch is used +# Warnings "Field or reference of SELECT #n was resolved in SELECT #m" +# are generated during both prepare and execution stages. So disable PS protocol +# to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED +SELECT * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo # Turn off all applicable strategies. DuplicateWeedout should be used +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo # Similar with SEMIJOIN hint +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--enable_ps_protocol + +--echo # Disable all strategies +SET optimizer_switch='firstmatch=off,loosescan=off,materialization=off,duplicateweedout=off'; +--echo # DuplicateWeedout is then used +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Turning off extra strategies should not change anything +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN, DUPSWEEDOUT) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # Turning on some strategies should give one of those +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 FIRSTMATCH, MATERIALIZATION) */ * +FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3); +--echo # For this query that cannot use LooseScan or Materialization, +--echo # turning those on will still give DuplicateWeedout +# Warnings "Field or reference of SELECT #n was resolved in SELECT #m" +# are generated during both prepare and execution stages. So disable PS protocol +# to avoid duplication +--disable_ps_protocol +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, MATERIALIZATION) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--echo # Turning on FirstMatch should give FirstMatch +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN, FIRSTMATCH) */ * FROM t1 +WHERE t1.b IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3 WHERE t3.b = t1.a); +--enable_ps_protocol + +SET optimizer_switch = default; + +--echo Test that setting optimizer_switch after prepare will change strategy +PREPARE stmt1 FROM "EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) + NO_SEMIJOIN(@subq2 FIRSTMATCH, LOOSESCAN) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +SET optimizer_switch = 'duplicateweedout=off'; +--echo Will now use materialization +EXECUTE stmt1; +SET optimizer_switch = 'duplicateweedout=on'; +--echo Turn DuplicateWeedout back on +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; + +SET optimizer_switch = default; + +--echo # Specifying two SEMIJOIN/NO_SEMIJOIN for same query block gives warning +--echo # First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() SEMIJOIN() */ a FROM t1); +--echo # Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SEMIJOIN() NO_SEMIJOIN() */ a FROM t1); +--echo # Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +--echo # Specify at different levels, opposite order +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); +--echo # Duplicate hints also gives warning, but hint has effect +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); + +--echo # Multiple subqueries with conflicting hints +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) NO_SEMIJOIN() */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) SEMIJOIN(LOOSESCAN) */ a FROM t2); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq2 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) NO_SEMIJOIN(LOOSESCAN) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) SEMIJOIN(LOOSESCAN) */ a FROM t2); +--echo # Conflicting hints in same hint comment +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) SEMIJOIN(@subq1 FIRSTMATCH) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 LOOSESCAN) NO_SEMIJOIN(@subq1 LOOSESCAN) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); +EXPLAIN EXTENDED +SELECT /*+ NO_SEMIJOIN(@subq1 LOOSESCAN) NO_SEMIJOIN(@subq1 FIRSTMATCH) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +--echo # Non-supported strategies should give warnings +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq1 INTOEXISTS) NO_SEMIJOIN(@subq2 INTOEXISTS) */ * +FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2); + +--echo # SUBQUERY tests +--echo # SUBQUERY should disable SEMIJOIN and use specified subquery strategy +EXPLAIN EXTENDED +SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) */ a FROM t1); +EXPLAIN +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1); +--echo # Query with two subqueries +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 INTOEXISTS) SUBQUERY(@subq2 MATERIALIZATION) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # Query with nested sub-queries +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 INTOEXISTS) SUBQUERY(@subq2 MATERIALIZATION) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION) SUBQUERY(@subq2 INTOEXISTS) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx + WHERE tx.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty)); + +--echo # This query does not support SEMIJOIN. Materialization is default +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +--echo # Use In-to-exists instead +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); + +--echo # For this query In-to-exists is default +EXPLAIN EXTENDED +SELECT a, a IN (SELECT a FROM t1) FROM t2; +--echo # Force Subquery Materialization +EXPLAIN EXTENDED +SELECT a, a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1) FROM t2; +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ a, + a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1) FROM t2; + +--echo # This query does not support Subquery Materialization due to type mismatch +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ sum(b) FROM t1 group by a); +--echo # Trying to force Subquery Materialization will not change anything +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ sum(b) FROM t1 group by a); + +--echo # Test hints with prepared statements +PREPARE stmt1 FROM "EXPLAIN +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION) + SUBQUERY(@subq2 INTOEXISTS) */ * FROM t1 +WHERE t1.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3) + AND t1.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t2)"; +EXECUTE stmt1; +EXECUTE stmt1; +DEALLOCATE PREPARE stmt1; + +--echo # Test optimizer_switch settings with SUBQUERY hint +SET optimizer_switch='materialization=off'; +--echo This query will now use In-to-exist +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); +--echo # Force it to use Materialization +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) */ min(a) FROM t1 group by a); + +SET optimizer_switch='materialization=on'; +--echo # This query will now use In-to_exists +EXPLAIN EXTENDED +SELECT a, a IN (SELECT a FROM t1) FROM t2; +--echo Force Materialization +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ a, + a IN (SELECT /*+ QB_NAME(subq) */ a FROM t1) FROM t2; + +--echo # Specifying both strategies should give a warning +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 MATERIALIZATION, INTOEXISTS) + SUBQUERY(@subq2 MATERIALIZATION, INTOEXISTS) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); +--echo # Non-supported strategies should give warnings +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq1 FIRSTMATCH) SUBQUERY(@subq2 LOOSESCAN) */ * +FROM t3 +WHERE t3.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t1 tx) + AND t3.b IN (SELECT /*+ QB_NAME(subq2) */ a FROM t1 ty); + +SET optimizer_switch= default; + +--echo # Specifying two SUBQUERY for same query block gives warning +--echo # First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) SUBQUERY(INTOEXISTS) */ a +FROM t1); +--echo # Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) SUBQUERY(MATERIALIZATION) */ a +FROM t1); +--echo # Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(INTOEXISTS) */ a FROM t1); +--echo # Specify at different levels, opposite order +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(MATERIALIZATION) */ a FROM t1); + +--echo # Specifying combinations of SUBQUERY and SEMIJOIN/NO_SEMIJOIN +--echo # for same query block gives warning +--echo # First has effect, second is ignored +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) SEMIJOIN() */ a FROM t1); +--echo # Try opposite order +EXPLAIN EXTENDED +SELECT * FROM t2 +WHERE t2.a IN (SELECT /*+ NO_SEMIJOIN() SUBQUERY(MATERIALIZATION) */ a FROM t1); +--echo # Specify at different levels, hint inside block has effect +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq MATERIALIZATION) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SEMIJOIN() */ a FROM t1); +EXPLAIN EXTENDED +SELECT /*+ SUBQUERY(@subq INTOEXISTS) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) NO_SEMIJOIN() */ a FROM t1); +EXPLAIN EXTENDED +SELECT /*+ SEMIJOIN(@subq FIRSTMATCH) */ * FROM t2 +WHERE t2.a IN (SELECT /*+ QB_NAME(subq) SUBQUERY(@subq INTOEXISTS) */ a FROM t1); + +DROP TABLE t1,t2,t3; + diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index e3099480f33d0..387ae180369bf 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -88,16 +88,6 @@ select * from v1 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -124,6 +114,16 @@ select * from v1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -259,16 +259,6 @@ select * from (select * from t1 where t1.a=1)q { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -295,6 +285,16 @@ select * from (select * from t1 where t1.a=1)q { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -435,16 +435,6 @@ select * from v2 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -471,6 +461,16 @@ select * from v2 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -567,16 +567,6 @@ select * from v2 { ] } }, - { - "table_dependencies": [ - { - "table": "", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -589,6 +579,16 @@ select * from v2 { } ] }, + { + "table_dependencies": [ + { + "table": "", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -724,16 +724,6 @@ explain select * from v2 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t2", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -746,6 +736,16 @@ explain select * from v2 { } ] }, + { + "table_dependencies": [ + { + "table": "t2", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -860,16 +860,6 @@ explain select * from v1 { "join_optimization": { "select_id": 2, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -882,6 +872,16 @@ explain select * from v1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -973,16 +973,6 @@ explain select * from v1 { ] } }, - { - "table_dependencies": [ - { - "table": "", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -995,6 +985,16 @@ explain select * from v1 { } ] }, + { + "table_dependencies": [ + { + "table": "", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -1134,22 +1134,6 @@ explain select * from t1,t2 where t1.a=t2.b+2 and t2.a= t1.b { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t2", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -1188,6 +1172,22 @@ explain select * from t1,t2 where t1.a=t2.b+2 and t2.a= t1.b { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t2", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -1433,16 +1433,6 @@ EXPLAIN SELECT DISTINCT a FROM t1 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -1514,6 +1504,16 @@ EXPLAIN SELECT DISTINCT a FROM t1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -1641,16 +1641,6 @@ set statement optimizer_scan_setup_cost=0 for EXPLAIN SELECT MIN(d) FROM t1 wher ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -1729,6 +1719,16 @@ set statement optimizer_scan_setup_cost=0 for EXPLAIN SELECT MIN(d) FROM t1 wher } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -1895,16 +1895,6 @@ EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104e0 GROUP BY id { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -1982,6 +1972,16 @@ EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104e0 GROUP BY id { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -2116,16 +2116,6 @@ EXPLAIN SELECT * FROM t1 WHERE a = 20010104e0 GROUP BY id { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -2203,6 +2193,16 @@ EXPLAIN SELECT * FROM t1 WHERE a = 20010104e0 GROUP BY id { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -2351,16 +2351,6 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -2497,6 +2487,16 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -2738,22 +2738,6 @@ select t1.a from t1 left join t2 on t1.a=t2.a { "resulting_condition": "multiple equal(t1.a, t2.a)" } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t2", - "row_may_be_null": true, - "map_bit": 1, - "depends_on_map_bits": ["0"] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -2786,6 +2770,22 @@ select t1.a from t1 left join t2 on t1.a=t2.a { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t2", + "row_may_be_null": true, + "map_bit": 1, + "depends_on_map_bits": ["0"] + } + ] + }, { "considered_execution_plans": [ { @@ -2898,22 +2898,6 @@ explain select * from t1 left join t2 on t2.a=t1.a { "resulting_condition": "multiple equal(t2.a, t1.a)" } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t2", - "row_may_be_null": true, - "map_bit": 1, - "depends_on_map_bits": ["0"] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -2948,6 +2932,22 @@ explain select * from t1 left join t2 on t2.a=t1.a { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t2", + "row_may_be_null": true, + "map_bit": 1, + "depends_on_map_bits": ["0"] + } + ] + }, { "considered_execution_plans": [ { @@ -3112,28 +3112,6 @@ explain select t1.a from t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and "resulting_condition": "multiple equal(t2.a, t1.a, t3.a) and multiple equal(t2.b, t3.b)" } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t2", - "row_may_be_null": true, - "map_bit": 1, - "depends_on_map_bits": ["0"] - }, - { - "table": "t3", - "row_may_be_null": true, - "map_bit": 2, - "depends_on_map_bits": ["0"] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -3193,6 +3171,28 @@ explain select t1.a from t1 left join (t2 join t3 on t2.b=t3.b) on t2.a=t1.a and } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t2", + "row_may_be_null": true, + "map_bit": 1, + "depends_on_map_bits": ["0"] + }, + { + "table": "t3", + "row_may_be_null": true, + "map_bit": 2, + "depends_on_map_bits": ["0"] + } + ] + }, { "considered_execution_plans": [ { @@ -3357,34 +3357,18 @@ explain extended select * from t1 where a in (select p from t2) { "steps": [ { "transformation": "equality_propagation", - "resulting_condition": "1 and multiple equal(t1.a, t2.p)" - }, - { - "transformation": "constant_propagation", - "resulting_condition": "1 and multiple equal(t1.a, t2.p)" - }, - { - "transformation": "trivial_condition_removal", - "resulting_condition": "multiple equal(t1.a, t2.p)" - } - ] - } - }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t2", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - } - ] + "resulting_condition": "1 and multiple equal(t1.a, t2.p)" + }, + { + "transformation": "constant_propagation", + "resulting_condition": "1 and multiple equal(t1.a, t2.p)" + }, + { + "transformation": "trivial_condition_removal", + "resulting_condition": "multiple equal(t1.a, t2.p)" + } + ] + } }, { "ref_optimizer_key_uses": [] @@ -3414,6 +3398,22 @@ explain extended select * from t1 where a in (select p from t2) { "pulled_out_tables": [] } }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t2", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + } + ] + }, { "execution_plan_for_potential_materialization": { "steps": [ @@ -3720,16 +3720,6 @@ explain select * from t1 where pk = 2 and a=5 and b=1 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -3935,6 +3925,16 @@ explain select * from t1 where pk = 2 and a=5 and b=1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -4081,16 +4081,6 @@ select f1(a) from t1 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -4103,6 +4093,16 @@ select f1(a) from t1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -4198,16 +4198,6 @@ select f2(a) from t1 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -4220,6 +4210,16 @@ select f2(a) from t1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -4464,22 +4464,6 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { ] } }, - { - "table_dependencies": [ - { - "table": "t0", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -4652,6 +4636,22 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 { } ] }, + { + "table_dependencies": [ + { + "table": "t0", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -4908,16 +4908,6 @@ explain select * from (select rand() from t1)q { "join_optimization": { "select_id": 2, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -4930,6 +4920,16 @@ explain select * from (select rand() from t1)q { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -4993,16 +4993,6 @@ explain select * from (select rand() from t1)q { ] } }, - { - "table_dependencies": [ - { - "table": "", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -5015,6 +5005,16 @@ explain select * from (select rand() from t1)q { } ] }, + { + "table_dependencies": [ + { + "table": "", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -5183,28 +5183,6 @@ explain select * from t1 where a in (select t_inner_1.a from t1 t_inner_1, t1 t_ ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_1", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_2", - "row_may_be_null": false, - "map_bit": 2, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -5241,6 +5219,28 @@ explain select * from t1 where a in (select t_inner_1.a from t1 t_inner_1, t1 t_ "pulled_out_tables": [] } }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_1", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_2", + "row_may_be_null": false, + "map_bit": 2, + "depends_on_map_bits": [] + } + ] + }, { "execution_plan_for_potential_materialization": { "steps": [ @@ -5791,58 +5791,18 @@ t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4) { "steps": [ { "transformation": "equality_propagation", - "resulting_condition": "1 and 1 and multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" - }, - { - "transformation": "constant_propagation", - "resulting_condition": "1 and 1 and multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" - }, - { - "transformation": "trivial_condition_removal", - "resulting_condition": "multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" - } - ] - } - }, - { - "table_dependencies": [ - { - "table": "t_outer_1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t_outer_2", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_2", - "row_may_be_null": false, - "map_bit": 2, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_1", - "row_may_be_null": false, - "map_bit": 3, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_3", - "row_may_be_null": false, - "map_bit": 4, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_4", - "row_may_be_null": false, - "map_bit": 5, - "depends_on_map_bits": [] - } - ] + "resulting_condition": "1 and 1 and multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" + }, + { + "transformation": "constant_propagation", + "resulting_condition": "1 and 1 and multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" + }, + { + "transformation": "trivial_condition_removal", + "resulting_condition": "multiple equal(t_outer_1.a, t_inner_1.a) and multiple equal(t_outer_2.a, t_inner_3.a)" + } + ] + } }, { "ref_optimizer_key_uses": [] @@ -5909,6 +5869,46 @@ t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4) { "pulled_out_tables": [] } }, + { + "table_dependencies": [ + { + "table": "t_outer_1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t_outer_2", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_2", + "row_may_be_null": false, + "map_bit": 2, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_1", + "row_may_be_null": false, + "map_bit": 3, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_3", + "row_may_be_null": false, + "map_bit": 4, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_4", + "row_may_be_null": false, + "map_bit": 5, + "depends_on_map_bits": [] + } + ] + }, { "execution_plan_for_potential_materialization": { "steps": [] @@ -8212,46 +8212,6 @@ t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4) { ] } }, - { - "table_dependencies": [ - { - "table": "t_outer_1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t_outer_2", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_2", - "row_may_be_null": false, - "map_bit": 2, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_1", - "row_may_be_null": false, - "map_bit": 3, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_3", - "row_may_be_null": false, - "map_bit": 4, - "depends_on_map_bits": [] - }, - { - "table": "t_inner_4", - "row_may_be_null": false, - "map_bit": 5, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -8317,6 +8277,46 @@ t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4) { "pulled_out_tables": [] } }, + { + "table_dependencies": [ + { + "table": "t_outer_1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t_outer_2", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_2", + "row_may_be_null": false, + "map_bit": 2, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_1", + "row_may_be_null": false, + "map_bit": 3, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_3", + "row_may_be_null": false, + "map_bit": 4, + "depends_on_map_bits": [] + }, + { + "table": "t_inner_4", + "row_may_be_null": false, + "map_bit": 5, + "depends_on_map_bits": [] + } + ] + }, { "execution_plan_for_potential_materialization": { "steps": [ @@ -11902,22 +11902,6 @@ UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a { "steps": [] } }, - { - "table_dependencies": [ - { - "table": "t", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -11941,6 +11925,22 @@ UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a { } ] }, + { + "table_dependencies": [ + { + "table": "t", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -12149,16 +12149,6 @@ UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a { "join_optimization": { "select_id": "fake", "steps": [ - { - "table_dependencies": [ - { - "table": "union", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -12171,6 +12161,16 @@ UPDATE t, v SET t.b = t.a, t.a = v.c WHERE v.c < t.a { } ] }, + { + "table_dependencies": [ + { + "table": "union", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -12409,16 +12409,6 @@ select count(*) from seq_1_to_10000000 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "seq_1_to_10000000", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -12431,6 +12421,16 @@ select count(*) from seq_1_to_10000000 { } ] }, + { + "table_dependencies": [ + { + "table": "seq_1_to_10000000", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -13043,16 +13043,6 @@ explain select * from t1 where a<10 and b between 10 and 50 and c < 10 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -13161,6 +13151,16 @@ explain select * from t1 where a<10 and b between 10 and 50 and c < 10 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -13438,22 +13438,6 @@ explain format=json select * from three, t1 where t1.a=three.a and t1.b<5000 and ] } }, - { - "table_dependencies": [ - { - "table": "three", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - }, - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 1, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -13548,6 +13532,22 @@ explain format=json select * from three, t1 where t1.a=three.a and t1.b<5000 and } ] }, + { + "table_dependencies": [ + { + "table": "three", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + }, + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 1, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { diff --git a/mysql-test/main/opt_trace_index_merge.result b/mysql-test/main/opt_trace_index_merge.result index 70db13798a74d..2ad39304ebe69 100644 --- a/mysql-test/main/opt_trace_index_merge.result +++ b/mysql-test/main/opt_trace_index_merge.result @@ -53,16 +53,6 @@ explain select * from t1 where a=1 or b=1 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [] }, @@ -205,6 +195,16 @@ explain select * from t1 where a=1 or b=1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { diff --git a/mysql-test/main/opt_trace_index_merge_innodb.result b/mysql-test/main/opt_trace_index_merge_innodb.result index 02509aa961089..e5263f7bbd7e7 100644 --- a/mysql-test/main/opt_trace_index_merge_innodb.result +++ b/mysql-test/main/opt_trace_index_merge_innodb.result @@ -61,16 +61,6 @@ explain select * from t1 where pk1 != 0 and key1 = 1 { ] } }, - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "ref_optimizer_key_uses": [ { @@ -199,6 +189,16 @@ explain select * from t1 where pk1 != 0 and key1 = 1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { diff --git a/mysql-test/main/opt_trace_security.result b/mysql-test/main/opt_trace_security.result index cf5dcf5d886b2..8b483cf458b6f 100644 --- a/mysql-test/main/opt_trace_security.result +++ b/mysql-test/main/opt_trace_security.result @@ -64,16 +64,6 @@ select * from db1.t1 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -86,6 +76,16 @@ select * from db1.t1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { @@ -206,16 +206,6 @@ select * from db1.v1 { "join_optimization": { "select_id": 1, "steps": [ - { - "table_dependencies": [ - { - "table": "t1", - "row_may_be_null": false, - "map_bit": 0, - "depends_on_map_bits": [] - } - ] - }, { "rows_estimation": [ { @@ -228,6 +218,16 @@ select * from db1.v1 { } ] }, + { + "table_dependencies": [ + { + "table": "t1", + "row_may_be_null": false, + "map_bit": 0, + "depends_on_map_bits": [] + } + ] + }, { "considered_execution_plans": [ { diff --git a/mysql-test/main/rename.result b/mysql-test/main/rename.result index 43713dc61279a..cd617ca25558b 100644 --- a/mysql-test/main/rename.result +++ b/mysql-test/main/rename.result @@ -175,3 +175,19 @@ drop table t2; rename table if exists t1 to t2; alter table if exists t2 rename to t1; drop table t1; +# +# MDEV-36484 Atomic DDL: Assertion `!param->got_error' failed upon +# unsuccessful multi-RENAME TABLE +# +CREATE TABLE t1 (a INT); +CREATE TRIGGER t1 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t1 VALUES (0); +RENAME TABLE t1 TO t2, t3 TO t4; +ERROR 42S02: Table 'test.t3' doesn't exist +SHOW CREATE TRIGGER t1; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created +t1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1 AFTER INSERT ON `t1` FOR EACH ROW INSERT INTO t1 VALUES (0) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci # +RENAME TABLE t1 TO t2; +SHOW CREATE TRIGGER t1; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created +t1 STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`root`@`localhost` TRIGGER t1 AFTER INSERT ON `t2` FOR EACH ROW INSERT INTO t1 VALUES (0) latin1 latin1_swedish_ci utf8mb4_uca1400_ai_ci # +DROP TABLE t2; diff --git a/mysql-test/main/rename.test b/mysql-test/main/rename.test index a0b9f38ae2ea6..81c7e084672ee 100644 --- a/mysql-test/main/rename.test +++ b/mysql-test/main/rename.test @@ -166,3 +166,21 @@ drop table t2; rename table if exists t1 to t2; alter table if exists t2 rename to t1; drop table t1; + + +--echo # +--echo # MDEV-36484 Atomic DDL: Assertion `!param->got_error' failed upon +--echo # unsuccessful multi-RENAME TABLE +--echo # +CREATE TABLE t1 (a INT); +CREATE TRIGGER t1 AFTER INSERT ON t1 FOR EACH ROW INSERT INTO t1 VALUES (0); +--error ER_NO_SUCH_TABLE +RENAME TABLE t1 TO t2, t3 TO t4; + +--replace_column 7 # +SHOW CREATE TRIGGER t1; +RENAME TABLE t1 TO t2; + +--replace_column 7 # +SHOW CREATE TRIGGER t1; +DROP TABLE t2; diff --git a/mysql-test/suite/gcol/inc/gcol_select.inc b/mysql-test/suite/gcol/inc/gcol_select.inc index 72fe76d5f25c0..f0d702db012f2 100644 --- a/mysql-test/suite/gcol/inc/gcol_select.inc +++ b/mysql-test/suite/gcol/inc/gcol_select.inc @@ -895,7 +895,7 @@ CREATE TABLE t ( INSERT INTO t(a,b) VALUES(1,'cccc'); let $query= -SELECT /*+ bka() */ 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c +SELECT 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c WHERE b.b>c.a; eval EXPLAIN $query; eval $query; @@ -1022,13 +1022,12 @@ VALUES (1, 'j'), (2, 'c'), (0, 'a'); ANALYZE TABLE t1, t2, t3, t4; -# Hint is added to avoid materialization of the subquery let query= -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( - SELECT /*+ QB_NAME(subq1) */ t4.i1 + SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1049,13 +1048,12 @@ if ($support_virtual_index) ALTER TABLE t3 ADD INDEX v_idx2 (i2_key, i1); } -# Hint is added to avoid materialization of the subquery let query= -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( - SELECT /*+ QB_NAME(subq1) */ t4.i1 + SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1079,11 +1077,11 @@ ALTER TABLE t3 DROP INDEX v_idx; # Hint is added to avoid materialization of the subquery let query= -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( - SELECT /*+ QB_NAME(subq1) */ t4.i1 + SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1117,11 +1115,11 @@ VALUES (10,1), (11,1), (12,1), (13,1), (14,1),(15,1), (16,1),(17,1), (18,1), --echo # non-covering. # Hint is added to avoid materialization of the subquery let query= -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1, t3.i1 +SELECT t1.c1, t2.i1, t3.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( - SELECT /*+ QB_NAME(subq1) */ t4.i1 + SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1154,11 +1152,11 @@ eval $query; # an extra query condition is added to the subquery. # Hint is added to avoid materialization of the subquery let query= -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( - SELECT /*+ QB_NAME(subq1) */ t4.i1 + SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' and t4.i1 < (t2.i1 + 1) ) diff --git a/mysql-test/suite/gcol/r/gcol_bugfixes.result b/mysql-test/suite/gcol/r/gcol_bugfixes.result index 21b6a71ea845f..c790658ccaaad 100644 --- a/mysql-test/suite/gcol/r/gcol_bugfixes.result +++ b/mysql-test/suite/gcol/r/gcol_bugfixes.result @@ -444,7 +444,7 @@ Note 1265 Data truncated for column 'c13' at row 1 Note 1265 Data truncated for column 'c11' at row 2 Note 1265 Data truncated for column 'c13' at row 2 CREATE VIEW view_C AS SELECT * FROM C; -SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13); +SELECT t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13); c13 00:00:00 00:00:00 diff --git a/mysql-test/suite/gcol/r/gcol_select_innodb.result b/mysql-test/suite/gcol/r/gcol_select_innodb.result index fb79c79032cfa..5c12c139a4249 100644 --- a/mysql-test/suite/gcol/r/gcol_select_innodb.result +++ b/mysql-test/suite/gcol/r/gcol_select_innodb.result @@ -555,12 +555,12 @@ c BLOB GENERATED ALWAYS AS (a+b) VIRTUAL, UNIQUE KEY i0008 (a) ); INSERT INTO t(a,b) VALUES(1,'cccc'); -EXPLAIN SELECT /*+ bka() */ 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c +EXPLAIN SELECT 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c WHERE b.b>c.a; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE b ALL i0008 NULL NULL NULL 1 1 SIMPLE c ALL i0008 NULL NULL NULL 1 Range checked for each record (index map: 0x1) -SELECT /*+ bka() */ 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c +SELECT 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c WHERE b.b>c.a; c Warnings: @@ -689,11 +689,11 @@ test.t3 analyze status Engine-independent statistics collected test.t3 analyze status OK test.t4 analyze status Engine-independent statistics collected test.t4 analyze status OK -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -704,11 +704,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -743,11 +743,11 @@ t 9 # # Test 2: Two alternative covering indexes for the range scan # -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -758,11 +758,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -798,11 +798,11 @@ t 9 # Test 3: One covering index including the base column for the virtual # column # -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -813,11 +813,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -861,11 +861,11 @@ VALUES (10,1), (11,1), (12,1), (13,1), (14,1),(15,1), (16,1),(17,1), (18,1), (28,1), (29,1); # Change the query to read an extra column (t3.i1) making the index # non-covering. -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1, t3.i1 +EXPLAIN SELECT t1.c1, t2.i1, t3.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -876,11 +876,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1, t3.i1 +SELECT t1.c1, t2.i1, t3.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -916,11 +916,11 @@ t 9 48 # Test 5: Test where the added primary key to secondary indexes is # used after it has been included in the join buffer # -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' and t4.i1 < (t2.i1 + 1) ) @@ -931,11 +931,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY PRIMARY 4 test.t4.i1 1 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; End temporary; Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' and t4.i1 < (t2.i1 + 1) ) diff --git a/mysql-test/suite/gcol/r/gcol_select_myisam.result b/mysql-test/suite/gcol/r/gcol_select_myisam.result index 534a62cfc0d41..20a8b1764c19a 100644 --- a/mysql-test/suite/gcol/r/gcol_select_myisam.result +++ b/mysql-test/suite/gcol/r/gcol_select_myisam.result @@ -1174,14 +1174,14 @@ c BLOB GENERATED ALWAYS AS (a+b) VIRTUAL, UNIQUE KEY i0008 (a) ); INSERT INTO t(a,b) VALUES(1,'cccc'); -EXPLAIN SELECT /*+ bka() */ 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c +EXPLAIN SELECT 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c WHERE b.b>c.a; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: Warning 1292 Truncated incorrect DOUBLE value: 'cccc' Warning 1292 Truncated incorrect DECIMAL value: 'cccc' -SELECT /*+ bka() */ 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c +SELECT 1 AS c FROM t AS b RIGHT JOIN t AS c ON b.a > c.c WHERE b.b>c.a; c Warnings: @@ -1315,11 +1315,11 @@ test.t3 analyze status Engine-independent statistics collected test.t3 analyze status OK test.t4 analyze status Engine-independent statistics collected test.t4 analyze status OK -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1330,11 +1330,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY,v_idx PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1370,11 +1370,11 @@ t 9 # Test 2: Two alternative covering indexes for the range scan # ALTER TABLE t3 ADD INDEX v_idx2 (i2_key, i1); -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1385,11 +1385,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY,v_idx,v_idx2 PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1427,11 +1427,11 @@ t 9 # # Drop the index with only the virtual column ALTER TABLE t3 DROP INDEX v_idx; -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1442,11 +1442,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY,v_idx2 PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1493,11 +1493,11 @@ VALUES (10,1), (11,1), (12,1), (13,1), (14,1),(15,1), (16,1),(17,1), (18,1), (28,1), (29,1); # Change the query to read an extra column (t3.i1) making the index # non-covering. -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1, t3.i1 +EXPLAIN SELECT t1.c1, t2.i1, t3.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1508,11 +1508,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY,v_idx PRIMARY 4 test.t4.i1 1 Using where; End temporary 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1, t3.i1 +SELECT t1.c1, t2.i1, t3.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' ) @@ -1548,11 +1548,11 @@ t 9 48 # Test 5: Test where the added primary key to secondary indexes is # used after it has been included in the join buffer # -EXPLAIN SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +EXPLAIN SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' and t4.i1 < (t2.i1 + 1) ) @@ -1563,11 +1563,11 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using join buffer (flat, BNL join) 1 PRIMARY t3 eq_ref PRIMARY,v_idx PRIMARY 4 test.t4.i1 1 Using where 1 PRIMARY t2 ALL NULL NULL NULL NULL 5 Using where; End temporary; Using join buffer (flat, BNL join) -SELECT /*+ NO_SEMIJOIN(@subq1) */ t1.c1, t2.i1 +SELECT t1.c1, t2.i1 FROM t1 STRAIGHT_JOIN t3 STRAIGHT_JOIN t2 WHERE ( t3.pk IN ( -SELECT /*+ QB_NAME(subq1) */ t4.i1 +SELECT t4.i1 FROM t4 WHERE t4.c1 < 'o' and t4.i1 < (t2.i1 + 1) ) diff --git a/mysql-test/suite/gcol/t/gcol_bugfixes.test b/mysql-test/suite/gcol/t/gcol_bugfixes.test index c4beb69058a52..4ff3f7247a77c 100644 --- a/mysql-test/suite/gcol/t/gcol_bugfixes.test +++ b/mysql-test/suite/gcol/t/gcol_bugfixes.test @@ -429,7 +429,7 @@ INSERT INTO C (c8,c9) VALUES('1970-01-01',0),('1970-01-01',1); CREATE VIEW view_C AS SELECT * FROM C; -SELECT /*+ NO_BNL(t1) */ t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13); +SELECT t1.c13 FROM C AS t2 STRAIGHT_JOIN C AS t1 FORCE INDEX(c13); SELECT DISTINCT t1.c13 FROM C AS t1, view_C AS t2; DROP TABLE C; diff --git a/mysql-test/suite/json/r/json_table_mysql.result b/mysql-test/suite/json/r/json_table_mysql.result index b9f39dbeb3cb9..a1e806c765d49 100644 --- a/mysql-test/suite/json/r/json_table_mysql.result +++ b/mysql-test/suite/json/r/json_table_mysql.result @@ -469,7 +469,7 @@ id jid val 2 1 2 2 2 4 2 3 6 -SELECT /*+ JOIN_ORDER(jt, t1) */ id, jt.* +SELECT id, jt.* FROM t1, JSON_TABLE(jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt; @@ -480,14 +480,14 @@ id jid val 2 1 2 2 2 4 2 3 6 -EXPLAIN SELECT /*+ JOIN_ORDER(jt, t1) */ id, jt.* +EXPLAIN SELECT id, jt.* FROM t1, JSON_TABLE(jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 1 SIMPLE jt ALL NULL NULL NULL NULL 40 Table function: json_table -SELECT /*+ JOIN_ORDER(t2,jt) */ t1.id, t2.id, jt.* +SELECT t1.id, t2.id, jt.* FROM t1, JSON_TABLE(t1.jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt, @@ -514,7 +514,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using join buffer (flat, BNL join) 1 SIMPLE jt ALL NULL NULL NULL NULL 40 Table function: json_table -EXPLAIN SELECT /*+ JOIN_ORDER(t2,jt) */ t1.id, t2.id, jt.* +EXPLAIN SELECT t1.id, t2.id, jt.* FROM t1, JSON_TABLE(t1.jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt, diff --git a/mysql-test/suite/json/t/json_table_mysql.test b/mysql-test/suite/json/t/json_table_mysql.test index 1c970eab2d4a6..6ef834f3e6e92 100644 --- a/mysql-test/suite/json/t/json_table_mysql.test +++ b/mysql-test/suite/json/t/json_table_mysql.test @@ -379,18 +379,18 @@ INSERT INTO t1 values (1, '[1,3,5]'),(2,'[2,4,6]'); SELECT id, jt.* FROM t1, JSON_TABLE(jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt; -SELECT /*+ JOIN_ORDER(jt, t1) */ id, jt.* +SELECT id, jt.* FROM t1, JSON_TABLE(jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt; -EXPLAIN SELECT /*+ JOIN_ORDER(jt, t1) */ id, jt.* +EXPLAIN SELECT id, jt.* FROM t1, JSON_TABLE(jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt; --sorted_result -SELECT /*+ JOIN_ORDER(t2,jt) */ t1.id, t2.id, jt.* +SELECT t1.id, t2.id, jt.* FROM t1, JSON_TABLE(t1.jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt, @@ -402,7 +402,7 @@ EXPLAIN SELECT t1.id, t2.id, jt.* val INT PATH '$')) AS jt, t1 AS t2; -EXPLAIN SELECT /*+ JOIN_ORDER(t2,jt) */ t1.id, t2.id, jt.* +EXPLAIN SELECT t1.id, t2.id, jt.* FROM t1, JSON_TABLE(t1.jd, '$[*]' COLUMNS (jid FOR ORDINALITY, val INT PATH '$')) AS jt, diff --git a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result index 7d735f95b50df..05aed97b6e8f5 100644 --- a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result +++ b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result @@ -1,58 +1,58 @@ set @@global.optimizer_switch=@@optimizer_switch; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on select @@session.optimizer_switch; @@session.optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on show global variables like 'optimizer_switch'; Variable_name Value -optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on show session variables like 'optimizer_switch'; Variable_name Value -optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on select * from information_schema.global_variables where variable_name='optimizer_switch'; VARIABLE_NAME VARIABLE_VALUE -OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on select * from information_schema.session_variables where variable_name='optimizer_switch'; VARIABLE_NAME VARIABLE_VALUE -OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on -set global optimizer_switch=2053; -set session optimizer_switch=1034; +OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=off,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +set global optimizer_switch=4101; +set session optimizer_switch=2058; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off select @@session.optimizer_switch; @@session.optimizer_switch -index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off set global optimizer_switch="index_merge_sort_union=on"; set session optimizer_switch="index_merge=off"; select @@global.optimizer_switch; @@global.optimizer_switch -index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off select @@session.optimizer_switch; @@session.optimizer_switch -index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off show global variables like 'optimizer_switch'; Variable_name Value -optimizer_switch index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +optimizer_switch index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off show session variables like 'optimizer_switch'; Variable_name Value -optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off select * from information_schema.global_variables where variable_name='optimizer_switch'; VARIABLE_NAME VARIABLE_VALUE -OPTIMIZER_SWITCH index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +OPTIMIZER_SWITCH index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off select * from information_schema.session_variables where variable_name='optimizer_switch'; VARIABLE_NAME VARIABLE_VALUE -OPTIMIZER_SWITCH index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +OPTIMIZER_SWITCH index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=on,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off set session optimizer_switch="default"; select @@session.optimizer_switch; @@session.optimizer_switch -index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off +index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,duplicateweedout=off,materialization=off,in_to_exists=on,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,condition_pushdown_for_subquery=off,rowid_filter=off,condition_pushdown_from_having=off,not_null_range_scan=off,hash_join_cardinality=off,cset_narrowing=off,sargable_casefold=off set optimizer_switch = replace(@@optimizer_switch, '=off', '=on'); select @@optimizer_switch; @@optimizer_switch -index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=on,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=on,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on +index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=on,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,duplicateweedout=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,condition_pushdown_for_subquery=on,rowid_filter=on,condition_pushdown_from_having=on,not_null_range_scan=on,hash_join_cardinality=on,cset_narrowing=on,sargable_casefold=on set global optimizer_switch=1.1; ERROR 42000: Incorrect argument type to variable 'optimizer_switch' set global optimizer_switch=1e1; diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 2c1da015576ee..16c355c7f36b4 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2649,7 +2649,7 @@ VARIABLE_COMMENT Fine-tune the optimizer behavior NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,condition_pushdown_for_subquery,rowid_filter,condition_pushdown_from_having,not_null_range_scan,hash_join_cardinality,cset_narrowing,sargable_casefold,default +ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,duplicateweedout,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,condition_pushdown_for_subquery,rowid_filter,condition_pushdown_from_having,not_null_range_scan,hash_join_cardinality,cset_narrowing,sargable_casefold,default READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_TRACE diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 00f5095a49df2..fba0e439252e3 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -2859,7 +2859,7 @@ VARIABLE_COMMENT Fine-tune the optimizer behavior NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,condition_pushdown_for_subquery,rowid_filter,condition_pushdown_from_having,not_null_range_scan,hash_join_cardinality,cset_narrowing,sargable_casefold,default +ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,duplicateweedout,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,condition_pushdown_for_subquery,rowid_filter,condition_pushdown_from_having,not_null_range_scan,hash_join_cardinality,cset_narrowing,sargable_casefold,default READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_TRACE diff --git a/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test b/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test index a5162075e35fb..ada22845f3e25 100644 --- a/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test +++ b/mysql-test/suite/sys_vars/t/optimizer_switch_basic.test @@ -19,8 +19,8 @@ select * from information_schema.session_variables where variable_name='optimize # # show that it's writable # -set global optimizer_switch=2053; -set session optimizer_switch=1034; +set global optimizer_switch=4101; +set session optimizer_switch=2058; select @@global.optimizer_switch; select @@session.optimizer_switch; set global optimizer_switch="index_merge_sort_union=on"; diff --git a/mysql-test/suite/sysschema/r/optimizer_switch.result b/mysql-test/suite/sysschema/r/optimizer_switch.result index ceabebf0f2e56..29a8e11c48bcf 100644 --- a/mysql-test/suite/sysschema/r/optimizer_switch.result +++ b/mysql-test/suite/sysschema/r/optimizer_switch.result @@ -6,6 +6,7 @@ condition_pushdown_from_having on cset_narrowing on derived_merge on derived_with_keys on +duplicateweedout on exists_to_in on extended_keys on firstmatch on diff --git a/mysys/my_error.c b/mysys/my_error.c index 106e51de581e8..0f5ae74e4594e 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */ #define ERRMSGSIZE (512) @@ -93,6 +94,62 @@ const char *my_get_err_msg(uint nr) return format; } +/** + Internal method used to format and prepare the error structure. + Called by my_error_prepare() and my_error(). + */ +static struct Prepared_error my_error_format(uint nr, myf MyFlags, va_list args) +{ + const char *format; + struct Prepared_error error; + error.code= nr; + error.flags= MyFlags; + + if (!(format = my_get_err_msg(nr))) + (void) my_snprintf(error.message, sizeof(error.message), + "Unknown error %d", nr); + else + { + (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, + error.message, sizeof(error.message), format, args); + } + return error; +} + +/** + Prepare the error message for deferred printing with my_error_issue() + + @param nr error number + @param MyFlags Flags + @param ... variable list matching that error format string + + @retval error prepared structure containg error code, formatted message + and flags +*/ + +struct Prepared_error my_error_prepare(uint nr, myf MyFlags, ...) +{ + struct Prepared_error error; + va_list args; + + va_start(args,MyFlags); + error= my_error_format(nr, MyFlags, args); + va_end(args); + + return error; +} + + +/** + Print a previously prepared error (see my_error_prepare()) + + * @param error error structure prepared for deferred printing@retval + */ +void my_error_issue(struct Prepared_error* error) +{ + (*error_handler_hook)(error->code, error->message, error->flags); +} + /** Fill in and print a previously registered error message. @@ -107,21 +164,14 @@ const char *my_get_err_msg(uint nr) void my_error(uint nr, myf MyFlags, ...) { - const char *format; + struct Prepared_error error; va_list args; - char ebuff[ERRMSGSIZE]; DBUG_ENTER("my_error"); DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d", nr, MyFlags, errno)); - if (!(format = my_get_err_msg(nr))) - (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr); - else - { - va_start(args,MyFlags); - (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff, - sizeof(ebuff), format, args); - va_end(args); - } - (*error_handler_hook)(nr, ebuff, MyFlags); + va_start(args,MyFlags); + error= my_error_format(nr, MyFlags, args); + my_error_issue(&error); + va_end(args); DBUG_VOID_RETURN; } diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 8738c7b70d1bb..cbb304d1a727e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -192,6 +192,8 @@ SET (SQL_SOURCE socketpair.c socketpair.h opt_vcol_substitution.h opt_vcol_substitution.cc + opt_hints_parser.cc opt_hints_parser.h scan_char.h + opt_hints.cc opt_hints.h ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h ${GEN_SOURCES} diff --git a/sql/lex_ident.h b/sql/lex_ident.h index 99597397c5611..776cbdd9bf167 100644 --- a/sql/lex_ident.h +++ b/sql/lex_ident.h @@ -18,8 +18,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ +#include "my_global.h" +#include "m_ctype.h" #include "char_buffer.h" #include "lex_string.h" +#include "my_sys.h" extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *table_alias_charset; diff --git a/sql/lex_ident_cli.h b/sql/lex_ident_cli.h index 438354dda4b09..cba5086d0c865 100644 --- a/sql/lex_ident_cli.h +++ b/sql/lex_ident_cli.h @@ -85,21 +85,26 @@ struct Lex_string_with_metadata_st: public LEX_CSTRING struct Lex_ident_cli_st: public Lex_string_with_metadata_st { public: - void set_keyword(const char *s, size_t len) + Lex_ident_cli_st & set_keyword(const char *s, size_t len) { set(s, len, false, '\0'); + return *this; } - void set_ident(const char *s, size_t len, bool is_8bit) + Lex_ident_cli_st & set_ident(const char *s, size_t len, bool is_8bit) { set(s, len, is_8bit, '\0'); + return *this; } - void set_ident_quoted(const char *s, size_t len, bool is_8bit, char quote) + Lex_ident_cli_st & set_ident_quoted(const char *s, size_t len, + bool is_8bit, char quote) { set(s, len, is_8bit, quote); + return *this; } - void set_unquoted(const LEX_CSTRING *s, bool is_8bit) + Lex_ident_cli_st & set_unquoted(const LEX_CSTRING *s, bool is_8bit) { set(s, is_8bit, '\0'); + return *this; } const char *pos() const { return str - is_quoted(); } const char *end() const { return str + length + is_quoted(); } diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index 8a824eff582e2..f356f7467294a 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -4857,7 +4857,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) set/reset the slave thread's timer; a Rows_log_event update needs to set the timer itself */ - thd->set_query_timer(); + thd->set_query_timer_if_needed(); /* If there are no tables open, this must be the first row event seen diff --git a/sql/mem_root_array.h b/sql/mem_root_array.h index 3d03a5a5db218..4284b70420289 100644 --- a/sql/mem_root_array.h +++ b/sql/mem_root_array.h @@ -43,6 +43,7 @@ __has_trivial_destructor is supported by some (but not all) compilers we use. */ + template class Mem_root_array { @@ -53,7 +54,6 @@ class Mem_root_array Mem_root_array(MEM_ROOT *root) : m_root(root), m_array(NULL), m_size(0), m_capacity(0) { - DBUG_ASSERT(m_root != NULL); } Mem_root_array(MEM_ROOT *root, size_t n, const value_type &val= value_type()) @@ -62,6 +62,20 @@ class Mem_root_array resize(n, val); } + Mem_root_array(const Mem_root_array& other) + { + do_copy_construct(other); + } + + Mem_root_array &operator=(const Mem_root_array& other) + { + if(this != &other) + { + clear(); + do_copy_construct(other); + } + } + ~Mem_root_array() { clear(); @@ -231,14 +245,22 @@ class Mem_root_array const MEM_ROOT *mem_root() const { return m_root; } private: - MEM_ROOT *const m_root; - Element_type *m_array; - size_t m_size; - size_t m_capacity; - - // Not (yet) implemented. - Mem_root_array(const Mem_root_array&); - Mem_root_array &operator=(const Mem_root_array&); + MEM_ROOT *m_root; + Element_type *m_array= nullptr; + size_t m_size= 0; + size_t m_capacity= 0; + + void do_copy_construct(const Mem_root_array& other) + { + m_root= other.m_root; + reserve(other.size()); + for (size_t ix= 0; ix < other.size(); ++ix) + { + Element_type *p= &m_array[ix]; + new (p) Element_type(other[ix]); + } + m_size= other.m_size; + } }; diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index cc6c99588928e..09dab83439b81 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -21,6 +21,7 @@ #include "sql_statistics.h" #include "rowid_filter.h" #include "optimizer_defaults.h" +#include "opt_hints.h" static void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted, Cost_estimate *cost); @@ -1148,7 +1149,9 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, buf_manager.reset_buffer_sizes= do_nothing; buf_manager.redistribute_buffer_space= do_nothing; - if (mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED)) + if (!hint_key_state(thd, table, h_arg->active_index, + MRR_HINT_ENUM, OPTIMIZER_SWITCH_MRR) || + mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED)) goto use_default_impl; /* @@ -1901,10 +1904,16 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, THD *thd= primary_file->get_table()->in_use; TABLE_SHARE *share= primary_file->get_table_share(); + const bool mrr_on= hint_key_state(thd, table, keyno, MRR_HINT_ENUM, + OPTIMIZER_SWITCH_MRR); + const bool force_dsmrr_by_hints= + hint_key_state(thd, table, keyno, MRR_HINT_ENUM, 0) || + hint_table_state(thd, table, BKA_HINT_ENUM, false); + bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags); bool using_cpk= primary_file->is_clustering_key(keyno); *flags &= ~HA_MRR_IMPLEMENTATION_FLAGS; - if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) || + if (!(mrr_on || force_dsmrr_by_hints) || *flags & HA_MRR_INDEX_ONLY || (using_cpk && !doing_cpk_scan) || key_uses_partial_cols(share, keyno)) { @@ -1919,15 +1928,17 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, &dsmrr_cost)) return TRUE; - bool force_dsmrr; /* If mrr_cost_based flag is not set, then set cost of DS-MRR to be minimum of DS-MRR and Default implementations cost. This allows one to force use of DS-MRR whenever it is applicable without affecting other cost-based - choices. + choices. Note that if MRR or BKA hint is + specified, DS-MRR will be used regardless of cost. */ - if ((force_dsmrr= !optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED)) && - dsmrr_cost.total_cost() > cost->total_cost()) + const bool force_dsmrr= + (force_dsmrr_by_hints || + !optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED)); + if (force_dsmrr && dsmrr_cost.total_cost() > cost->total_cost()) dsmrr_cost= *cost; if (force_dsmrr || dsmrr_cost.total_cost() <= cost->total_cost()) diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc new file mode 100644 index 0000000000000..1a6038aa17c56 --- /dev/null +++ b/sql/opt_hints.cc @@ -0,0 +1,992 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2024, MariaDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "my_global.h" +#include "sql_class.h" +#include "sql_lex.h" +#include "sql_select.h" +#include "opt_hints.h" + +/** + Information about hints. Must be in sync with opt_hints_enum. + + Note: Hint name depends on hint state. 'NO_' prefix is added + if appropriate hint state bit(see Opt_hints_map::hints) is not + set. Depending on 'switch_state_arg' argument in 'parse tree + object' constructors(see parse_tree_h../cnm ints.[h,cc]) implementor + can control wishful form of the hint name. +*/ + +struct st_opt_hint_info opt_hint_info[]= +{ + {{STRING_WITH_LEN("BKA")}, true, false, false}, + {{STRING_WITH_LEN("BNL")}, true, false, false}, + {{STRING_WITH_LEN("ICP")}, true, false, false}, + {{STRING_WITH_LEN("MRR")}, true, false, false}, + {{STRING_WITH_LEN("NO_RANGE_OPTIMIZATION")}, true, false, false}, + {{STRING_WITH_LEN("QB_NAME")}, false, false, false}, + {{STRING_WITH_LEN("MAX_EXECUTION_TIME")}, false, true, false}, + {{STRING_WITH_LEN("SEMIJOIN")}, false, true, false}, + {{STRING_WITH_LEN("SUBQUERY")}, false, true, false}, + {{STRING_WITH_LEN("JOIN_PREFIX")}, false, true, true}, + {{STRING_WITH_LEN("JOIN_SUFFIX")}, false, true, true}, + {{STRING_WITH_LEN("JOIN_ORDER")}, false, true, true}, + {{STRING_WITH_LEN("JOIN_FIXED_ORDER")}, false, true, false}, + {null_clex_str, 0, 0, 0} +}; + +/** + Prefix for system generated query block name. + Used in information warning in EXPLAIN oputput. +*/ + +const LEX_CSTRING sys_qb_prefix= {"select#", 7}; + +void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type, + bool hint_state, + const Lex_ident_sys *qb_name_arg, + const Lex_ident_sys *table_name_arg, + const Lex_ident_sys *key_name_arg, + const Printable_parser_rule *hint) +{ + String str; + + /* Append hint name */ + if (!hint_state) + str.append(STRING_WITH_LEN("NO_")); + str.append(opt_hint_info[hint_type].hint_type); + + /* ER_WARN_UNKNOWN_QB_NAME with two arguments */ + if (err_code == ER_WARN_UNKNOWN_QB_NAME) + { + String qb_name_str; + append_identifier(thd, &qb_name_str, qb_name_arg->str, qb_name_arg->length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + err_code, ER_THD(thd, err_code), + qb_name_str.c_ptr_safe(), str.c_ptr_safe()); + return; + } + + /* ER_BAD_OPTION_VALUE with two arguments. hint argument is required here */ + if (err_code == ER_BAD_OPTION_VALUE) + { + DBUG_ASSERT(hint); + String args; + hint->append_args(thd, &args); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + err_code, ER_THD(thd, err_code), + args.c_ptr_safe(), str.c_ptr_safe()); + return; + } + + /* ER_WARN_CONFLICTING_HINT with one argument */ + str.append('('); + + /* Append table name */ + if (table_name_arg && table_name_arg->length > 0) + append_identifier(thd, &str, table_name_arg->str, table_name_arg->length); + + /* Append QB name */ + bool got_qb_name= qb_name_arg && qb_name_arg->length > 0; + if (got_qb_name) + { + if (hint_type != QB_NAME_HINT_ENUM) + { + /* + Add the delimiter for warnings like "Hint NO_ICP(`t1`@`q1` is ignored". + No need for the delimiter for warnings "Hint QB_NAME(qb1) is ignored" + */ + str.append(STRING_WITH_LEN("@")); + } + append_identifier(thd, &str, qb_name_arg->str, qb_name_arg->length); + } + + /* Append key name */ + if (key_name_arg && key_name_arg->length > 0) + { + str.append(' '); + append_identifier(thd, &str, key_name_arg->str, key_name_arg->length); + } + + /* Append additional hint arguments if they exist */ + if (hint) + { + if (got_qb_name || table_name_arg || key_name_arg) + str.append(' '); + + hint->append_args(thd, &str); + } + + str.append(')'); + + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + err_code, ER_THD(thd, err_code), str.c_ptr_safe()); +} + + +/** + Returns a pointer to Opt_hints_global object, + creates Opt_hints object if not exist. + + @param pc pointer to Parse_context object + + @return pointer to Opt_hints object, + NULL if failed to create the object +*/ + +Opt_hints_global *get_global_hints(Parse_context *pc) +{ + LEX *lex= pc->thd->lex; + + if (!lex->opt_hints_global) + { + lex->opt_hints_global= new (pc->thd->mem_root) + Opt_hints_global(pc->thd->mem_root); + } + return lex->opt_hints_global; +} + + +Opt_hints_qb *get_qb_hints(Parse_context *pc) +{ + if (pc->select->opt_hints_qb) + return pc->select->opt_hints_qb; + + Opt_hints_global *global_hints= get_global_hints(pc); + if (global_hints == NULL) + return NULL; + + Opt_hints_qb *qb= new (pc->thd->mem_root) + Opt_hints_qb(global_hints, pc->thd->mem_root, pc->select->select_number); + if (qb) + { + global_hints->register_child(qb); + pc->select->opt_hints_qb= qb; + /* + 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. + */ + qb->set_fixed(); + } + return qb; +} + +/** + Find existing Opt_hints_qb object, print warning + if the query block is not found. + + @param pc pointer to Parse_context object + @param qb_name query block name + @param hint_type the type of the hint from opt_hints_enum + @param hint_state true: hint enables a feature; false: disables it + + @return pointer to Opt_hints_table object if found, + NULL otherwise +*/ + +Opt_hints_qb *find_qb_hints(Parse_context *pc, + const Lex_ident_sys &qb_name, + opt_hints_enum hint_type, + bool hint_state) +{ + if (qb_name.length == 0) // no QB NAME is used + return pc->select->opt_hints_qb; + + Opt_hints_qb *qb= static_cast + (pc->thd->lex->opt_hints_global->find_by_name(qb_name)); + + if (qb == NULL) + { + print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state, + &qb_name, NULL, NULL, NULL); + } + return qb; +} + + +/** + Returns pointer to Opt_hints_table object, + create Opt_hints_table object if not exist. + + @param pc pointer to Parse_context object + @param table_name pointer to Hint_param_table object + @param qb pointer to Opt_hints_qb object + + @return pointer to Opt_hints_table object, + NULL if failed to create the object +*/ + +Opt_hints_table *get_table_hints(Parse_context *pc, + const Lex_ident_sys &table_name, + Opt_hints_qb *qb) +{ + Opt_hints_table *tab= + static_cast (qb->find_by_name(table_name)); + if (!tab) + { + tab= new (pc->thd->mem_root) + Opt_hints_table(table_name, qb, pc->thd->mem_root); + qb->register_child(tab); + } + + return tab; +} + + +bool Opt_hints::get_switch(opt_hints_enum type_arg) const +{ + if (is_specified(type_arg)) + return hints_map.is_switched_on(type_arg); + + if (opt_hint_info[type_arg].check_upper_lvl) + return parent->get_switch(type_arg); + + return false; +} + + +Opt_hints* Opt_hints::find_by_name(const LEX_CSTRING &name_arg) const +{ + for (uint i= 0; i < child_array.size(); i++) + { + const LEX_CSTRING name= child_array[i]->get_name(); + CHARSET_INFO *cs= child_array[i]->charset_info(); + if (name.str && !cs->strnncollsp(name, name_arg)) + return child_array[i]; + } + return NULL; +} + + +void Opt_hints::print(THD *thd, String *str) +{ + /* Do not print the hint if we couldn't attach it to its object */ + if (!is_fixed()) + return; + + // Print the hints stored in the bitmap + for (uint i= 0; i < MAX_HINT_ENUM; i++) + { + if (opt_hint_info[i].irregular_hint) + continue; + opt_hints_enum hint_type= static_cast(i); + if (is_specified(hint_type)) + { + append_hint_type(str, hint_type); + str->append(STRING_WITH_LEN("(")); + uint32 len_before_name= str->length(); + append_name(thd, str); + uint32 len_after_name= str->length(); + if (len_after_name > len_before_name) + str->append(' '); + if (opt_hint_info[i].has_arguments) + append_hint_arguments(thd, hint_type, str); + if (str->length() == len_after_name + 1) + { + // No additional arguments were printed, trim the space added before + str->length(len_after_name); + } + str->append(STRING_WITH_LEN(") ")); + } + } + + print_irregular_hints(thd, str); + + for (uint i= 0; i < child_array.size(); i++) + child_array[i]->print(thd, str); +} + + +/* + @brief + Append hint "type", for example, "NO_RANGE_OPTIMIZATION" or "BKA" +*/ + +void Opt_hints::append_hint_type(String *str, opt_hints_enum type) +{ + if(!hints_map.is_switched_on(type)) + str->append(STRING_WITH_LEN("NO_")); + str->append(opt_hint_info[type].hint_type); +} + + +void Opt_hints::print_unfixed_warnings(THD *thd) +{ + String hint_name_str, hint_type_str; + append_name(thd, &hint_name_str); + + for (uint i= 0; i < MAX_HINT_ENUM; i++) + { + if (is_specified(static_cast(i))) + { + hint_type_str.length(0); + append_hint_type(&hint_type_str, static_cast(i)); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + get_unfixed_warning_code(), + ER_THD(thd, get_unfixed_warning_code()), + hint_name_str.c_ptr_safe(), + hint_type_str.c_ptr_safe()); + } + } +} + +/* + @brief + Recursively walk the descendant hints and emit warnings for any + unresolved hints +*/ + +void Opt_hints::check_unfixed(THD *thd) +{ + if (!is_fixed()) + print_unfixed_warnings(thd); + + if (!are_children_fully_fixed()) + { + for (uint i= 0; i < child_array.size(); i++) + child_array[i]->check_unfixed(thd); + } +} + + +Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg, + MEM_ROOT *mem_root_arg, + uint select_number_arg) + : Opt_hints(Lex_ident_sys(), opt_hints_arg, mem_root_arg), + select_number(select_number_arg), + join_order_hints(mem_root_arg), + join_order_hints_ignored(0) +{ + sys_name.str= buff; + sys_name.length= my_snprintf(buff, sizeof(buff), "%s%x", + sys_qb_prefix.str, select_number); +} + + +Opt_hints_table *Opt_hints_qb::fix_hints_for_table(TABLE *table, + const Lex_ident_table &alias) +{ + Opt_hints_table *tab= static_cast(find_by_name(alias)); + + table->pos_in_table_list->opt_hints_qb= this; + + if (!tab) // Tables not found + return NULL; + + if (!tab->fix_hint(table)) + incr_fully_fixed_children(); + + return tab; +} + + +bool Opt_hints_qb::semijoin_enabled(THD *thd) const +{ + if (subquery_hint) // SUBQUERY hint disables semi-join + return false; + + if (semijoin_hint) + { + // SEMIJOIN hint will always force semijoin regardless of optimizer_switch + if(get_switch(SEMIJOIN_HINT_ENUM)) + return true; + + // NO_SEMIJOIN hint. If strategy list is empty, do not use SEMIJOIN + if (semijoin_strategies_map == 0) + return false; + + // Fall through: NO_SEMIJOIN w/ strategies neither turns SEMIJOIN off nor on + } + + return optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN); +} + + +uint Opt_hints_qb::sj_enabled_strategies(uint opt_switches) const +{ + // Hints override switches + if (semijoin_hint) + { + const uint strategies= semijoin_strategies_map; + if (get_switch(SEMIJOIN_HINT_ENUM)) // SEMIJOIN hint + return (strategies == 0) ? opt_switches : strategies; + + // NO_SEMIJOIN hint. Hints and optimizer_switch both affect strategies + return ~strategies & opt_switches; + } + + return opt_switches; +} + + +void Opt_hints_qb::append_hint_arguments(THD *thd, opt_hints_enum hint, + String *str) +{ + switch (hint) + { + case SUBQUERY_HINT_ENUM: + subquery_hint->append_args(thd, str); + break; + case SEMIJOIN_HINT_ENUM: + semijoin_hint->append_args(thd, str); + break; + case JOIN_FIXED_ORDER_HINT_ENUM: + join_fixed_order->append_args(thd, str); + break; + default: + DBUG_ASSERT(0); + } +} + + +/* + @brief + For each index IDX, put its hints into keyinfo_array[IDX] +*/ + +bool Opt_hints_table::fix_hint(TABLE *table) +{ + /* + Ok, there's a table we attach to. Mark this hint as fixed and proceed to + fixing the child objects. + */ + set_fixed(); + + if (child_array_ptr()->size() == 0) // No key level hints + return false; // Ok, fully fixed + + /* Make sure that adjustment is called only once. */ + DBUG_ASSERT(keyinfo_array.size() == 0); + keyinfo_array.resize(table->s->keys, NULL); + + for (Opt_hints** hint= child_array_ptr()->begin(); + hint < child_array_ptr()->end(); ++hint) + { + KEY *key_info= table->key_info; + for (uint j= 0 ; j < table->s->keys ; j++, key_info++) + { + if (key_info->name.streq((*hint)->get_name())) + { + (*hint)->set_fixed(); + keyinfo_array[j]= static_cast(*hint); + incr_fully_fixed_children(); + break; + } + } + } + + if (are_children_fully_fixed()) + return false; + + return true; // Some children are not fully fixed +} + + +/** + Function returns hint value depending on + the specfied hint level. If hint is specified + on current level, current level hint value is + returned, otherwise parent level hint is checked. + + @param hint Pointer to the hint object + @param parent_hint Pointer to the parent hint object, + should never be NULL + @param type_arg hint type + @param OUT ret_val hint value depending on + what hint level is used + + @return true if hint is specified, false otherwise +*/ + +static bool get_hint_state(Opt_hints *hint, + Opt_hints *parent_hint, + opt_hints_enum type_arg, + bool *ret_val) +{ + DBUG_ASSERT(parent_hint); + + if (!opt_hint_info[type_arg].has_arguments) + { + if (hint && hint->is_specified(type_arg)) + { + *ret_val= hint->get_switch(type_arg); + return true; + } + else if (opt_hint_info[type_arg].check_upper_lvl && + parent_hint->is_specified(type_arg)) + { + *ret_val= parent_hint->get_switch(type_arg); + return true; + } + } + else + { + /* Complex hint with arguments, not implemented atm */ + DBUG_ASSERT(0); + } + return false; +} + + +/* + @brief + Check whether a given optimization is enabled for table.keyno. + + @detail + First check if a hint is present, then check optimizer_switch +*/ + +bool hint_key_state(const THD *thd, const TABLE *table, + uint keyno, opt_hints_enum type_arg, + uint optimizer_switch) +{ + Opt_hints_table *table_hints= table->pos_in_table_list->opt_hints_table; + + /* Parent should always be initialized */ + if (table_hints && keyno != MAX_KEY) + { + Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ? + table_hints->keyinfo_array[keyno] : NULL; + bool ret_val= false; + if (get_hint_state(key_hints, table_hints, type_arg, &ret_val)) + return ret_val; + } + + return optimizer_flag(thd, optimizer_switch); +} + + +bool hint_table_state(const THD *thd, const TABLE *table, + 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; + if (get_hint_state(table_list->opt_hints_table, + table_list->opt_hints_qb, + type_arg, &ret_val)) + return ret_val; + } + + return fallback_value; +} + + +void append_table_name(THD *thd, String *str, const LEX_CSTRING &table_name, + const LEX_CSTRING &qb_name) +{ + /* Append table name */ + append_identifier(thd, str, &table_name); + + /* Append QB name */ + if (qb_name.length > 0) + { + str->append(STRING_WITH_LEN("@")); + append_identifier(thd, str, &qb_name); + } +} + + +void Opt_hints_qb::apply_join_order_hints(JOIN *join) +{ + if (join_fixed_order) + { + DBUG_ASSERT(join_order_hints.size() == 0 && !join_prefix && !join_suffix); + // The hint is already applied at Parser::Join_order_hint::resolve() + return; + } + DBUG_ASSERT(!join_fixed_order); + + // Apply hints in the same order they were specified in the query + for (uint hint_idx= 0; hint_idx < join_order_hints.size(); hint_idx++) + { + Parser::Join_order_hint* hint= join_order_hints[hint_idx]; + if (join->select_options & SELECT_STRAIGHT_JOIN) + { + // Only mark as ignored and print the warning + join_order_hints_ignored |= 1ULL << hint_idx; + print_warn(join->thd, ER_WARN_CONFLICTING_HINT, hint->hint_type, true, + nullptr, nullptr, nullptr, hint); + } + else if (set_join_hint_deps(join, hint)) + { + // Mark as ignored + join_order_hints_ignored |= 1ULL << hint_idx; + } + } +} + + +/** + @brief + Resolve hint tables, check and set table dependencies according to one + JOIN_ORDER, JOIN_PREFIX, or JOIN_SUFFIX hint. + + @param join pointer to JOIN object + @param hint The hint + + @detail + If the hint is ignored due to circular table dependencies, original + dependencies are restored and a warning is generated. + + == Dependencies that we add == + For any JOIN_HINT(t1, t2, t3, t4) we add the these dependencies: + + t2.dependent|= {t1} + t3.dependent|= {t1,t2} + t4.dependent|= {t1,t2,t3} + and so forth. + + This makes sure that the listed tables occur in the join order in the order + they are listed in the hint. + + For JOIN_ORDER, this is all what we need. + For JOIN_PREFIX(t1, t2, ...) we also add dependencies on {t1,t2,...} + for all tables not listed in the hint. + For JOIN_SUFFIX(t1, t2, ...) dependencies on all tables that are NOT listed + in the hint are added to all tables LISTED in the hint: {t1, t2, ...} + + @return false if hint is applied, true otherwise. +*/ + +bool Opt_hints_qb::set_join_hint_deps(JOIN *join, + const Parser::Join_order_hint *hint) +{ + /* + Make a copy of original table dependencies. If an error occurs + when applying the hint, the original dependencies will be restored. + */ + table_map *orig_dep_array= join->export_table_dependencies(); + + // Map of the tables, specified in the hint + table_map hint_tab_map= 0; + + for (const Parser::Table_name_and_Qb& tbl_name_and_qb : hint->table_names) + { + bool hint_table_found= false; + for (uint i= 0; i < join->table_count; i++) + { + TABLE_LIST *table= join->join_tab[i].tab_list; + if (!compare_table_name(&tbl_name_and_qb, table)) + { + hint_table_found= true; + /* + Const tables are excluded from the process of dependency setting + since they are always first in the table order. Note that it + does not prevent the hint from being applied to the non-const tables + */ + if (join->const_table_map & table->get_map()) + break; + + JOIN_TAB *join_tab= &join->join_tab[i]; + // Hint tables are always dependent on preceding tables + join_tab->dependent |= hint_tab_map; + update_nested_join_deps(join, join_tab, hint_tab_map); + hint_tab_map |= join_tab->tab_list->get_map(); + break; + } + } + + if (!hint_table_found) + { + print_join_order_warn(join->thd, hint->hint_type, tbl_name_and_qb); + join->restore_table_dependencies(orig_dep_array); + return true; + } + } + + // Add dependencies that are related to non-hint tables + for (uint i= 0; i < join->table_count; i++) + { + JOIN_TAB *join_tab= &join->join_tab[i]; + const table_map dependent_tables= + get_other_dep(join, hint->hint_type, hint_tab_map, + join_tab->tab_list->get_map()); + update_nested_join_deps(join, join_tab, dependent_tables); + join_tab->dependent |= dependent_tables; + } + + if (join->propagate_dependencies(join->join_tab)) + { + join->restore_table_dependencies(orig_dep_array); + print_warn(join->thd, ER_WARN_CONFLICTING_HINT, hint->hint_type, true, + nullptr, nullptr, nullptr, hint); + return true; + } + return false; +} + + +/** + Function updates dependencies for nested joins. If a table + specified in the hint belongs to a nested join, we need + to update dependencies of all tables of the nested join + with the same dependency as for the hint table. It is also + necessary to update all tables of the nested joins this table + is part of. + + @param join pointer to JOIN object + @param hint_tab pointer to JOIN_TAB object + @param hint_tab_map map of the tables, specified in the hint + + + @detail + This function is called when the caller has added a dependency: + + hint_tab now also depends on hint_tab_map. + + For example: + + FROM t0 ... LEFT JOIN ( ... t1 ... t2 ... ) ON ... + + hint_tab=t1, hint_tab_map={t0}. + + We want to avoid the situation where the optimizer has constructed a join + prefix with table t2 and without table t0: + + ... t2 + + and now it needs to add t1 to the join prefix (it must do so, see + add_table_function_dependencies, check_interleaving_with_nj) but it can't + do that because t0 is not in the join prefix, and it's not possible to add + t0 as that would break the NO-INTERLEAVING rule (see mentioned functions) + + In order to avoid this situation, we make t2 also depend t0 (that is, also + depend on any tables outside the join nest that we've made t1 to depend on) + + Note that inside the join nest + + LEFT JOIN ( ... t1 ... t2 ... ) + + t1 and t2 may not be direct children but rather occur inside children join + nests: + + LEFT JOIN ( ... LEFT JOIN (...t1...) ... LEFT JOIN (...t2...) ... ) +*/ + +void Opt_hints_qb::update_nested_join_deps(JOIN *join, const JOIN_TAB *hint_tab, + table_map hint_tab_map) +{ + const TABLE_LIST *table= hint_tab->tab_list; + if (table->embedding) + { + for (uint i= 0; i < join->table_count; i++) + { + JOIN_TAB *tab= &join->join_tab[i]; + /* Walk up the nested joins that tab->table is a part of */ + for (TABLE_LIST *emb= tab->tab_list->embedding; emb; emb=emb->embedding) + { + /* + Apply the rule only for outer joins. Semi-joins do not impose such + limitation + */ + if (emb->on_expr) + { + const NESTED_JOIN *const nested_join= emb->nested_join; + /* Is hint_tab somewhere inside this nested join, too? */ + if (hint_tab->embedding_map & nested_join->nj_map) + { + /* + Yes, it is. Then, tab->table be also dependent on all outside + tables that hint_tab is dependent on: + */ + tab->dependent |= (hint_tab_map & ~nested_join->used_tables); + } + } + } + } + } +} + + +/** + Function returns a map of dependencies which must be applied to the + particular table of a JOIN, according to the join order hint + + @param join JOIN to which the hints are being applied + @param type hint type + @param hint_tab_map Bitmap of all tables listed in the hint. + @param table_map Bit of the table that we're setting extra dependencies + for. + + @detail + This returns extra dependencies between tables listed in the Hint and tables + that are not listed. Depending on hint type, these are: + + JOIN_PREFIX(t1, t2, ...) - all not listed tables depend on {t1,t2,...}. + + JOIN_SUFFIX(t1, t2, ...) - all tables listed in the hint depend on all tables + that are not listed in the hint + JOIN_ORDER(t1, t2, ...) - No extra dependencies needed. + + @return bitmap of dependencies to apply +*/ + +table_map Opt_hints_qb:: + get_other_dep(JOIN *join, opt_hints_enum type, table_map hint_tab_map, + table_map table_map) +{ + switch (type) + { + case JOIN_PREFIX_HINT_ENUM: + if (hint_tab_map & table_map) // Hint table: No additional dependencies + return 0; + // Other tables: depend on all hint tables + return hint_tab_map; + case JOIN_SUFFIX_HINT_ENUM: + if (hint_tab_map & table_map) // Hint table: depends on all other tables + return join->all_tables_map() & ~hint_tab_map; + return 0; + case JOIN_ORDER_HINT_ENUM: + return 0; // No additional dependencies + default: + DBUG_ASSERT(0); + break; + } + return 0; +} + + +/** + Function compares hint table name and TABLE_LIST table name. + Query block name is taken into account also. + + @param hint_table_and_qb table/query block names given in the hint + @param table pointer to TABLE_LIST object + + @return false if table names are equal, true otherwise. +*/ + +bool Opt_hints_qb::compare_table_name( + const Parser::Table_name_and_Qb *hint_table_and_qb, + const TABLE_LIST *table) +{ + const LEX_CSTRING &join_tab_qb_name= + table->opt_hints_qb ? table->opt_hints_qb->get_name() : Lex_ident_sys(); + + /* + If QB name is not specified explicitly for a table name int hint, + for example `JOIN_PREFIX(t2)` or `JOIN_SUFFIX(@q1 t3)` then QB name is + considered to be equal to `Opt_hints_qb::get_name()` + */ + const LEX_CSTRING &hint_tab_qb_name= + hint_table_and_qb->qb_name.length > 0 ? hint_table_and_qb->qb_name : + this->get_name(); + + CHARSET_INFO *cs= charset_info(); + // Compare QB names + if (cs->strnncollsp(join_tab_qb_name, hint_tab_qb_name)) + return true; + // Compare table names + return cs->strnncollsp(table->alias, hint_table_and_qb->table_name); +} + + +void Opt_hints_qb::print_irregular_hints(THD *thd, String *str) +{ + /* Print join order hints */ + for (uint i= 0; i < join_order_hints.size(); i++) + { + if (join_order_hints_ignored & (1ULL << i)) + continue; + const Parser::Join_order_hint *hint= join_order_hints[i]; + str->append(opt_hint_info[hint->hint_type].hint_type); + str->append(STRING_WITH_LEN("(")); + append_name(thd, str); + str->append(STRING_WITH_LEN(" ")); + hint->append_args(thd, str); + str->append(STRING_WITH_LEN(") ")); + } +} + + +void Opt_hints_qb::print_join_order_warn(THD *thd, opt_hints_enum type, + const Parser::Table_name_and_Qb &tbl_name) +{ + String tbl_name_str, hint_type_str; + hint_type_str.append(opt_hint_info[type].hint_type); + append_table_name(thd, &tbl_name_str, tbl_name.table_name, tbl_name.qb_name); + uint err_code= ER_UNRESOLVED_TABLE_HINT_NAME; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + err_code, ER_THD(thd, err_code), + tbl_name_str.c_ptr_safe(), hint_type_str.c_ptr_safe()); +} + + +/* + @brief + Fix global-level hints (and only them) +*/ + +bool Opt_hints_global::fix_hint(THD *thd) +{ + if (thd->lex->is_ps_or_view_context_analysis()) + return false; + + if (!max_exec_time_hint) + { + /* No possible errors */ + set_fixed(); + return false; + } + + /* + 2nd step of MAX_EXECUTION_TIME() hint validation. Some checks were already + performed during the parsing stage (Max_execution_time_hint::resolve()), + but the following checks can only be performed during the JOIN preparation + because thd->lex variables are not available during parsing + */ + if (thd->lex->sql_command != SQLCOM_SELECT || // not a SELECT statement + thd->lex->sphead || thd->in_sub_stmt != 0 || // or a SP/trigger/event + max_exec_time_select_lex->master_unit() != &thd->lex->unit || // or a subquery + max_exec_time_select_lex->select_number != 1) // not a top-level select + { + print_warn(thd, ER_NOT_ALLOWED_IN_THIS_CONTEXT, MAX_EXEC_TIME_HINT_ENUM, + true, NULL, NULL, NULL, max_exec_time_hint); + } + else + { + thd->reset_query_timer(); + thd->set_query_timer_force(max_exec_time_hint->get_milliseconds() * 1000); + } + set_fixed(); + return false; +} + + +#ifndef DBUG_OFF +static char dbug_print_hint_buf[64]; + +const char *dbug_print_hints(Opt_hints_qb *hint) +{ + char *buf= dbug_print_hint_buf; + THD *thd= current_thd; + String str(buf, sizeof(dbug_print_hint_buf), &my_charset_bin); + str.length(0); + if (!hint) + return "(Opt_hints_qb*)NULL"; + + hint->print(thd, &str); + + if (str.c_ptr_safe() == buf) + return buf; + else + return "Couldn't fit into buffer"; +} +#endif diff --git a/sql/opt_hints.h b/sql/opt_hints.h new file mode 100644 index 0000000000000..817a5a27ae2f4 --- /dev/null +++ b/sql/opt_hints.h @@ -0,0 +1,685 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2024, MariaDB plc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + HintsArchitecture + + == Parsing == + Hints have a separate parser, see sql/opt_hint_parser.{h,cc} + The parser is invoked separately for each occurence of + + SELECT / *+ hint_body * / ... + + in the query. The result of parsing is saved in + SELECT_LEX::parsed_optimizer_hints. + + == Hint "resolution" == + + This is done using "resolve" method of parsed data structures + This process + - Creates interpreted hint structures: Opt_hints_global, Opt_hints_qb, + Opt_hints_table, Opt_hints_key. + - Interprets QB_NAME hints and assigns Opt_hints_qb objects their names. + - Table-level hints are put into their Query Block's Opt_hints_qb object. + - Index-level hints are put into their table's Opt_hints_table object. + + Currently, this process is done at the parser stage. (This is one of the + causes why hints do not work across VIEW bounds, here or in MySQL). + + Query-block level hints can be reached through SELECT_LEX::opt_hints_qb. + + == Hint "fixing" == + + During Name Resolution, hints are attached the real objects they control: + - table-level hints find their tables, + - index-level hints find their indexes. + + This is done in setup_tables() which calls fix_hints_for_table() for + each table, as a result TABLE_LIST::opt_hints_table points to the table's + hints. + + == Hint hierarchy == + + Hints have this hierarchy, less specific to more specific: + + Opt_hints_global + Opt_hints_qb + Opt_hints_table + Opt_hints_key + + Some hints can be specified at a specific level (e.g. per-index) or at a + more general level (e.g. per-table). When checking the hint, we need + to check for per-index commands and then maybe per-table command. + + == API for checking hints == + + The optimizer checks hints' instructions using these calls for table/index + level hints: + hint_table_state() + hint_table_state_or_fallback() + hint_key_state() + For query block-level hints: + opt_hints_qb->semijoin_enabled() + opt_hints_qb->sj_enabled_strategies() + opt_hints_qb->apply_join_order_hints(join) - This adds extra dependencies + between tables that ensure that the optimizer picks a join order + prescribed by the hints. +*/ + + +#ifndef OPT_HINTS_INCLUDED +#define OPT_HINTS_INCLUDED + +#include +#include "my_config.h" +#include "sql_alloc.h" +#include "sql_list.h" +#include "mem_root_array.h" +#include "sql_string.h" +#include "sql_bitmap.h" +#include "sql_show.h" +#include "mysqld_error.h" +#include "opt_hints_parser.h" +#include "opt_trace.h" + + +struct LEX; +struct TABLE; + +struct st_opt_hint_info +{ + LEX_CSTRING hint_type; // Hint "type", like "BKA" or "MRR". + bool check_upper_lvl; // true if upper level hint check is needed (for hints + // which can be specified on more than one level). + bool has_arguments; // true if hint has additional arguments. + bool irregular_hint; // true if hint requires some special handling. + // Currently it's used only for join order hints + // since they need a special printing procedure. +}; + +typedef Optimizer_hint_parser Parser; + +/** + Opt_hints_map contains information + about hint state(specified or not, hint value). +*/ + +class Opt_hints_map : public Sql_alloc +{ + Bitmap<64> hints; // hint state + Bitmap<64> hints_specified; // true if hint is specified + +public: + Opt_hints_map() + { + hints.clear_all(); + hints_specified.clear_all(); + } + + my_bool is_specified(opt_hints_enum type_arg) const + { + return hints_specified.is_set(type_arg); + } + + void set_switch(opt_hints_enum type_arg, + bool switch_state_arg) + { + if (switch_state_arg) + hints.set_bit(type_arg); + else + hints.clear_bit(type_arg); + hints_specified.set_bit(type_arg); + } + + bool is_switched_on(opt_hints_enum type_arg) const + { + return hints.is_set(type_arg); + } +}; + + +class Opt_hints_key; + + +/** + Opt_hints class is used as ancestor for Opt_hints_global, + Opt_hints_qb, Opt_hints_table, Opt_hints_key classes. + + Opt_hints_global class is hierarchical structure. + It contains information about global hints and also + contains array of QUERY BLOCK level objects (Opt_hints_qb class). + Each QUERY BLOCK level object contains array of TABLE level hints + (class Opt_hints_table). Each TABLE level hint contains array of + KEY level hints (Opt_hints_key class). + Hint information(specified, on|off state) is stored in hints_map object. +*/ + +class Opt_hints : public Sql_alloc +{ +protected: + /* + Name of object referred by the hint. + This name is empty for global level, + query block name for query block level, + table name for table level and key name + for key level. + */ + Lex_ident_sys name; +private: + /* + Parent object. There is no parent for global level, + for query block level parent is Opt_hints_global object, + for table level parent is Opt_hints_qb object, + for key level parent is Opt_hints_key object. + */ + Opt_hints *parent; + + /* Bitmap describing switch-type (on/off) hints set at this scope */ + Opt_hints_map hints_map; + + /* Array of child objects. i.e. array of the lower level objects */ + Mem_root_array child_array; + + /* true if hint is connected to the real object */ + bool fixed; + + /* + Number of child hints that are fully fixed, that is, fixed and + have all their children also fully fixed. + */ + uint n_fully_fixed_children; + +public: + + Opt_hints(const Lex_ident_sys &name_arg, + 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) + { } + + bool is_specified(opt_hints_enum type_arg) const + { + return hints_map.is_specified(type_arg); + } + + /** + Function sets switch hint state. + + @param switch_state_arg switch hint state + @param type_arg hint type + @param check_parent true if hint can be on parent level + + @return true if hint is already specified, + false otherwise + */ + bool set_switch(bool switch_state_arg, + opt_hints_enum type_arg, + bool check_parent) + { + if (is_specified(type_arg) || + (check_parent && parent->is_specified(type_arg))) + return true; + + hints_map.set_switch(type_arg, switch_state_arg); + return false; + } + + /** + Function returns switch hint state. + + @param type_arg hint type + + @return hint value if hint is specified, + false otherwise + */ + bool get_switch(opt_hints_enum type_arg) const; + + /* Collation for comparing the name of this hint */ + virtual CHARSET_INFO *charset_info() const + { + return Lex_ident_column::charset_info(); + } + + const LEX_CSTRING get_name() const + { + return name; + } + 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 incr_fully_fixed_children() { n_fully_fixed_children++; } + Mem_root_array *child_array_ptr() { return &child_array; } + + bool are_children_fully_fixed() const + { + return child_array.size() == n_fully_fixed_children; + } + + void register_child(Opt_hints* hint_arg) + { + child_array.push_back(hint_arg); + } + + /** + Find hint among lower-level hint objects. + + @param name_arg hint name + + @return hint if found, + NULL otherwise + */ + Opt_hints *find_by_name(const LEX_CSTRING &name_arg) const; + /** + Print all hints except of QB_NAME hint. + + @param thd Pointer to THD object + @param str Pointer to String object + */ + void print(THD *thd, String *str); + /** + Check if there are any unfixed hint objects and + print warnings for them. + + @param thd Pointer to THD object + */ + void check_unfixed(THD *thd); + + /* + Append the name of object(table, index, etc) that hints in this collection + are attached to. + */ + virtual void append_name(THD *thd, String *str)= 0; + + virtual void append_hint_arguments(THD *thd, opt_hints_enum hint, String *str) + { + DBUG_ASSERT(0); + } + + virtual ~Opt_hints() {} + +private: + /** + Append hint type. + + @param str Pointer to String object + @param type Hint type + */ + void append_hint_type(String *str, opt_hints_enum type); + /** + Print warnings abount unfixed hints in this hint collection + + @param thd Pointer to THD object + */ + void print_unfixed_warnings(THD *thd); + +protected: + /** + Override this function in descendants so that print_unfixed_warnings() + prints the proper warning text for table/index level unfixed hints + */ + virtual uint get_unfixed_warning_code() const + { + DBUG_ASSERT(0); + return 0; + } + + /** + Function prints hints which are non-standard and don't + fit into existing hint infrastructure. + + @param thd pointer to THD object + @param str pointer to String object + */ + virtual void print_irregular_hints(THD *thd, String *str) {} +}; + + +/** + Global level hints. Currently, it's only MAX_EXECUTION_TIME. +*/ + +class Opt_hints_global : public Opt_hints +{ +public: + const Parser::Max_execution_time_hint *max_exec_time_hint= nullptr; + + /* + If MAX_EXECUTION_TIME() hint was provided, this pointer is set to + the SELECT_LEX which the hint is attached to. + NULL if MAX_EXECUTION_TIME() hint is missing. + */ + st_select_lex *max_exec_time_select_lex= nullptr; + + Opt_hints_global(MEM_ROOT *mem_root_arg) + : Opt_hints(Lex_ident_sys(), NULL, mem_root_arg) + {} + + virtual void append_name(THD *thd, String *str) override + { + /* + Don't write anything: global hints are not attached to anything named + like a table/query block/etc + */ + } + + void append_hint_arguments(THD *thd, opt_hints_enum hint, + String *str) override + { + if (hint == MAX_EXEC_TIME_HINT_ENUM) + max_exec_time_hint->append_args(thd, str); + else + DBUG_ASSERT(0); + } + + bool fix_hint(THD *thd); +}; + + +class Opt_hints_table; + +/** + Query block level hints. Currently, these can be: + - QB_NAME (this is interpreted in the parser and is not explicitly + present after that) + - [NO_]SEMIJOIN + - SUBQUERY + - JOIN_PREFIX + - JOIN_SUFFIX + - JOIN_ORDER + - JOIN_FIXED_ORDER +*/ + +class Opt_hints_qb : public Opt_hints +{ + uint select_number; // SELECT_LEX number + LEX_CSTRING sys_name; // System QB name + char buff[32]; // Buffer to hold sys name + + // Array of join order hints + Mem_root_array join_order_hints; + // Bitmap marking ignored hints + ulonglong join_order_hints_ignored; + // Max capacity to avoid overflowing of join_order_hints_ignored bitmap + static const uint MAX_ALLOWED_JOIN_ORDER_HINTS= + sizeof(join_order_hints_ignored) * 8; + +public: + Opt_hints_qb(Opt_hints *opt_hints_arg, + MEM_ROOT *mem_root_arg, + uint select_number_arg); + + const LEX_CSTRING get_print_name() + { + return name.str ? name : sys_name; + } + + + void append_qb_hint(THD *thd, String *str) + { + if (name.str) + { + str->append(STRING_WITH_LEN("QB_NAME(")); + append_identifier(thd, str, &name); + str->append(STRING_WITH_LEN(") ")); + } + } + + virtual void append_name(THD *thd, String *str) override + { + /* Append query block name. */ + str->append(STRING_WITH_LEN("@")); + const LEX_CSTRING print_name= get_print_name(); + append_identifier(thd, str, &print_name); + } + + void append_hint_arguments(THD *thd, opt_hints_enum hint, + String *str) override; + + /** + 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 Pointer to TABLE object + @param alias Table alias + + @return pointer Opt_hints_table object if this object is found, + NULL otherwise. + */ + Opt_hints_table *fix_hints_for_table(TABLE *table, + const Lex_ident_table &alias); + + /** + Checks if join order hints are applicable and + applies table dependencies if possible. + + @param join JOIN object + */ + void apply_join_order_hints(JOIN *join); + + /** + Returns whether semi-join is enabled for this query block + + A SEMIJOIN hint will force semi-join regardless of optimizer_switch settings. + A NO_SEMIJOIN hint will only turn off semi-join if the variant with no + strategies is used. + A SUBQUERY hint will turn off semi-join. + If there is no SEMIJOIN/SUBQUERY hint, optimizer_switch setting determines + whether SEMIJOIN is used. + + @param thd Pointer to THD object for session. + Used to access optimizer_switch + + @return true if semijoin is enabled + */ + bool semijoin_enabled(THD *thd) const; + + /** + Returns bit mask of which semi-join strategies are enabled for this query + block. + + @param opt_switches Bit map of strategies enabled by optimizer_switch + + @return Bit mask of strategies that are enabled + */ + uint sj_enabled_strategies(uint opt_switches) const; + + const Parser::Semijoin_hint* semijoin_hint= nullptr; + + /** + Bitmap of strategies listed in the SEMIJOIN/NO_SEMIJOIN hint body, e.g. + FIRSTMATCH | LOOSESCAN + */ + uint semijoin_strategies_map= 0; + + const Parser::Subquery_hint *subquery_hint= nullptr; + uint subquery_strategy= SUBS_NOT_TRANSFORMED; + + /** + Returns TRUE if the query block has at least one of the hints + JOIN_PREFIX(), JOIN_SUFFIX(), JOIN_ORDER() (but not JOIN_FIXED_ORDER()!) + */ + bool has_join_order_hints() const + { + return join_order_hints.size() > 0; + } + + /* + Adds a join order hint to the array if the capacity is not exceeded. + Returns FALSE on success, + TRUE on failure (capacity exceeded) + */ + bool add_join_order_hint(Parser::Join_order_hint *hint_arg) + { + if (join_order_hints.size() >= MAX_ALLOWED_JOIN_ORDER_HINTS) + return true; + join_order_hints.push_back(hint_arg); + return false; + } + + const Parser::Join_order_hint *join_prefix= nullptr; + const Parser::Join_order_hint *join_suffix= nullptr; + const Parser::Join_order_hint *join_fixed_order= nullptr; + + void trace_hints(THD *thd) + { + if (unlikely(thd->trace_started())) + { + Json_writer_object obj(thd); + String str; + str.length(0); + print(thd, &str); + // Eventually we may want to print them as array. + obj.add("hints", str.c_ptr_safe()); + } + } + +private: + bool set_join_hint_deps(JOIN *join, const Parser::Join_order_hint *hint); + void update_nested_join_deps(JOIN *join, const JOIN_TAB *hint_tab, + table_map hint_tab_map); + table_map get_other_dep(JOIN *join, opt_hints_enum type, + table_map hint_tab_map, table_map table_map); + bool compare_table_name(const Parser::Table_name_and_Qb *hint_table_and_qb, + const TABLE_LIST *table); + void print_irregular_hints(THD *thd, String *str) override; + void print_join_order_warn(THD *thd, opt_hints_enum type, + const Parser::Table_name_and_Qb &tbl_name); +}; + + +/** + Table level hints. +*/ + +class Opt_hints_table : public Opt_hints +{ +public: + Mem_root_array keyinfo_array; + + Opt_hints_table(const Lex_ident_sys &table_name_arg, + Opt_hints_qb *qb_hints_arg, + MEM_ROOT *mem_root_arg) + : Opt_hints(table_name_arg, qb_hints_arg, mem_root_arg), + keyinfo_array(mem_root_arg) + { } + + CHARSET_INFO *charset_info() const override + { + return Lex_ident_table::charset_info(); + } + + /** + Append table name. + + @param thd pointer to THD object + @param str pointer to String object + */ + virtual void append_name(THD *thd, String *str) override + { + append_identifier(thd, str, &name); + get_parent()->append_name(thd, str); + } + + /** + Function sets correlation between key hint objects and + appropriate KEY structures. + + @param table Pointer to TABLE object + */ + bool fix_hint(TABLE *table); + + virtual uint get_unfixed_warning_code() const override + { + return ER_UNRESOLVED_TABLE_HINT_NAME; + } +}; + + +/** + Key level hints. +*/ + +class Opt_hints_key : public Opt_hints +{ +public: + + Opt_hints_key(const Lex_ident_sys &key_name_arg, + Opt_hints_table *table_hints_arg, + MEM_ROOT *mem_root_arg) + : Opt_hints(key_name_arg, table_hints_arg, mem_root_arg) + { } + + /** + Append key name. + + @param thd pointer to THD object + @param str pointer to String object + */ + virtual void append_name(THD *thd, String *str) override + { + get_parent()->append_name(thd, str); + str->append(' '); + append_identifier(thd, str, &name); + } + + virtual uint get_unfixed_warning_code() const override + { + return ER_UNRESOLVED_INDEX_HINT_NAME; + } +}; + + +/** + Returns key hint value if hint is specified, returns + optimizer switch value if hint is not specified. + + @param thd Pointer to THD object + @param tab Pointer to TABLE object + @param keyno Key number + @param type_arg Hint type + @param optimizer_switch Optimizer switch flag + + @return key hint value if hint is specified, + otherwise optimizer switch value. +*/ +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 tab Pointer to TABLE 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 *table, + opt_hints_enum type_arg, bool fallback_value); + +#ifndef DBUG_OFF +const char *dbug_print_hints(Opt_hints_qb *hint); +#endif + +#endif /* OPT_HINTS_INCLUDED */ diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc new file mode 100644 index 0000000000000..55beaa1b3788e --- /dev/null +++ b/sql/opt_hints_parser.cc @@ -0,0 +1,1063 @@ +/* + Copyright (c) 2024, MariaDB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA +*/ + +#include "opt_hints_parser.h" +#include "sql_error.h" +#include "mysqld_error.h" +#include "sql_class.h" +#include "sql_show.h" +#include "opt_hints.h" + +using Parser= Optimizer_hint_parser; + +extern struct st_opt_hint_info opt_hint_info[]; + +// Forward declaration of functions +void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type, + bool hint_state, + const Lex_ident_sys *qb_name_arg, + const Lex_ident_sys *table_name_arg, + const Lex_ident_sys *key_name_arg, + const Printable_parser_rule *hint); + +Opt_hints_qb *get_qb_hints(Parse_context *pc); + +Opt_hints_qb *find_qb_hints(Parse_context *pc, + const Lex_ident_sys &qb_name, + opt_hints_enum hint_type, + bool hint_state); + +Opt_hints_global *get_global_hints(Parse_context *pc); + +Opt_hints_table *get_table_hints(Parse_context *pc, + const Lex_ident_sys &table_name, + Opt_hints_qb *qb); + +void append_table_name(THD *thd, String *str, const LEX_CSTRING &table_name, + const LEX_CSTRING &qb_name); + +static const Lex_ident_sys null_ident_sys; + +Parse_context::Parse_context(THD *thd, st_select_lex *select) +: thd(thd), + mem_root(thd->mem_root), + select(select) +{} + + +Optimizer_hint_tokenizer::TokenID +Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) +{ + switch (str.length) + { + case 3: + if ("BKA"_Lex_ident_column.streq(str)) return TokenID::keyword_BKA; + if ("BNL"_Lex_ident_column.streq(str)) return TokenID::keyword_BNL; + if ("MRR"_Lex_ident_column.streq(str)) return TokenID::keyword_MRR; + 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; + if ("NO_ICP"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_ICP; + if ("NO_MRR"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_MRR; + break; + + case 7: + if ("QB_NAME"_Lex_ident_column.streq(str)) + return TokenID::keyword_QB_NAME; + break; + + case 8: + if ("SEMIJOIN"_Lex_ident_column.streq(str)) + return TokenID::keyword_SEMIJOIN; + else if ("SUBQUERY"_Lex_ident_column.streq(str)) + return TokenID::keyword_SUBQUERY; + break; + + case 9: + if ("LOOSESCAN"_Lex_ident_column.streq(str)) + return TokenID::keyword_LOOSESCAN; + break; + + case 10: + if ("FIRSTMATCH"_Lex_ident_column.streq(str)) + return TokenID::keyword_FIRSTMATCH; + else if ("INTOEXISTS"_Lex_ident_column.streq(str)) + return TokenID::keyword_INTOEXISTS; + else if ("JOIN_ORDER"_Lex_ident_column.streq(str)) + return TokenID::keyword_JOIN_ORDER; + break; + + case 11: + if ("NO_SEMIJOIN"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_SEMIJOIN; + else if ("DUPSWEEDOUT"_Lex_ident_column.streq(str)) + return TokenID::keyword_DUPSWEEDOUT; + else if ("JOIN_PREFIX"_Lex_ident_column.streq(str)) + return TokenID::keyword_JOIN_PREFIX; + else if ("JOIN_SUFFIX"_Lex_ident_column.streq(str)) + return TokenID::keyword_JOIN_SUFFIX; + break; + + case 15: + if ("MATERIALIZATION"_Lex_ident_column.streq(str)) + return TokenID::keyword_MATERIALIZATION; + break; + + case 16: + if ("JOIN_FIXED_ORDER"_Lex_ident_column.streq(str)) + return TokenID::keyword_JOIN_FIXED_ORDER; + break; + + case 18: + if ("MAX_EXECUTION_TIME"_Lex_ident_column.streq(str)) + return TokenID::keyword_MAX_EXECUTION_TIME; + break; + + case 21: + if ("NO_RANGE_OPTIMIZATION"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_RANGE_OPTIMIZATION; + break; + } + + if (str.length > 0 && (str.str[0] >= '0' && str.str[0] <= '9')) + { + /* + If all characters are digits, qualify the token as a number, + otherwise as an identifier + */ + for(size_t i = 1; i < str.length; i++) + { + if (str.str[i] < '0' || str.str[i] > '9') + return TokenID::tIDENT; + } + return TokenID::tUNSIGNED_NUMBER; + } + return TokenID::tIDENT; +} + + +Optimizer_hint_tokenizer::Token +Optimizer_hint_tokenizer::get_token(CHARSET_INFO *cs) +{ + get_spaces(); + if (eof()) + return Token(Lex_cstring(m_ptr, m_ptr), TokenID::tEOF); + const char head= m_ptr[0]; + if (head == '`' || head=='"') + { + const Token_with_metadata delimited_ident= get_quoted_string(); + /* + Consider only non-empty quoted strings as identifiers. + Table and index names cannot be empty in MariaDB. + Let's also disallow empty query block names. + Note, table aliases can actually be empty: + SELECT ``.a FROM t1 ``; + But let's disallow them in hints for simplicity, to handle + all identifiers in the same way in the hint parser. + */ + if (delimited_ident.length > 2) + return Token(delimited_ident, TokenID::tIDENT); + /* + If the string is empty, "unget" it to have a good + syntax error position in the message text. + The point is to include the empty string in the error message: + EXPLAIN EXTENDED SELECT ... QB_NAME(``) ...; --> + Optimizer hint syntax error near '``) ...' at line 1 + */ + m_ptr-= delimited_ident.length; + return Token(Lex_cstring(m_ptr, m_ptr), TokenID::tNULL); + } + const Token_with_metadata ident= get_ident(); + if (ident.length) + return Token(ident, ident.m_extended_chars ? + TokenID::tIDENT : find_keyword(ident)); + if (!get_char(',')) + return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tCOMMA); + if (!get_char('@')) + return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tAT); + if (!get_char('(')) + return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tLPAREN); + if (!get_char(')')) + return Token(Lex_cstring(m_ptr - 1, 1), TokenID::tRPAREN); + return Token(Lex_cstring(m_ptr, m_ptr), TokenID::tNULL); +} + + +// This method is for debug purposes +bool Parser::parse_token_list(THD *thd) +{ + for ( ; ; m_look_ahead_token= get_token(m_cs)) + { + char tmp[200]; + my_snprintf(tmp, sizeof(tmp), "TOKEN: %d %.*s", + (int) m_look_ahead_token.id(), + (int) m_look_ahead_token.length, + m_look_ahead_token.str); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_ERROR, tmp); + if (m_look_ahead_token.id() == TokenID::tNULL || + m_look_ahead_token.id() == TokenID::tEOF) + break; + } + return true; // Success +} + +void Parser::push_warning_syntax_error(THD *thd, uint start_lineno) +{ + DBUG_ASSERT(m_start <= m_ptr); + DBUG_ASSERT(m_ptr <= m_end); + const char *msg= ER_THD(thd, ER_WARN_OPTIMIZER_HINT_SYNTAX_ERROR); + ErrConvString txt(m_look_ahead_token.str, strlen(m_look_ahead_token.str), + thd->variables.character_set_client); + /* + start_lineno is the line number on which the whole hint started. + Add the line number of the current tokenizer position inside the hint + (in case hints are written in multiple lines). + */ + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_PARSE_ERROR, ER_THD(thd, ER_PARSE_ERROR), + msg, txt.ptr(), start_lineno + lineno()); +} + + +bool +Parser::Table_name_list_container::add(Optimizer_hint_parser *p, + Table_name &&elem) +{ + Table_name *pe= (Table_name*) p->m_thd->alloc(sizeof(*pe)); + if (!pe) + return true; + *pe= std::move(elem); + return push_back(pe, p->m_thd->mem_root); +} + + +bool Parser::Hint_param_table_list_container::add(Optimizer_hint_parser *p, + Hint_param_table &&elem) +{ + Hint_param_table *pe= (Hint_param_table*) p->m_thd->alloc(sizeof(*pe)); + if (!pe) + return true; + *pe= std::move(elem); + return push_back(pe, p->m_thd->mem_root); +} + + +bool Parser::Hint_param_index_list_container::add(Optimizer_hint_parser *p, + Hint_param_index &&elem) +{ + Hint_param_index *pe= (Hint_param_index*) p->m_thd->alloc(sizeof(*pe)); + if (!pe) + return true; + *pe= std::move(elem); + return push_back(pe, p->m_thd->mem_root); +} + + +bool Parser::Hint_list_container::add(Optimizer_hint_parser *p, Hint &&elem) +{ + Hint *pe= new (p->m_thd->mem_root) Hint; + if (!pe) + return true; + *pe= std::move(elem); + return push_back(pe, p->m_thd->mem_root); +} + + +bool Parser::Semijoin_strategy_list_container::add(Optimizer_hint_parser *p, + Semijoin_strategy &&elem) +{ + Semijoin_strategy *pe= (Semijoin_strategy*) p->m_thd->alloc(sizeof(*pe)); + if (!pe) + return true; + *pe= std::move(elem); + return push_back(pe, p->m_thd->mem_root); +} + + +/* + Resolve a parsed table level hint, i.e. set up proper Opt_hint_* structures + which will be used later during query preparation and optimization. + +Return value: +- false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed +- true: critical errors detected, break further hints processing +*/ +bool Parser::Table_level_hint::resolve(Parse_context *pc) const +{ + const Table_level_hint_type &table_level_hint_type= *this; + opt_hints_enum hint_type; + bool hint_state; // ON or OFF + + switch (table_level_hint_type.id()) + { + case TokenID::keyword_BNL: + hint_type= BNL_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_BNL: + hint_type= BNL_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_BKA: + hint_type= BKA_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_BKA: + hint_type= BKA_HINT_ENUM; + hint_state= false; + break; + default: + DBUG_ASSERT(0); + return true; + } + + if (const At_query_block_name_opt_table_name_list & + at_query_block_name_opt_table_name_list= *this) + { + // this is @ query_block_name opt_table_name_list + const Lex_ident_sys qb_name_sys= Query_block_name::to_ident_sys(pc->thd); + Opt_hints_qb *qb= find_qb_hints(pc, qb_name_sys, hint_type, hint_state); + if (qb == NULL) + return false; + if (at_query_block_name_opt_table_name_list.is_empty()) + { + // e.g. BKA(@qb1) + if (qb->set_switch(hint_state, hint_type, false)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &qb_name_sys, nullptr, nullptr, nullptr); + } + return false; + } + else + { + // e.g. BKA(@qb1 t1, t2, t3) + const Opt_table_name_list &opt_table_name_list= *this; + for (const Table_name &table : opt_table_name_list) + { + const Lex_ident_sys table_name_sys= table.to_ident_sys(pc->thd); + Opt_hints_table *tab= get_table_hints(pc, table_name_sys, qb); + if (!tab) + return false; + if (tab->set_switch(hint_state, hint_type, true)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &qb_name_sys, &table_name_sys, nullptr, + nullptr); + } + } + } + } + else + { + // this is opt_hint_param_table_list + const Opt_hint_param_table_list &opt_hint_param_table_list= *this; + Opt_hints_qb *qb= find_qb_hints(pc, Lex_ident_sys(), hint_type, hint_state); + if (qb == NULL) + return false; + if (opt_hint_param_table_list.is_empty()) + { + // e.g. BKA() + if (qb->set_switch(hint_state, hint_type, false)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &null_ident_sys, nullptr, nullptr, nullptr); + } + return false; + } + for (const Hint_param_table &table : opt_hint_param_table_list) + { + // e.g. BKA(t1@qb1, t2@qb2, t3) + const Lex_ident_sys qb_name_sys= table.Query_block_name:: + to_ident_sys(pc->thd); + Opt_hints_qb *qb= find_qb_hints(pc, qb_name_sys, hint_type, hint_state); + if (qb == NULL) + return false; + const Lex_ident_sys table_name_sys= table.Table_name:: + to_ident_sys(pc->thd); + Opt_hints_table *tab= get_table_hints(pc, table_name_sys, qb); + if (!tab) + return false; + if (tab->set_switch(hint_state, hint_type, true)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &qb_name_sys, &table_name_sys, nullptr, nullptr); + } + } + } + return false; +} + +/* + Resolve a parsed index level hint, i.e. set up proper Opt_hint_* structures + which will be used later during query preparation and optimization. + +Return value: +- false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed +- true: critical errors detected, break further hints processing +*/ +bool Parser::Index_level_hint::resolve(Parse_context *pc) const +{ + const Index_level_hint_type &index_level_hint_type= *this; + opt_hints_enum hint_type; + bool hint_state; // ON or OFF + + switch (index_level_hint_type.id()) + { + case TokenID::keyword_NO_ICP: + hint_type= ICP_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_MRR: + hint_type= MRR_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_MRR: + hint_type= MRR_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_NO_RANGE_OPTIMIZATION: + hint_type= NO_RANGE_HINT_ENUM; + hint_state= true; + break; + default: + DBUG_ASSERT(0); + return true; + } + + const Hint_param_table_ext &table_ext= *this; + const Lex_ident_sys qb_name_sys= table_ext.Query_block_name:: + to_ident_sys(pc->thd); + const Lex_ident_sys table_name_sys= table_ext.Table_name:: + to_ident_sys(pc->thd); + Opt_hints_qb *qb= find_qb_hints(pc, qb_name_sys, hint_type, hint_state); + if (qb == NULL) + return false; + + Opt_hints_table *tab= get_table_hints(pc, table_name_sys, qb); + if (!tab) + return false; + + if (is_empty()) // Table level hint + { + if (tab->set_switch(hint_state, hint_type, false)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &qb_name_sys, &table_name_sys, nullptr, nullptr); + } + return false; + } + + for (const Hint_param_index &index_name : *this) + { + const Lex_ident_sys index_name_sys= index_name.to_ident_sys(pc->thd); + Opt_hints_key *idx= (Opt_hints_key *)tab->find_by_name(index_name_sys); + if (!idx) + { + idx= new (pc->thd->mem_root) + Opt_hints_key(index_name_sys, tab, pc->thd->mem_root); + tab->register_child(idx); + } + + if (idx->set_switch(hint_state, hint_type, true)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, + &qb_name_sys, &table_name_sys, &index_name_sys, nullptr); + } + } + return false; +} + + +/* + Resolve a parsed query block name hint, i.e. set up proper Opt_hint_* + structures which will be used later during query preparation and optimization. + +Return value: +- false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed +- true: critical errors detected, break further hints processing +*/ +bool Parser::Qb_name_hint::resolve(Parse_context *pc) const +{ + Opt_hints_qb *qb= pc->select->opt_hints_qb; + + DBUG_ASSERT(qb); + + const Lex_ident_sys qb_name_sys= Query_block_name::to_ident_sys(pc->thd); + + if (qb->get_name().str || // QB name is already set + qb->get_parent()->find_by_name(qb_name_sys)) // Name is already used + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, true, + &qb_name_sys, nullptr, nullptr, nullptr); + return false; + } + + qb->set_name(qb_name_sys); + return false; +} + + +void Parser::Semijoin_hint::fill_strategies_map(Opt_hints_qb *qb) const +{ + // Loop for hints like SEMIJOIN(firstmatch, dupsweedout) + const Hint_param_opt_sj_strategy_list &hint_param_strategy_list= *this; + for (const Semijoin_strategy &strat : hint_param_strategy_list) + add_strategy_to_map(strat.id(), qb); + + // Loop for hints like SEMIJOIN(@qb1 firstmatch, dupsweedout) + const Opt_sj_strategy_list &opt_sj_strategy_list= *this; + for (const Semijoin_strategy &strat : opt_sj_strategy_list) + add_strategy_to_map(strat.id(), qb); +} + + +void Parser::Semijoin_hint::add_strategy_to_map(TokenID token_id, + Opt_hints_qb *qb) const +{ + switch(token_id) + { + case TokenID::keyword_DUPSWEEDOUT: + qb->semijoin_strategies_map |= OPTIMIZER_SWITCH_DUPSWEEDOUT; + break; + case TokenID::keyword_FIRSTMATCH: + qb->semijoin_strategies_map |= OPTIMIZER_SWITCH_FIRSTMATCH; + break; + case TokenID::keyword_LOOSESCAN: + qb->semijoin_strategies_map |= OPTIMIZER_SWITCH_LOOSE_SCAN; + break; + case TokenID::keyword_MATERIALIZATION: + qb->semijoin_strategies_map |= OPTIMIZER_SWITCH_MATERIALIZATION; + break; + default: + DBUG_ASSERT(0); + } +} + +/* + Resolve a parsed semijoin hint, i.e. set up proper Opt_hint_* structures + which will be used later during query preparation and optimization. + +Return value: +- false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed +- true: critical errors detected, break further hints processing +*/ +bool Parser::Semijoin_hint::resolve(Parse_context *pc) const +{ + const Semijoin_hint_type &semijoin_hint_type= *this; + bool hint_state; // true - SEMIJOIN(), false - NO_SEMIJOIN() + if (semijoin_hint_type.id() == TokenID::keyword_SEMIJOIN) + hint_state= true; + else + hint_state= false; + Opt_hints_qb *qb; + if (const At_query_block_name_opt_strategy_list & + at_query_block_name_opt_strategy_list __attribute__((unused)) = *this) + { + /* + This is @ query_block_name opt_strategy_list, + e.g. SEMIJOIN(@qb1) or SEMIJOIN(@qb1 firstmatch, loosescan) + */ + const Lex_ident_sys qb_name= Query_block_name::to_ident_sys(pc->thd); + qb= resolve_for_qb_name(pc, hint_state, &qb_name); + } + else + { + // This is opt_strategy_list, e.g. SEMIJOIN(loosescan, dupsweedout) + Lex_ident_sys empty_qb_name= Lex_ident_sys(); + qb= resolve_for_qb_name(pc, hint_state, &empty_qb_name); + } + if (qb) + qb->semijoin_hint= this; + return false; +} + + +/* + Helper function to be called by Semijoin_hint::resolve(). + +Return value: +- pointer to Opt_hints_qb if the hint was resolved successfully +- NULL if the hint was ignored +*/ +Opt_hints_qb* Parser::Semijoin_hint:: + resolve_for_qb_name(Parse_context *pc, bool hint_state, + const Lex_ident_sys *qb_name) const +{ + Opt_hints_qb *qb= find_qb_hints(pc, *qb_name, SEMIJOIN_HINT_ENUM, hint_state); + if (!qb) + return nullptr; + if (qb->subquery_hint) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, SEMIJOIN_HINT_ENUM, + hint_state, qb_name, nullptr, nullptr, this); + return nullptr; + } + if (qb->set_switch(hint_state, SEMIJOIN_HINT_ENUM, false)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, SEMIJOIN_HINT_ENUM, + hint_state, qb_name, nullptr, nullptr, this); + return nullptr; + } + fill_strategies_map(qb); + return qb; +} + + +void Parser::Semijoin_hint::append_args(THD *thd, String *str) const +{ + // Loop for hints without query block name, e.g. SEMIJOIN(firstmatch, dupsweedout) + const Hint_param_opt_sj_strategy_list &hint_param_strategy_list= *this; + uint32 len_before= str->length(); + for (const Semijoin_strategy &strat : hint_param_strategy_list) + { + if (str->length() > len_before) + str->append(STRING_WITH_LEN(", ")); + append_strategy_name(strat.id(), str); + } + + // Loop for hints with query block name, e.g. SEMIJOIN(@qb1 firstmatch, dupsweedout) + const Opt_sj_strategy_list &opt_sj_strategy_list= *this; + for (const Semijoin_strategy &strat : opt_sj_strategy_list) + { + if (str->length() > len_before) + str->append(STRING_WITH_LEN(", ")); + append_strategy_name(strat.id(), str); + } +} + + +void Parser::Semijoin_hint:: + append_strategy_name(TokenID token_id, String *str) const +{ + switch(token_id) + { + case TokenID::keyword_DUPSWEEDOUT: + str->append(STRING_WITH_LEN("DUPSWEEDOUT")); + break; + case TokenID::keyword_FIRSTMATCH: + str->append(STRING_WITH_LEN("FIRSTMATCH")); + break; + case TokenID::keyword_LOOSESCAN: + str->append(STRING_WITH_LEN("LOOSESCAN")); + break; + case TokenID::keyword_MATERIALIZATION: + str->append(STRING_WITH_LEN("MATERIALIZATION")); + break; + default: + DBUG_ASSERT(0); + } +} + +/* + Resolve a parsed subquery hint, i.e. set up proper Opt_hint_* structures + which will be used later during query preparation and optimization. + +Return value: +- false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed +- true: critical errors detected, break further hints processing +*/ +bool Parser::Subquery_hint::resolve(Parse_context *pc) const +{ + Opt_hints_qb *qb; + if (const At_query_block_name_subquery_strategy & + at_query_block_name_subquery_strategy= *this) + { + /* + This is @ query_block_name subquery_strategy, + e.g. SUBQUERY(@qb1 INTOEXISTS) + */ + const Lex_ident_sys qb_name= Query_block_name::to_ident_sys(pc->thd); + const Subquery_strategy &strat= at_query_block_name_subquery_strategy; + qb= resolve_for_qb_name(pc, strat.id(), &qb_name); + } + else + { + // This is subquery_strategy, e.g. SUBQUERY(MATERIALIZATION) + Lex_ident_sys empty_qb_name= Lex_ident_sys(); + const Hint_param_subquery_strategy &strat= *this; + qb= resolve_for_qb_name(pc, strat.id(), &empty_qb_name); + } + if (qb) + qb->subquery_hint= this; + return false; +} + + +/* + Helper function to be called by Subquery_hint::resolve(). + +Return value: +- pointer to Opt_hints_qb if the hint was resolved successfully +- NULL if the hint was ignored +*/ +Opt_hints_qb* Parser::Subquery_hint:: + resolve_for_qb_name(Parse_context *pc, TokenID token_id, + const Lex_ident_sys *qb_name) const +{ + Opt_hints_qb *qb= find_qb_hints(pc, *qb_name, SUBQUERY_HINT_ENUM, true); + if (!qb) + return nullptr; + if (qb->semijoin_hint) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, SUBQUERY_HINT_ENUM, + true, qb_name, nullptr, nullptr, this); + return nullptr; + } + if (qb->set_switch(true, SUBQUERY_HINT_ENUM, false)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, SUBQUERY_HINT_ENUM, + true, qb_name, nullptr, nullptr, this); + return nullptr; + } + set_subquery_strategy(token_id, qb); + return qb; +} + + +void Parser::Subquery_hint::set_subquery_strategy(TokenID token_id, + Opt_hints_qb *qb) const +{ + switch(token_id) + { + case TokenID::keyword_INTOEXISTS: + qb->subquery_strategy= SUBS_IN_TO_EXISTS; + break; + case TokenID::keyword_MATERIALIZATION: + qb->subquery_strategy= SUBS_MATERIALIZATION; + break; + default: + DBUG_ASSERT(0); + } +} + + +void Parser::Subquery_hint::append_args(THD *thd, String *str) const +{ + TokenID token_id; + if (const At_query_block_name_subquery_strategy & + at_query_block_name_subquery_strategy= *this) + { + const Subquery_strategy &strat= at_query_block_name_subquery_strategy; + token_id= strat.id(); + } + else + { + const Hint_param_subquery_strategy& hint_param_strat= *this; + token_id= hint_param_strat.id(); + } + + switch(token_id) + { + case TokenID::keyword_INTOEXISTS: + str->append(STRING_WITH_LEN("INTOEXISTS")); + break; + case TokenID::keyword_MATERIALIZATION: + str->append(STRING_WITH_LEN("MATERIALIZATION")); + break; + default: + DBUG_ASSERT(0); + } +} + +/* + This is the first step of MAX_EXECUTION_TIME() hint resolution. It is invoked + during the parsing phase, but at this stage some essential information is + not yet available, preventing a full validation of the hint. + Particularly, the type of SQL command, mark of a stored procedure execution + or whether SELECT_LEX is not top-level (i.e., a subquery) are not yet set. + However, some basic checks like the numeric argument validation or hint + duplication check can still be performed. + The second step of hint validation is performed during the JOIN preparation + phase, within Opt_hints_global::resolve(). By this point, all necessary + information is up-to-date, allowing the hint to be fully resolved +*/ +bool Parser::Max_execution_time_hint::resolve(Parse_context *pc) const +{ + const Unsigned_Number& hint_arg= *this; + const ULonglong_null time_ms= hint_arg.get_ulonglong(); + + if (time_ms.is_null() || time_ms.value() == 0 || time_ms.value() > INT_MAX32) + { + print_warn(pc->thd, ER_BAD_OPTION_VALUE, MAX_EXEC_TIME_HINT_ENUM, + true, NULL, NULL, NULL, this); + return false; + } + + Opt_hints_global *global_hint= get_global_hints(pc); + if (global_hint->is_specified(MAX_EXEC_TIME_HINT_ENUM)) + { + // Hint duplication: /*+ MAX_EXECUTION_TIME ... MAX_EXECUTION_TIME */ + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, MAX_EXEC_TIME_HINT_ENUM, true, + NULL, NULL, NULL, this); + return false; + } + + global_hint->set_switch(true, MAX_EXEC_TIME_HINT_ENUM, false); + global_hint->max_exec_time_hint= this; + global_hint->max_exec_time_select_lex= pc->select; + return false; +} + + +void Parser::Join_order_hint::append_args(THD *thd, String *str) const +{ + bool first_table_name= true; + for (const Parser::Table_name_and_Qb& tbl : table_names) + { + if (!first_table_name) + str->append(STRING_WITH_LEN(",")); + append_table_name(thd, str, tbl.table_name, tbl.qb_name); + first_table_name= false; + } +} + +/* + Resolve a parsed join order hint, i.e. set up proper Opt_hint_* structures + which will be used later during query preparation and optimization. + + Return value: + - false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + - true: critical errors detected, break further hints processing +*/ +bool Parser::Join_order_hint::resolve(Parse_context *pc) +{ + const Join_order_hint_type &join_order_hint_type= *this; + + switch (join_order_hint_type.id()) + { + case TokenID::keyword_JOIN_FIXED_ORDER: + hint_type= JOIN_FIXED_ORDER_HINT_ENUM; + break; + case TokenID::keyword_JOIN_ORDER: + hint_type= JOIN_ORDER_HINT_ENUM; + break; + case TokenID::keyword_JOIN_PREFIX: + hint_type= JOIN_PREFIX_HINT_ENUM; + break; + case TokenID::keyword_JOIN_SUFFIX: + hint_type= JOIN_SUFFIX_HINT_ENUM; + break; + default: + DBUG_ASSERT(0); + return true; + } + + Opt_hints_qb *qb= nullptr; + Lex_ident_sys qb_name; + if (const At_query_block_name_opt_table_name_list &at_qb_tab_list= *this) + { + // this is @ query_block_name opt_table_name_list + qb_name= Query_block_name::to_ident_sys(pc->thd); + qb= find_qb_hints(pc, qb_name, hint_type, true); + // Compose `tables_names` list for warnings and final hints resolving + const Opt_table_name_list &opt_table_name_list= at_qb_tab_list; + for (const Table_name &table : opt_table_name_list) + { + Parser::Table_name_and_Qb *tbl_qb= new (pc->mem_root) + Parser::Table_name_and_Qb(table.to_ident_sys(pc->thd), Lex_ident_sys()); + if (!tbl_qb) + return true; + table_names.push_back(tbl_qb, pc->mem_root); + } + } + else + { + // this is opt_hint_param_table_list, query block name is not specified + qb= find_qb_hints(pc, Lex_ident_sys(), hint_type, true); + const Opt_hint_param_table_list &opt_hint_param_table_list= *this; + for (const Hint_param_table &table : opt_hint_param_table_list) + { + // e.g. JOIN_ORDER(t1@qb1, t2@qb2, t3) + Parser::Table_name_and_Qb *tbl_qb= + new (pc->mem_root) Parser::Table_name_and_Qb( + table.Table_name::to_ident_sys(pc->thd), + table.Query_block_name::to_ident_sys(pc->thd)); + table_names.push_back(tbl_qb, pc->mem_root); + } + } + + if (qb == nullptr) + return false; + + if ((hint_type != JOIN_FIXED_ORDER_HINT_ENUM && table_names.is_empty()) || + (hint_type == JOIN_FIXED_ORDER_HINT_ENUM && !table_names.is_empty())) + { + /* + Skipping table name(s) only allowed and required for the + JOIN_FIXED_ORDER hint and is not allowed for other hint types + */ + print_warn(pc->thd, ER_WARN_MALFORMED_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + + if (hint_type == JOIN_FIXED_ORDER_HINT_ENUM) + { + /* + This is JOIN_ORDER_FIXED() or JOIN_ORDER_FIXED(@qb1) + There can be only one JOIN_ORDER_FIXED hint in a query block, + other hints are not allowed in this case + */ + if (qb->has_join_order_hints()|| qb->join_fixed_order) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + qb->join_fixed_order= this; + qb->set_switch(true, hint_type, false); + pc->select->options |= SELECT_STRAIGHT_JOIN; + return false; + } + + // Finished with processing of JOIN_FIXED_ORDER() + DBUG_ASSERT(hint_type != JOIN_FIXED_ORDER_HINT_ENUM); + /* + Hints except JOIN_ORDER() must not duplicate. If there is JOIN_ORDER_FIXED() + already, then other hints are not allowed for this query block + */ + if ((qb->get_switch(hint_type) && hint_type != JOIN_ORDER_HINT_ENUM) || + qb->join_fixed_order) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + + switch(hint_type) + { + case JOIN_PREFIX_HINT_ENUM: + if (qb->join_prefix || qb->add_join_order_hint(this)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + qb->join_prefix= this; + qb->set_switch(true, JOIN_PREFIX_HINT_ENUM, false); + break; + case JOIN_SUFFIX_HINT_ENUM: + if (qb->join_suffix || qb->add_join_order_hint(this)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + qb->join_suffix= this; + qb->set_switch(true, JOIN_SUFFIX_HINT_ENUM, false); + break; + case JOIN_ORDER_HINT_ENUM: + // Multiple JOIN_ORDER() hints are allowed + if (qb->add_join_order_hint(this)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, true, + &qb_name, nullptr, nullptr, this); + return false; + } + qb->set_switch(true, JOIN_ORDER_HINT_ENUM, false); + break; + default: + DBUG_ASSERT(0); + } + return false; +} + + +void Parser::Max_execution_time_hint::append_args(THD *thd, String *str) const +{ + const Unsigned_Number& hint_arg= *this; + str->append(ErrConvString(hint_arg.str, hint_arg.length, + &my_charset_latin1).lex_cstring()); +} + + +ulonglong Parser::Max_execution_time_hint::get_milliseconds() const +{ + const Unsigned_Number& hint_arg= *this; + return hint_arg.get_ulonglong().value(); +} + + +bool Parser::Hint_list::resolve(Parse_context *pc) const +{ + if (pc->thd->lex->create_view) + { + // we're creating or modifying a view, hints are not allowed here + push_warning(pc->thd, Sql_condition::WARN_LEVEL_WARN, + ER_HINTS_INSIDE_VIEWS_NOT_SUPPORTED, + ER_THD(pc->thd, ER_HINTS_INSIDE_VIEWS_NOT_SUPPORTED)); + return false; + } + + if (!get_qb_hints(pc)) + return true; + + for (Hint_list::iterator li= this->begin(); li != this->end(); ++li) + { + Parser::Hint &hint= *li; + if (const Table_level_hint &table_hint= hint) + { + if (table_hint.resolve(pc)) + return true; + } + else if (const Index_level_hint &index_hint= hint) + { + if (index_hint.resolve(pc)) + return true; + } + else if (const Qb_name_hint &qb_hint= hint) + { + if (qb_hint.resolve(pc)) + return true; + } + else if (const Max_execution_time_hint &max_hint= hint) + { + if (max_hint.resolve(pc)) + return true; + } + else if (const Semijoin_hint &sj_hint= hint) + { + if (sj_hint.resolve(pc)) + return true; + } + else if (const Subquery_hint &subq_hint= hint) + { + if (subq_hint.resolve(pc)) + return true; + } + else if (Join_order_hint &join_order_hint= hint) + { + if (join_order_hint.resolve(pc)) + return true; + } + else { + DBUG_ASSERT(0); + } + } + return false; +} diff --git a/sql/opt_hints_parser.h b/sql/opt_hints_parser.h new file mode 100644 index 0000000000000..0a8e13f53cbe4 --- /dev/null +++ b/sql/opt_hints_parser.h @@ -0,0 +1,984 @@ +#ifndef OPT_HINTS_PARSER_H +#define OPT_HINTS_PARSER_H +/* + Copyright (c) 2024, MariaDB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA +*/ + +#include "lex_ident_sys.h" +#include "simple_tokenizer.h" +#include "sql_list.h" +#include "sql_string.h" +#include "sql_type_int.h" +#include "simple_parser.h" + +class st_select_lex; +class Opt_hints_qb; + +/** + Hint types, MAX_HINT_ENUM should be always last. + This enum should be synchronized with opt_hint_info + array(see opt_hints.cc). +*/ +enum opt_hints_enum +{ + BKA_HINT_ENUM= 0, + BNL_HINT_ENUM, + ICP_HINT_ENUM, + MRR_HINT_ENUM, + NO_RANGE_HINT_ENUM, + QB_NAME_HINT_ENUM, + MAX_EXEC_TIME_HINT_ENUM, + SEMIJOIN_HINT_ENUM, + SUBQUERY_HINT_ENUM, + JOIN_PREFIX_HINT_ENUM, + JOIN_SUFFIX_HINT_ENUM, + JOIN_ORDER_HINT_ENUM, + JOIN_FIXED_ORDER_HINT_ENUM, + MAX_HINT_ENUM // This one must be the last in the list +}; + +/** + Environment data for the name resolution phase +*/ +struct Parse_context { + THD * const thd; ///< Current thread handler + MEM_ROOT *mem_root; ///< Current MEM_ROOT + st_select_lex * select; ///< Current SELECT_LEX object + + Parse_context(THD *thd, st_select_lex *select); +}; + + +class Optimizer_hint_tokenizer: public Extended_string_tokenizer +{ +public: + Optimizer_hint_tokenizer(CHARSET_INFO *cs, const LEX_CSTRING &hint) + :Extended_string_tokenizer(cs, hint) + { } + + // Let's use "enum class" to easier distinguish token IDs vs rule names + enum class TokenID + { + // Special purpose tokens: + tNULL= 0, // returned if the tokenizer failed to detect a token + // also used if the parser failed to parse a token + tEMPTY= 1, // returned on empty optional constructs in a grammar like: + // rule ::= [ rule1 ] + // when rule1 does not present in the input. + tEOF= 2, // returned when the end of input is reached + + // One character tokens + tCOMMA= ',', + tAT= '@', + tLPAREN= '(', + tRPAREN= ')', + // Other token types + tIDENT= 'i', + tUNSIGNED_NUMBER= 'n', + + // Keywords + keyword_BKA = 256, // Value must be greater than any of the above + keyword_BNL, + keyword_NO_BKA, + keyword_NO_BNL, + keyword_NO_ICP, + keyword_NO_MRR, + keyword_NO_RANGE_OPTIMIZATION, + keyword_MRR, + keyword_QB_NAME, + keyword_MAX_EXECUTION_TIME, + keyword_SEMIJOIN, + keyword_NO_SEMIJOIN, + keyword_SUBQUERY, + keyword_MATERIALIZATION, + keyword_FIRSTMATCH, + keyword_LOOSESCAN, + keyword_DUPSWEEDOUT, + keyword_INTOEXISTS, + keyword_JOIN_PREFIX, + keyword_JOIN_SUFFIX, + keyword_JOIN_ORDER, + keyword_JOIN_FIXED_ORDER + }; + + class Token: public Lex_cstring + { + protected: + TokenID m_id; + public: + Token() + :Lex_cstring(), m_id(TokenID::tNULL) + { } + Token(const LEX_CSTRING &str, TokenID id) + :Lex_cstring(str), m_id(id) + { } + TokenID id() const { return m_id; } + static Token empty(const char *pos) + { + return Token(Lex_cstring(pos, pos), TokenID::tEMPTY); + } + operator bool() const + { + return m_id != TokenID::tNULL; + } + }; + +protected: + Token get_token(CHARSET_INFO *cs); + static TokenID find_keyword(const LEX_CSTRING &str); +}; + + +class Printable_parser_rule +{ +public: + virtual void append_args(THD *thd, String *str) const = 0; + virtual ~Printable_parser_rule() {} +}; + + +class Optimizer_hint_parser: public Optimizer_hint_tokenizer, + public Parser_templates +{ +private: + Token m_look_ahead_token; + THD *m_thd; + const char *m_start; + bool m_syntax_error; + bool m_fatal_error; +public: + Optimizer_hint_parser(THD *thd, CHARSET_INFO *cs, const LEX_CSTRING &hint) + :Optimizer_hint_tokenizer(cs, hint), + m_look_ahead_token(get_token(cs)), + m_thd(thd), + m_start(hint.str), + m_syntax_error(false), + m_fatal_error(false) + { } + bool set_syntax_error() + { + m_syntax_error= true; + return false; + } + bool set_fatal_error() + { + m_fatal_error= true; + return false; + } + // Calculate the line number inside the whole hint + uint lineno(const char *ptr) const + { + DBUG_ASSERT(m_start <= ptr); + DBUG_ASSERT(ptr <= m_end); + uint lineno= 0; + for ( ; ptr >= m_start; ptr--) + { + if (*ptr == '\n') + lineno++; + } + return lineno; + } + uint lineno() const + { + return lineno(m_ptr); + } + + TokenID look_ahead_token_id() const + { + return is_error() ? TokenID::tNULL : m_look_ahead_token.id(); + } + /* + Return an empty token at the position of the current + look ahead token with a zero length. Used for optional grammar constructs. + + For example, if the grammar is "rule ::= ruleA [ruleB] ruleC" + and the input is "A C", then: + - the optional rule "ruleB" will point to the input position "C" + with a zero length + - while the rule "ruleC" will point to the same input position "C" + with a non-zero length + */ + Token empty_token() const + { + return Token::empty(m_look_ahead_token.str); + } + static Token null_token() + { + return Token(); + } + + /* + Return the current look ahead token and scan the next one + */ + Token shift() + { + DBUG_ASSERT(!is_error()); + const Token res= m_look_ahead_token; + m_look_ahead_token= get_token(m_cs); + return res; + } + +public: + /* + Return the current look ahead token if it matches the given ID + and scan the next one. + */ + Token token(TokenID id) + { + if (m_look_ahead_token.id() != id || is_error()) + return Token(); + return shift(); + } + + bool is_error() const + { + return m_syntax_error || m_fatal_error; + } + bool is_syntax_error() const + { + return m_syntax_error; + } + bool is_fatal_error() const + { + return m_fatal_error; + } + + bool parse_token_list(THD *thd); // For debug purposes + + void push_warning_syntax_error(THD *thd, uint lineno); + + +private: + + using Parser= Optimizer_hint_parser; // for a shorter notation + + // Rules consisting of a single token + + class TokenAT: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class TokenEOF: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class Keyword_QB_NAME: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class Keyword_MAX_EXECUTION_TIME: + public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class Keyword_SUBQUERY: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class Identifier: public TOKEN + { + public: + using TOKEN::TOKEN; + Lex_ident_cli_st to_ident_cli() const + { + Lex_ident_cli_st cli; + if (length >= 2 && (str[0] == '`' || str[0] == '"')) + return cli.set_ident_quoted(str + 1, length - 2, true, str[0]); + return cli.set_ident(str, length, true); + } + Lex_ident_sys to_ident_sys(THD *thd) const + { + const Lex_ident_cli_st cli= to_ident_cli(); + return Lex_ident_sys(thd, &cli); + } + }; + + class Unsigned_Number: public TOKEN + { + public: + using TOKEN::TOKEN; + + /* + Converts token string to a non-negative number ( >=0 ). + Returns the converted number if the conversion succeeds. + Returns non-NULL ULonglong_null value on successful string conversion and + NULL ULonglong_null if the conversion failed or the number is negative + */ + ULonglong_null get_ulonglong() const + { + int error; + char *end= const_cast(str + length); + longlong n= my_strtoll10(str, &end, &error); + if (error != 0 || end != str + length || n < 0) + return ULonglong_null(0, true); + return ULonglong_null(n, false); + } + }; + + class LParen: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + class RParen: public TOKEN + { + public: + using TOKEN::TOKEN; + }; + + + // Rules consisting of multiple choices of tokens + + // table_level_hint_type ::= BKA | BNL | NO_BKA | NO_BNL + class Table_level_hint_type_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_BKA || + id == TokenID::keyword_BNL || + id == TokenID::keyword_NO_BKA || + id == TokenID::keyword_NO_BNL; + } + }; + class Table_level_hint_type: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + + // index_level_hint_type ::= MRR | NO_RANGE_OPTIMIZATION | NO_ICP | NO_MRR + class Index_level_hint_type_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_MRR || + id == TokenID::keyword_NO_RANGE_OPTIMIZATION || + id == TokenID::keyword_NO_ICP || + id == TokenID::keyword_NO_MRR; + } + }; + class Index_level_hint_type: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + // Identifiers of various kinds + + // query_block_name ::= identifier + class Query_block_name: public Identifier + { + public: + using Identifier::Identifier; + }; + + // table_name ::= identifier + class Table_name: public Identifier + { + public: + using Identifier::Identifier; + }; + + // hint_param_index ::= identifier + class Hint_param_index: public Identifier + { + public: + using Identifier::Identifier; + }; + + + // More complex rules + + /* + at_query_block_name ::= @ query_block_name + */ + class At_query_block_name: public AND2 + { + public: + using AND2::AND2; + using AND2::operator=; + }; + + /* + opt_qb_name ::= [ @ query_block_name ] + */ + class Opt_qb_name: public OPT + { + public: + using OPT::OPT; + }; + + /* + hint_param_table ::= table_name opt_qb_name + */ + class Hint_param_table: public AND2 + { + public: + using AND2::AND2; + }; + + + /* + hint_param_table_list ::= hint_param_table [ {, hint_param_table}... ] + opt_hint_param_table_list ::= [ hint_param_table_list ] + */ + class Hint_param_table_list_container: public List + { + public: + Hint_param_table_list_container() + { } + bool add(Optimizer_hint_parser *p, Hint_param_table &&table); + size_t count() const { return elements; } + }; + + class Opt_hint_param_table_list: public LIST + { + using LIST::LIST; + }; + + /* + table_name_list ::= table_name [ {, table_name }... ] + opt_table_name_list ::= [ table_name_list ] + */ + class Table_name_list_container: public List + { + public: + Table_name_list_container() + { } + bool add(Optimizer_hint_parser *p, Table_name &&table); + size_t count() const { return elements; } + }; + + class Opt_table_name_list: public LIST + { + public: + using LIST::LIST; + }; + + + /* + hint_param_index_list ::= hint_param_index [ {, hint_param_index }...] + opt_hint_param_index_list ::= [ hint_param_index_list ] + */ + class Hint_param_index_list_container: public List + { + public: + Hint_param_index_list_container() + { } + bool add(Optimizer_hint_parser *p, Hint_param_index &&table); + size_t count() const { return elements; } + }; + + class Opt_hint_param_index_list: public LIST + { + public: + using LIST::LIST; + }; + + + /* + hint_param_table_ext ::= hint_param_table + | @ query_block_name table_name + */ + class At_query_block_name_table_name: public AND2 + { + public: + using AND2::AND2; + }; + + class Hint_param_table_ext_container: public Query_block_name, + public Table_name + { + public: + Hint_param_table_ext_container() + { } + Hint_param_table_ext_container(const Hint_param_table &hint_param_table) + :Query_block_name(hint_param_table), Table_name(hint_param_table) + { } + Hint_param_table_ext_container(const At_query_block_name_table_name &qbt) + :Query_block_name(qbt), Table_name(qbt) + { } + operator bool() const + { + return Query_block_name::operator bool() && Table_name::operator bool(); + } + }; + + class Hint_param_table_ext: public OR2C + { + public: + using OR2C::OR2C; + }; + + + /* + at_query_block_name_opt_table_name_list ::= + @ query_block_name opt_table_name_list + */ + class At_query_block_name_opt_table_name_list: public AND2< + Parser, + At_query_block_name, + Opt_table_name_list> + { + public: + using AND2::AND2; + }; + + + /* + table_level_hint_body: @ query_block_name opt_table_name_list + | opt_hint_param_table_list + */ + class Table_level_hint_body: public OR2< + Parser, + At_query_block_name_opt_table_name_list, + Opt_hint_param_table_list> + { + public: + using OR2::OR2; + }; + + // table_level_hint ::= table_level_hint_type ( table_level_hint_body ) + class Table_level_hint: public AND4 + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + }; + + + // index_level_hint_body ::= hint_param_table_ext opt_hint_param_index_list + class Index_level_hint_body: public AND2 + { + public: + using AND2::AND2; + }; + + + // index_level_hint ::= index_level_hint_type ( index_level_hint_body ) + class Index_level_hint: public AND4 + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + }; + + + // qb_name_hint ::= QB_NAME ( query_block_name ) + class Qb_name_hint: public AND4 + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + }; + + +public: + // max_execution_time_hint ::= MAX_EXECUTION_TIME ( milliseconds ) + class Max_execution_time_hint: public AND4, + public Printable_parser_rule + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + void append_args(THD *thd, String *str) const override; + ulonglong get_milliseconds() const; + }; + + // semijoin_hint_type ::= SEMIJOIN | NO_SEMIJOIN + class Semijoin_hint_type_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_SEMIJOIN || + id == TokenID::keyword_NO_SEMIJOIN; + } + }; + class Semijoin_hint_type: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + + // semijoin_strategy ::= MATERIALIZATION | FIRSTMATCH | LOOSESCAN | DUPSWEEDOUT + class Semijoin_strategy_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_MATERIALIZATION || + id == TokenID::keyword_FIRSTMATCH || + id == TokenID::keyword_LOOSESCAN || + id == TokenID::keyword_DUPSWEEDOUT; + } + }; + + class Semijoin_strategy: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + + /* + strategy_list ::= strategy_name [ {, strategy_name }... ] + opt_strategy_list ::= [ strategy_list ] + */ + class Semijoin_strategy_list_container: public List + { + public: + Semijoin_strategy_list_container() + { } + bool add(Optimizer_hint_parser *p, Semijoin_strategy &&strategy); + size_t count() const { return elements; } + }; + + class Opt_sj_strategy_list: public LIST + { + public: + using LIST::LIST; + }; + + + class Hint_param_opt_sj_strategy_list: public LIST + { + public: + using LIST::LIST; + }; + + + /* + at_query_block_name_opt_strategies_list ::= + @ query_block_name opt_strategies_list + */ + class At_query_block_name_opt_strategy_list: public AND2< + Parser, + At_query_block_name, + Opt_sj_strategy_list> + { + public: + using AND2::AND2; + }; + + /* + semijoin_hint_body: @ query_block_name opt_sj_strategy_list + | opt_sj_strategy_list + */ + class Semijoin_hint_body: public OR2 + { + public: + using OR2::OR2; + }; + +public: + /* + semijoin_hint ::= semijoin_hint_type ( semijoin_hint_body ) + */ + class Semijoin_hint: public AND4, + public Printable_parser_rule + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + void append_args(THD *thd, String *str) const override; + + private: + Opt_hints_qb* resolve_for_qb_name(Parse_context *pc, bool hint_state, + const Lex_ident_sys *qb_name) const; + void fill_strategies_map(Opt_hints_qb *qb) const; + void add_strategy_to_map(TokenID token_id, Opt_hints_qb *qb) const; + void append_strategy_name(TokenID token_id, String *str) const; + }; + +private: + // subquery_strategy ::= MATERIALIZATION | INTOEXISTS + class Subquery_strategy_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_MATERIALIZATION || + id == TokenID::keyword_INTOEXISTS; + } + }; + + class Subquery_strategy: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + class Hint_param_subquery_strategy: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + + /* + at_query_block_name_subquery_strategy ::= + @ query_block_name subquery_strategy + */ + class At_query_block_name_subquery_strategy: public AND2< + Parser, + At_query_block_name, + Subquery_strategy> + { + public: + using AND2::AND2; + }; + + + /* + subquery_hint_body: @ query_block_name subquery_strategy + | subquery_strategy + */ + class Subquery_hint_body: public OR2 + { + public: + using OR2::OR2; + }; + + +public: + // subquery_hint ::= SUBQUERY( subquery_hint_body ) + class Subquery_hint: public AND4, + public Printable_parser_rule + { + public: + using AND4::AND4; + + bool resolve(Parse_context *pc) const; + void append_args(THD *thd, String *str) const override; + + private: + void set_subquery_strategy(TokenID token_id, Opt_hints_qb *qb) const; + Opt_hints_qb* resolve_for_qb_name(Parse_context *pc, TokenID token_id, + const Lex_ident_sys *qb_name) const; + }; + + + /* + join_order_hint_type ::= JOIN_FIXED_ORDER + | JOIN_ORDER + | JOIN_PREFIX + | JOIN_SUFFIX + */ + class Join_order_hint_type_cond + { + public: + static bool allowed_token_id(TokenID id) + { + return id == TokenID::keyword_JOIN_FIXED_ORDER || + id == TokenID::keyword_JOIN_ORDER || + id == TokenID::keyword_JOIN_PREFIX || + id == TokenID::keyword_JOIN_SUFFIX; + } + }; + class Join_order_hint_type: public TokenChoice + { + public: + using TokenChoice::TokenChoice; + }; + + /* + Struct representing table names listed in optimizer hints bodies. + They may optionally include query block names, for example: + t1, t2@qb1, t3, t4@qb5 + */ + struct Table_name_and_Qb: public Sql_alloc + { + Lex_ident_sys table_name; + Lex_ident_sys qb_name; // may be empty + + Table_name_and_Qb(const Lex_ident_sys& tbl, const Lex_ident_sys& qb) : + table_name(tbl), qb_name(qb) + {} + + Table_name_and_Qb(Lex_ident_sys&& tbl, Lex_ident_sys&& qb) : + table_name(std::move(tbl)), qb_name(std::move(qb)) + {} + }; + + /* + join_order_hint ::= join_order_hint_type ( table_level_hint_body ) + */ + class Join_order_hint: public AND4, + public Printable_parser_rule + { + public: + using AND4::AND4; + + opt_hints_enum hint_type= MAX_HINT_ENUM; + + bool resolve(Parse_context *pc); + + void append_args(THD *thd, String *str) const override; + + /* + Table names (optionally augmented with query block names) listed in + the hint body. + */ + List table_names; + }; + + + /* + hint ::= index_level_hint + | table_level_hint + | qb_name_hint + | max_execution_time_hint + | semijoin_hint + | subquery_hint + | join_order_hint + */ + class Hint: public OR7 + { + public: + using OR7::OR7; + }; + +private: + // hint_list ::= hint [ hint... ] + class Hint_list_container: public List + { + public: + Hint_list_container() + { } + bool add(Optimizer_hint_parser *p, Hint &&hint); + size_t count() const { return elements; } + }; + + + class Hint_list: public LIST + { + public: + using LIST::LIST; + + bool resolve(Parse_context *pc) const; + }; + +public: + /* + The main rule: + hints ::= hint_list EOF + */ + class Hints: public AND2 + { + public: + using AND2::AND2; + }; +}; + + +/* + These wrapper class is needed to use a forward declarations in sql_lex.h + instead of including the entire opt_hints_parser.h. + (forward declarations of qualified nested classes are not possible in C++) +*/ +class Optimizer_hint_parser_output: public Optimizer_hint_parser::Hints +{ +public: + using Hints::Hints; +}; + +#endif // OPT_HINTS_PARSER diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index f98adee4f7ccc..57d2063415481 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -18,6 +18,7 @@ #include "sql_select.h" #include "sql_test.h" #include "opt_trace.h" +#include "opt_hints.h" /**************************************************************************** * Index Condition Pushdown code starts @@ -336,7 +337,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno) re-evaluated when WL#6061 is implemented. */ if ((tab->table->key_info[keyno].index_flags & HA_DO_INDEX_COND_PUSHDOWN) && - optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) && + hint_key_state(tab->join->thd, tab->table, keyno, ICP_HINT_ENUM, + OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) && tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI && tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI && tab->type != JT_CONST && tab->type != JT_SYSTEM && diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 1ee4cdeb5d3b6..d0f3b8c614b05 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -116,6 +116,7 @@ #include "sql_statistics.h" #include "uniques.h" #include "my_json_writer.h" +#include "opt_hints.h" #ifndef EXTRA_DEBUG #define test_rb_tree(A,B) {} @@ -2854,6 +2855,13 @@ SQL_SELECT::test_quick_select(THD *thd, add("cause", "not applicable"); continue; } + if (hint_key_state(thd, head, idx, NO_RANGE_HINT_ENUM, 0)) + { + trace_idx_details. + add("usable", false). + add("cause", "no_range_optimization hint"); + continue; + } if (key_info->algorithm == HA_KEY_ALG_FULLTEXT) { trace_idx_details.add("usable", false).add("cause", "fulltext"); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 5640358d363f5..81e5cab285c33 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -465,6 +465,7 @@ enum_nested_loop_state end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); + /* Check if Materialization strategy is allowed for given subquery predicate. @@ -484,7 +485,7 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, /* Check if the subquery predicate can be executed via materialization. The required conditions are: - 0. The materialization optimizer switch was set. + 0. The materialization optimizer switch/hint was set. 1. Subquery is a single SELECT (not a UNION). TODO: this is a limitation that can be fixed 2. Subquery is not a table-less query. In this case there is no @@ -513,7 +514,8 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, select_lex->sj_subselects list to be populated for every EXECUTE. */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 + uint strategies_allowed= child_select->subquery_strategies_allowed(thd); + if ((strategies_allowed & SUBS_MATERIALIZATION) && // 0 !child_select->is_part_of_union() && // 1 parent_unit->first_select()->leaf_tables.elements && // 2 child_select->outer_select() && @@ -739,7 +741,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) yet. They are checked later in convert_join_subqueries_to_semijoins(), look for calls to block_conversion_to_sj(). */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN) && + if (select_lex->semijoin_enabled(thd) && in_subs && // 1 !select_lex->is_part_of_union() && // 2 !select_lex->group_list.elements && !join->order && // 3 @@ -815,7 +817,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) with jtbm strategy */ if (in_subs->emb_on_expr_nest == NO_JOIN_NEST && - optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN)) + select_lex->semijoin_enabled(thd)) { in_subs->is_flattenable_semijoin= FALSE; if (!in_subs->is_registered_semijoin) @@ -833,11 +835,11 @@ int check_and_do_in_subquery_rewrites(JOIN *join) /* IN-TO-EXISTS is the only universal strategy. Choose it if the user - allowed it via an optimizer switch, or if materialization is not + allowed it via an optimizer switch/hint, or if materialization is not possible. */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) || - !in_subs->has_strategy()) + uint strategies_allowed= select_lex->subquery_strategies_allowed(thd); + if (strategies_allowed & SUBS_IN_TO_EXISTS || !in_subs->has_strategy()) in_subs->add_strategy(SUBS_IN_TO_EXISTS); } @@ -2558,11 +2560,11 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map) sj_nest->sj_mat_info= NULL; /* - The statement may have been executed with 'semijoin=on' earlier. - We need to verify that 'semijoin=on' still holds. + The statement may have been executed as a semijoin earlier. + We need to verify that semijoin materialization is still allowed. */ - if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_SEMIJOIN) && - optimizer_flag(join->thd, OPTIMIZER_SWITCH_MATERIALIZATION)) + if (sj_nest->nested_join->sj_enabled_strategies & + OPTIMIZER_SWITCH_MATERIALIZATION) { if ((sj_nest->sj_inner_tables & ~join->const_table_map) && /* not everything was pulled out */ !sj_nest->sj_subq_pred->is_correlated && @@ -3049,10 +3051,20 @@ void optimize_semi_joins(JOIN *join, table_map remaining_tables, uint idx, (dusp_producing_tables & handled_fanout is true), then *current_read_time is updated and the cost for the next strategy can be smaller than *current_read_time. + + The strategy may be disabled by an optimizer switch or a hint, + which is checked at (1). Currently, this is applicable only to + Duplicate Weedout since other disabled strategies will will be + cut off earlier and will not make it here. However, since + Duplicate Weedout serves as the default fallback strategy, it is + chosen even when disabled, provided no other viable alternatives + are available. */ - if ((dups_producing_tables & handled_fanout) || + if (((dups_producing_tables & handled_fanout) || (read_time + COST_EPS < *current_read_time && - !(handled_fanout & pos->inner_tables_handled_with_other_sjs))) + !(handled_fanout & pos->inner_tables_handled_with_other_sjs))) && + (!(*strategy)->is_disabled() || + pos->sj_strategy == SJ_OPT_NONE)) // (1) { DBUG_ASSERT(pos->sj_strategy != sj_strategy); /* @@ -3480,7 +3492,8 @@ bool Firstmatch_picker::check_qep(JOIN *join, POSITION *loose_scan_pos) { if (new_join_tab->emb_sj_nest && - optimizer_flag(join->thd, OPTIMIZER_SWITCH_FIRSTMATCH) && + (new_join_tab->emb_sj_nest->nested_join->sj_enabled_strategies & + OPTIMIZER_SWITCH_FIRSTMATCH) && !join->outer_join) { const table_map outer_corr_tables= @@ -3724,10 +3737,22 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join, POSITION *p= join->positions + j; dups_cost= COST_ADD(dups_cost, p->read_time); - if (p->table->emb_sj_nest) + TABLE_LIST *emb_sj_nest= p->table->emb_sj_nest; + if (emb_sj_nest) { sj_inner_fanout= COST_MULT(sj_inner_fanout, p->records_out); dups_removed_fanout |= p->table->table->map; + + /* + Duplicate Weedout is the default fallback strategy. It is used when + all other strategies are disabled by either an optimizer switch or + a hint. So, mark it as disabled for when there are other enabled + strategies to choose from + */ + disabled |= + emb_sj_nest->nested_join->sj_enabled_strategies != 0 && // (1) + !(emb_sj_nest->nested_join->sj_enabled_strategies & + OPTIMIZER_SWITCH_DUPSWEEDOUT); } else { @@ -3935,7 +3960,9 @@ at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab, TABLE_LIST *emb_sj_nest= tab->emb_sj_nest; table_map suffix= remaining_tables & ~tab->table->map; if (emb_sj_nest && emb_sj_nest->sj_mat_info && - !(suffix & emb_sj_nest->sj_inner_tables)) + !(suffix & emb_sj_nest->sj_inner_tables) && + (emb_sj_nest->nested_join->sj_enabled_strategies & + OPTIMIZER_SWITCH_MATERIALIZATION)) { /* Walk back and check if all immediately preceding tables are from diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 8b4dd1f009203..4ec30d47f753e 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -117,6 +117,7 @@ class Loose_scan_opt bound 5. But some of the IN-equalities aren't (so this can't be handled by FirstMatch strategy) + 6. LooseScan strategy is enabled for this SJ nest */ best_loose_scan_cost= DBL_MAX; if (!join->emb_sjm_nest && s->emb_sj_nest && // (1) @@ -127,7 +128,8 @@ class Loose_scan_opt !(remaining_tables & s->emb_sj_nest->nested_join->sj_corr_tables) && // (4) remaining_tables & s->emb_sj_nest->nested_join->sj_depends_on &&// (5) - optimizer_flag(join->thd, OPTIMIZER_SWITCH_LOOSE_SCAN)) + (s->emb_sj_nest->nested_join->sj_enabled_strategies & // (6) + OPTIMIZER_SWITCH_LOOSE_SCAN)) { /* This table is an LooseScan scan candidate */ bound_sj_equalities= get_bound_sj_equalities(s->emb_sj_nest, diff --git a/sql/scan_char.h b/sql/scan_char.h new file mode 100644 index 0000000000000..1aba6e39ce50a --- /dev/null +++ b/sql/scan_char.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2024, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef SCAN_CHAR_H +#define SCAN_CHAR_H + + +/** + A helper class to store the head character of a string, + with help of a charlen() call. +*/ +class Scan_char +{ + const char *m_ptr; // The start of the character + int m_length; // The result: + // >0 - the character octet length + // <=0 - an error (e.g. end of input, wrong byte sequence) +public: + Scan_char(CHARSET_INFO *const cs, const char *str, const char *end) + :m_ptr(str), m_length(cs->charlen(str, end)) + { } + // Compare if two non-erroneous characters are equal + bool eq(const Scan_char &rhs) const + { + DBUG_ASSERT(m_length > 0); + DBUG_ASSERT(rhs.m_length > 0); + return m_length == rhs.m_length && + !memcmp(m_ptr, rhs.m_ptr, (size_t) m_length); + } + // Compare if two possibly erroneous characters are equal + bool eq_safe(const Scan_char &rhs) const + { + return m_length == rhs.m_length && m_length > 0 && + !memcmp(m_ptr, rhs.m_ptr, (size_t) m_length); + } + const char *ptr() const { return m_ptr; } + int length() const { return m_length; } +}; + + +#endif // SCAN_CHAR_H diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f8d1a47c9f9f9..1e1d61e9d3a39 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12296,4 +12296,18 @@ WARN_INDEX_HINTS_IGNORED eng "Index hints are ignored because they are incompatible with RETURNING clause" ukr "Підказки по використанню индексів ігноруются тому що вони несумісні з RETURNING" ER_SIGNAL_SKIP_ROW_FROM_TRIGGER - eng "The row is skipped by a trigger implementation" \ No newline at end of file + eng "The row is skipped by a trigger implementation" +ER_WARN_OPTIMIZER_HINT_SYNTAX_ERROR + eng "Optimizer hint syntax error" +ER_WARN_CONFLICTING_HINT + eng "Hint %s is ignored as conflicting/duplicated" +ER_WARN_UNKNOWN_QB_NAME + eng "Query block name %s is not found for %s hint" +ER_UNRESOLVED_TABLE_HINT_NAME + eng "Unresolved table name %s for %s hint" +ER_UNRESOLVED_INDEX_HINT_NAME + eng "Unresolved index name %s for %s hint" +ER_HINTS_INSIDE_VIEWS_NOT_SUPPORTED + eng "Optimizer hints are not supported inside view definitions" +ER_WARN_MALFORMED_HINT + eng "Hint %s is ignored as malformed" diff --git a/sql/simple_parser.h b/sql/simple_parser.h new file mode 100644 index 0000000000000..611b7e6a93655 --- /dev/null +++ b/sql/simple_parser.h @@ -0,0 +1,667 @@ +#ifndef SIMPLE_PARSER_H +#define SIMPLE_PARSER_H +/* + Copyright (c) 2024, MariaDB + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA +*/ + + +#include "simple_tokenizer.h" + +/* + A set of templates for constructing a recursive-descent LL(1) parser. + + One is supposed to define classes corresponding to grammar productions. + The class should inherit from the grammar rule template. For example, a + grammar rule + + foo := bar, baz + + is implemented with + + class Bar ... ; // "bar" is parsed into Bar object + class Baz ... ; // "baz" is parsed into Baz object + + // "foo" is parsed into a Foo object. + class Foo: public Parser_templates::AND2 { + using AND2::AND2; + ... + }; + + Parsing code is generated by inheriting AND2's constructors with "using" like + shown above. All grammar rule-based classes should also have + - a capability to construct an "empty"(i.e. invalid) object with the default + constructor. This will be invoked when parsing fails. + - operator bool() which returns true if the object is non-empty (i.e. valid) + and false otherwise. + + Parsing is done by constructing parser output from the parser object: + + Foo parsed_output(parser); + + PARSER_Impl here is a class implementing a tokenizer and error condition + storage, like Extended_string_tokenizer. +*/ + +class Parser_templates +{ +protected: + + // Templates to parse common rule sequences + + /* + A rule consisting of a single token, e.g.: + rule ::= @ + rule ::= IDENT + */ + template + class TOKEN: public PARSER::Token + { + public: + TOKEN() + { } + TOKEN(const class PARSER::Token &tok) + :PARSER::Token(tok) + { } + TOKEN(class PARSER::Token &&tok) + :PARSER::Token(std::move(tok)) + { } + TOKEN(PARSER *p) + :PARSER::Token(p->token(tid)) + { } + static TOKEN empty(const PARSER &p) + { + return TOKEN(p.empty_token()); + } + }; + + + /* + A rule consisting of a choice of multiple tokens + rule ::= TOK1 | TOK2 | TOK3 + */ + template + class TokenChoice: public PARSER::Token + { + public: + TokenChoice() + { } + TokenChoice(PARSER *p) + :PARSER::Token(COND::allowed_token_id(p->look_ahead_token_id()) ? + p->shift() : + p->null_token()) + { + DBUG_ASSERT(!p->is_error() || !PARSER::Token::operator bool()); + } + }; + + + /* + An optional rule: + opt_rule ::= [ rule ] + */ + template + class OPT: public RULE + { + public: + OPT() + { } + OPT(PARSER *p) + :RULE(p) + { + if (!RULE::operator bool() && !p->is_error()) + { + RULE::operator=(RULE::empty(*p)); + DBUG_ASSERT(RULE::operator bool()); + } + } + }; + + + /* + A rule consisting of two other rules in a row: + rule ::= rule1 rule2 + */ + template + class AND2: public A, public B + { + public: + AND2() + :A(), B() + { } + AND2(AND2 && rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))) + { } + AND2(A &&a, B &&b) + :A(std::move(a)), B(std::move(b)) + { } + AND2 & operator=(AND2 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + return *this; + } + AND2(PARSER *p) + :A(p), + B(A::operator bool() ? B(p) : B()) + { + if (A::operator bool() && !B::operator bool()) + { + p->set_syntax_error(); + // Reset A to have A, B reported as "false" by their operator bool() + A::operator=(std::move(A())); + } + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() && B::operator bool(); + } + static AND2 empty(const PARSER &p) + { + return AND2(A::empty(p), B::empty(p)); + } + }; + + + /* + A rule consisting of three other rules in a row: + rule ::= rule1 rule2 rule3 + */ + template + class AND3: public A, public B, public C + { + public: + AND3() + :A(), B(), C() + { } + AND3(AND3 && rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))), + C(std::move(static_cast(rhs))) + { } + AND3(A &&a, B &&b, C &&c) + :A(std::move(a)), B(std::move(b)), C(std::move(c)) + { } + AND3 & operator=(AND3 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + C::operator=(std::move(static_cast(rhs))); + return *this; + } + AND3(PARSER *p) + :A(p), + B(A::operator bool() ? B(p) : B()), + C(A::operator bool() && B::operator bool() ? C(p) : C()) + { + if (A::operator bool() && (!B::operator bool() || !C::operator bool())) + { + p->set_syntax_error(); + // Reset A to have A, B, C reported as "false" by their operator bool() + A::operator=(A()); + B::operator=(B()); + C::operator=(C()); + } + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() && B::operator bool() && C::operator bool(); + } + static AND3 empty(const PARSER &p) + { + return AND3(A::empty(p), B::empty(p), C::empty()); + } + }; + + + /* + A rule consisting of four other rules in a row: + rule ::= rule1 rule2 rule3 rule4 + */ + template + class AND4: public A, public B, public C, public D + { + public: + AND4() + :A(), B(), C(), D() + { } + AND4(AND4 && rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))), + C(std::move(static_cast(rhs))), + D(std::move(static_cast(rhs))) + { } + AND4(A &&a, B &&b, C &&c, D &&d) + :A(std::move(a)), B(std::move(b)), C(std::move(c)), D(std::move(d)) + { } + AND4 & operator=(AND4 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + C::operator=(std::move(static_cast(rhs))); + D::operator=(std::move(static_cast(rhs))); + return *this; + } + AND4(PARSER *p) + :A(p), + B(A::operator bool() ? B(p) : B()), + C(A::operator bool() && B::operator bool() ? C(p) : C()), + D(A::operator bool() && B::operator bool() && C::operator bool() ? + D(p) : D()) + { + if (A::operator bool() && + (!B::operator bool() || !C::operator bool() || !D::operator bool())) + { + p->set_syntax_error(); + // Reset A to have A, B, C reported as "false" by their operator bool() + A::operator=(A()); + B::operator=(B()); + C::operator=(C()); + D::operator=(D()); + } + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() && B::operator bool() && + C::operator bool() && D::operator bool(); + } + static AND4 empty(const PARSER &p) + { + return AND4(A::empty(p), B::empty(p), C::empty(), D::empty()); + } + }; + + + /* + A rule consisting of a choice of rwo rules: + rule ::= rule1 | rule2 + + For the cases when the two branches have incompatible storage. + */ + template + class OR2: public A, public B + { + public: + OR2() + { } + OR2(OR2 &&rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))) + { } + OR2(A && rhs) + :A(std::move(rhs)), B() + { } + OR2(B && rhs) + :A(), B(std::move(rhs)) + { } + OR2 & operator=(OR2 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + return *this; + } + OR2(PARSER *p) + :A(p), B(A::operator bool() ? B() :B(p)) + { + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() || B::operator bool(); + } + }; + + + /* + A rule consisting of a choice of rwo rules, e.g. + rule ::= rule1 | rule2 + + For the cases when the two branches have a compatible storage, + passed as a CONTAINER, which must have constructors: + CONTAINER(const A &a) + CONTAINER(const B &b) + */ + template + class OR2C: public CONTAINER + { + public: + OR2C() + { } + OR2C(A &&a) + :CONTAINER(std::move(a)) + { } + OR2C(B &&b) + :CONTAINER(std::move(b)) + { } + OR2C(OR2C &&rhs) + :CONTAINER(std::move(rhs)) + { } + OR2C & operator=(OR2C &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR2C & operator=(A &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR2C & operator=(B &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR2C(PARSER *p) + :CONTAINER(A(p)) + { + if (CONTAINER::operator bool() || + CONTAINER::operator=(B(p))) + return; + DBUG_ASSERT(!CONTAINER::operator bool()); + } + }; + + + /* + A rule consisting of a choice of three rules: + rule ::= rule1 | rule2 | rule3 + + For the case when the three branches have incompatible storage + */ + template + class OR3: public A, public B, public C + { + public: + OR3() + { } + OR3(OR3 &&rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))), + C(std::move(static_cast(rhs))) + { } + OR3 & operator=(OR3 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + C::operator=(std::move(static_cast(rhs))); + return *this; + } + OR3(PARSER *p) + :A(p), + B(A::operator bool() ? B() : B(p)), + C(A::operator bool() || B::operator bool() ? C() : C(p)) + { + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() || B::operator bool() || C::operator bool(); + } + }; + + /* + A rule consisting of a choice of three rules, e.g. + rule ::= rule1 | rule2 | rule3 + + For the cases when the three branches have a compatible storage, + passed as a CONTAINER, which must have constructors: + CONTAINER(const A &a) + CONTAINER(const B &b) + CONTAINER(const C &c) + */ + template + class OR3C: public CONTAINER + { + public: + OR3C() + { } + OR3C(OR3C &&rhs) + :CONTAINER(std::move(rhs)) + { } + OR3C(A &&a) + :CONTAINER(std::move(a)) + { } + OR3C(B &&b) + :CONTAINER(std::move(b)) + { } + OR3C(C &&c) + :CONTAINER(std::move(c)) + { } + OR3C & operator=(OR3C &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR3C & operator=(A &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR3C & operator=(B &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + OR3C & operator=(C &&rhs) + { + CONTAINER::operator=(std::move(rhs)); + return *this; + } + + OR3C(PARSER *p) + :CONTAINER(A(p)) + { + if (CONTAINER::operator bool() || + CONTAINER::operator=(B(p)) || + CONTAINER::operator=(C(p))) + return; + DBUG_ASSERT(!CONTAINER::operator bool()); + } + }; + + + /* + A rule consisting of a choice of four rules: + rule ::= rule1 | rule2 | rule3 | rule4 + For the case when the four branches have incompatible storage + */ + template + class OR4: public A, public B, public C, public D + { + public: + OR4() + { } + OR4(OR4 &&rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))), + C(std::move(static_cast(rhs))), + D(std::move(static_cast(rhs))) + { } + OR4 & operator=(OR4 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + C::operator=(std::move(static_cast(rhs))); + D::operator=(std::move(static_cast(rhs))); + return *this; + } + OR4(PARSER *p) + :A(p), + B(A::operator bool() ? B() : B(p)), + C(A::operator bool() || B::operator bool() ? C() : C(p)), + D(A::operator bool() || B::operator bool() || C::operator bool() ? + D() : D(p)) + { + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() || B::operator bool() || C::operator bool() || + D::operator bool(); + } + }; + + /* + A rule consisting of a choice of seven rules: + rule ::= rule1 | rule2 | rule3 | rule4 | rule5 | rule6 | rule7 + */ + template + class OR7: public A, public B, public C, public D, public E, public F, + public G + { + public: + OR7() + { } + OR7(OR7 &&rhs) + :A(std::move(static_cast(rhs))), + B(std::move(static_cast(rhs))), + C(std::move(static_cast(rhs))), + D(std::move(static_cast(rhs))), + E(std::move(static_cast(rhs))), + F(std::move(static_cast(rhs))), + G(std::move(static_cast(rhs))) + { } + OR7 & operator=(OR7 &&rhs) + { + A::operator=(std::move(static_cast(rhs))); + B::operator=(std::move(static_cast(rhs))); + C::operator=(std::move(static_cast(rhs))); + D::operator=(std::move(static_cast(rhs))); + E::operator=(std::move(static_cast(rhs))); + F::operator=(std::move(static_cast(rhs))); + G::operator=(std::move(static_cast(rhs))); + return *this; + } + OR7(PARSER *p) + :A(p), + B(A::operator bool() ? B() : B(p)), + C(A::operator bool() || B::operator bool() ? C() : C(p)), + D(A::operator bool() || B::operator bool() || C::operator bool() ? + D() : D(p)), + E(A::operator bool() || B::operator bool() || C::operator bool() || + D::operator bool() ? E() : E(p)), + F(A::operator bool() || B::operator bool() || C::operator bool() || + D::operator bool() || E::operator bool() ? F() : F(p)), + G(A::operator bool() || B::operator bool() || C::operator bool() || + D::operator bool() || E::operator bool() || F::operator bool() ? + G() : G(p)) + { + DBUG_ASSERT(!operator bool() || !p->is_error()); + } + operator bool() const + { + return A::operator bool() || B::operator bool() || C::operator bool() || + D::operator bool() || E::operator bool() || F::operator bool() || + G::operator bool(); + } + }; + + + /* + A list with at least MIN_COUNT elements (typlically 0 or 1), + with or without a token separator between elements: + + list ::= element [ {, element }... ] // with a separator + list ::= element [ element ... ] // without a separator + + Pass the null-token special purpose ID in SEP for a non-separated list, + or a real token ID for a separated list. + + If MIN_COUNT is 0, then the list becomes optional, + which corresponds to the following grammar: + + list ::= [ element [ {, element }... ] ] // with a separator + list ::= [ element [ element ... ] ] // without a separator + */ + template + class LIST: public LIST_CONTAINER + { + protected: + bool m_error; + public: + LIST() + :m_error(true) + { } + LIST(LIST &&rhs) + :LIST_CONTAINER(std::move(rhs)), + m_error(rhs.m_error) + { } + LIST & operator=(LIST &&rhs) + { + LIST_CONTAINER::operator=(std::move(rhs)); + m_error= rhs.m_error; + return *this; + } + LIST(PARSER *p) + :m_error(true) + { + // Determine if the caller wants a separated or a non-separated list + const bool separated= SEP != PARSER::null_token().id(); + for ( ; ; ) + { + ELEMENT elem(p); + if (!elem) + { + if (LIST_CONTAINER::count() == 0 || !separated) + { + /* + Could not get the very first element, + or not-first element in a non-separated list. + */ + m_error= p->is_error(); + DBUG_ASSERT(!m_error || !operator bool()); + return; + } + // Could not get the next element after the separator + p->set_syntax_error(); + m_error= true; + DBUG_ASSERT(!operator bool()); + return; + } + if (LIST_CONTAINER::add(p, std::move(elem))) + { + p->set_fatal_error(); + m_error= true; + DBUG_ASSERT(!operator bool()); + return; + } + if (separated) + { + if (!p->token(SEP)) + { + m_error= false; + DBUG_ASSERT(operator bool()); + return; + } + } + } + } + operator bool() const + { + return !m_error && LIST_CONTAINER::count() >= MIN_COUNT; + } + }; + +}; + +#endif // SIMPLE_PARSER_H diff --git a/sql/simple_tokenizer.h b/sql/simple_tokenizer.h index b284dc1001735..ec561f202e4e0 100644 --- a/sql/simple_tokenizer.h +++ b/sql/simple_tokenizer.h @@ -17,11 +17,21 @@ #define SIMPLE_TOKENIZER_INCLUDED +#include "lex_string.h" +#include "scan_char.h" + +/** + A tokenizer for an ASCII7 input +*/ class Simple_tokenizer { +protected: const char *m_ptr; const char *m_end; public: + Simple_tokenizer(const LEX_CSTRING &str) + :m_ptr(str.str), m_end(str.str + str.length) + { } Simple_tokenizer(const char *str, size_t length) :m_ptr(str), m_end(str + length) { } @@ -33,11 +43,15 @@ class Simple_tokenizer { return m_ptr >= m_end; } + bool is_space() const + { + return m_ptr[0] == ' ' || m_ptr[0] == '\r' || m_ptr[0] == '\n'; + } void get_spaces() { for ( ; !eof(); m_ptr++) { - if (m_ptr[0] != ' ') + if (!is_space()) break; } } @@ -82,4 +96,184 @@ class Simple_tokenizer }; +/** + A tokenizer for a character set aware input. +*/ +class Extended_string_tokenizer: public Simple_tokenizer +{ +protected: + + CHARSET_INFO *m_cs; + + class Token_metadata + { + public: + bool m_extended_chars:1; + bool m_double_quotes:1; + Token_metadata() + :m_extended_chars(false), m_double_quotes(false) + { } + }; + + class Token_with_metadata: public Lex_cstring, + public Token_metadata + { + public: + Token_with_metadata() + { } + Token_with_metadata(const char *str, size_t length, + const Token_metadata &metadata) + :Lex_cstring(str, length), Token_metadata(metadata) + { } + Token_with_metadata(const char *str) + :Lex_cstring(str, (size_t) 0), Token_metadata() + { } + }; + + /* + Get a non-delimited identifier for a 8-bit character set + */ + Token_with_metadata get_ident_8bit(const char *str, const char *end) const + { + DBUG_ASSERT(m_cs->mbmaxlen == 1); + Token_with_metadata res(str); + for ( ; str < end && m_cs->ident_map[(uchar) *str]; str++, res.length++) + { + if (*str & 0x80) + res.m_extended_chars= true; + } + return res; + } + + /* + Get a non-identifier for a multi-byte character set + */ + Token_with_metadata get_ident_mb(const char *str, const char *end) const + { + DBUG_ASSERT(m_cs->mbmaxlen > 1); + Token_with_metadata res(str); + for ( ; m_cs->ident_map[(uchar) *str]; ) + { + int char_length= m_cs->charlen(str, end); + if (char_length <= 0) + break; + str+= char_length; + res.length+= (size_t) char_length; + res.m_extended_chars|= char_length > 1; + } + return res; + } + + /* + Get a non-delimited identifier + */ + Token_with_metadata get_ident(const char *str, const char *end) + { + return m_cs->mbmaxlen == 1 ? get_ident_8bit(str, end) : + get_ident_mb(str, end); + } + + /* + Get a quoted string or a quoted identifier. + The quote character is determined by the current head character + pointed by str. The result is returned together with the left + and the right quotes. + */ + Token_with_metadata get_quoted_string(const char *str, const char *end) + { + Token_with_metadata res(str); + const Scan_char quote(m_cs, str, end); + if (quote.length() <= 0) + { + /* + Could not get the left quote character: + - the end of the input reached, or + - a bad byte sequence found. + Return a null token to signal the error to the caller. + */ + return Token_with_metadata(); + } + str+= quote.length(); + res.length+= (size_t) quote.length(); + + for ( ; ; ) + { + const Scan_char ch(m_cs, str, end); + if (ch.length() <= 0) + { + /* + Could not find the right quote character: + - the end of the input reached before the quote was not found, or + - a bad byte sequences found + Return a null token to signal the error to the caller. + */ + return Token_with_metadata(); + } + str+= ch.length(); + res.length+= (size_t) ch.length(); + if (quote.eq(ch)) + { + if (quote.eq_safe(Scan_char(m_cs, str, end))) + { + /* + Two quotes in a row found: + - `a``b` + - "a""b" + */ + str+= quote.length(); + res.length+= (size_t) quote.length(); + res.m_extended_chars|= quote.length() > 1; + res.m_double_quotes= true; + continue; + } + return res; // The right quote found + } + res.m_extended_chars|= ch.length() > 1; + } + return res; + } + +public: + Extended_string_tokenizer(CHARSET_INFO *cs, const LEX_CSTRING &str) + :Simple_tokenizer(str), + m_cs(cs) + { } + + // Skip all leading spaces + void get_spaces() + { + for ( ; !eof(); m_ptr++) + { + if (!my_isspace(m_cs, *m_ptr)) + break; + } + } + + /* + Get a non-delimited identifier. + Can return an empty token if the head character is not an identifier + character. + */ + Token_with_metadata get_ident() + { + const Token_with_metadata tok= get_ident(m_ptr, m_end); + m_ptr+= tok.length; + return tok; + } + + /* + Get a quoted string or a quoted identifier. + Can return a null token if there were errors + (e.g. unexpected end of the input, bad byte sequence). + */ + Token_with_metadata get_quoted_string() + { + const Token_with_metadata tok= get_quoted_string(m_ptr, m_end); + m_ptr+= tok.length; + return tok; + } + +}; + + #endif // SIMPLE_TOKENIZER_INCLUDED diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f6048cdf9969a..d4d16176af647 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -65,6 +65,7 @@ #include "wsrep_thd.h" #include "wsrep_trans_observer.h" #endif /* WITH_WSREP */ +#include "opt_hints.h" bool No_such_table_error_handler::handle_condition(THD *, @@ -8221,6 +8222,7 @@ void make_leaves_list(THD *thd, List &list, TABLE_LIST *tables, refresh It is only refresh for subquery select_insert It is SELECT ... INSERT command full_table_list a parameter to pass to the make_leaves_list function + resolve_opt_hints Whether optimizer hints must be resolved here NOTE Check also that the 'used keys' and 'ignored keys' exists and set up the @@ -8240,7 +8242,7 @@ void make_leaves_list(THD *thd, List &list, TABLE_LIST *tables, bool setup_tables(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, List &leaves, bool select_insert, - bool full_table_list) + bool full_table_list, bool resolve_opt_hints) { uint tablenr= 0; List_iterator ti(leaves); @@ -8259,6 +8261,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, 0); SELECT_LEX *select_lex= select_insert ? thd->lex->first_select_lex() : thd->lex->current_select; + Opt_hints_qb *qb_hints= select_lex->opt_hints_qb; if (select_lex->first_cond_optimization) { leaves.empty(); @@ -8310,6 +8313,14 @@ bool setup_tables(THD *thd, Name_resolution_context *context, my_error(ER_TOO_MANY_TABLES, MYF(0), static_cast(MAX_TABLES)); DBUG_RETURN(1); } + + if (qb_hints && // QB hints initialized + !table_list->opt_hints_table) // Table hints are not adjusted yet + { + table_list->opt_hints_table= + qb_hints->fix_hints_for_table(table_list->table, + table_list->alias); + } } if (select_insert && !is_insert_tables_num_set) { @@ -8378,6 +8389,19 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (setup_natural_join_row_types(thd, from_clause, context)) DBUG_RETURN(1); + if (resolve_opt_hints) + { + if (thd->lex->opt_hints_global && select_lex->select_number == 1) + { + thd->lex->opt_hints_global->fix_hint(thd); + /* + There's no need to call opt_hints_global->check_unresolved(), + this is done for each query block individually + */ + } + if (qb_hints) + qb_hints->check_unfixed(thd); + } DBUG_RETURN(0); } @@ -8397,6 +8421,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, select_insert It is SELECT ... INSERT command want_access what access is needed full_table_list a parameter to pass to the make_leaves_list function + resolve_opt_hints Whether optimizer hints must be resolved here NOTE a wrapper for check_tables that will also check the resulting @@ -8413,12 +8438,13 @@ bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context, bool select_insert, privilege_t want_access_first, privilege_t want_access, - bool full_table_list) + bool full_table_list, + bool resolve_opt_hints) { DBUG_ENTER("setup_tables_and_check_access"); if (setup_tables(thd, context, from_clause, tables, - leaves, select_insert, full_table_list)) + leaves, select_insert, full_table_list, resolve_opt_hints)) DBUG_RETURN(TRUE); List_iterator ti(leaves); diff --git a/sql/sql_base.h b/sql/sql_base.h index a65a223b6e5d0..3043d347f4ff9 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -228,7 +228,7 @@ Item ** find_item_in_list(Item *item, List &items, uint *counter, bool setup_tables(THD *thd, Name_resolution_context *context, List *from_clause, TABLE_LIST *tables, List &leaves, bool select_insert, - bool full_table_list); + bool full_table_list, bool resolve_opt_hints); bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context, List *from_clause, @@ -237,7 +237,8 @@ bool setup_tables_and_check_access(THD *thd, bool select_insert, privilege_t want_access_first, privilege_t want_access, - bool full_table_list); + bool full_table_list, + bool resolve_opt_hints); bool wait_while_table_is_used(THD *thd, TABLE *table, enum ha_extra_function function); diff --git a/sql/sql_class.h b/sql/sql_class.h index bea09e62b110b..6c3360383525e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5911,7 +5911,7 @@ class THD: public THD_count, /* this must be first */ ulonglong num_of_strings_sorted_on_truncated_length; public: - void set_query_timer() + void set_query_timer_if_needed() { #ifndef EMBEDDED_LIBRARY /* @@ -5930,6 +5930,12 @@ class THD: public THD_count, /* this must be first */ */ if (!timeout_val || spcont || in_sub_stmt || query_timer.expired == 0) return; + set_query_timer_force(timeout_val); +#endif + } + void set_query_timer_force(ulonglong timeout_val) + { +#ifndef EMBEDDED_LIBRARY thr_timer_settime(&query_timer, timeout_val); #endif } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 12950c5434732..01c944a64c54a 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1865,11 +1865,11 @@ bool Sql_cmd_delete::prepare_inner(THD *thd) if (setup_tables_and_check_access(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, - false, DELETE_ACL, SELECT_ACL, true)) + false, DELETE_ACL, SELECT_ACL, true, false)) DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, false, false)) + table_list, select_lex->leaf_tables, false, false, true)) DBUG_RETURN(TRUE); if (!multitable) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index a028c219936ee..e079e8b793a06 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -801,7 +801,7 @@ static bool init_items_for_help_command(THD *thd, if (setup_tables(thd, &first_select_lex->context, &first_select_lex->top_join_list, - &tables[0], leaves, false, false)) + &tables[0], leaves, false, false, true)) return true; memcpy((char*) used_fields, (char*) init_used_fields, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index ba9d576a91e5f..f717be92b9d26 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1629,7 +1629,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, table_list, thd->lex->first_select_lex()->leaf_tables, select_insert, INSERT_ACL, SELECT_ACL, - TRUE)) + true, true)) DBUG_RETURN(TRUE); if (insert_into_view && !fields.elements) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 89fe4af5f4884..54df993a30716 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -38,6 +38,7 @@ #include "sql_partition.h" #include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part #include "event_parse_data.h" +#include "opt_hints.h" #ifdef WITH_WSREP #include "mysql/service_wsrep.h" #endif @@ -855,6 +856,7 @@ Lex_input_stream::reset(char *buffer, size_t length) found_semicolon= NULL; ignore_space= MY_TEST(m_thd->variables.sql_mode & MODE_IGNORE_SPACE); stmt_prepare_mode= FALSE; + hint_comment= FALSE; multi_statements= TRUE; in_comment=NO_COMMENT; m_underscore_cs= NULL; @@ -1337,6 +1339,7 @@ void LEX::start(THD *thd_arg) table_count_update= 0; needs_reprepare= false; + opt_hints_global= 0; memset(&trg_chistics, 0, sizeof(trg_chistics)); DBUG_VOID_RETURN; @@ -2493,10 +2496,21 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd) else { in_comment= PRESERVE_COMMENT; + yylval->lex_comment.lineno= yylineno; + yylval->lex_comment.str= m_ptr; yySkip(); // Accept / yySkip(); // Accept * - comment_closed= ! consume_comment(0); /* regular comments can have zero comments inside. */ + if ((comment_closed= ! consume_comment(0)) && hint_comment) + { + if (yylval->lex_comment.str[2] == '+') + { + next_state= MY_LEX_START; + yylval->lex_comment.length= m_ptr - yylval->lex_comment.str; + restore_in_comment_state(); + return HINT_COMMENT; + } + } } /* Discard: @@ -3097,6 +3111,8 @@ void st_select_lex::init_query() versioned_tables= 0; pushdown_select= 0; orig_names_of_item_list_elems= 0; + opt_hints_qb= 0; + parsed_optimizer_hints= 0; } void st_select_lex::init_select() @@ -3150,6 +3166,8 @@ void st_select_lex::init_select() nest_flags= 0; orig_names_of_item_list_elems= 0; item_list_usage= MARK_COLUMNS_READ; + opt_hints_qb= 0; + parsed_optimizer_hints= 0; } /* @@ -4073,9 +4091,9 @@ void Query_tables_list::destroy_query_tables_list() */ LEX::LEX() - : explain(NULL), result(0), part_info(NULL), arena_for_set_stmt(0), - mem_root_for_set_stmt(0), json_table(NULL), analyze_stmt(0), - default_used(0), + : explain(NULL), result(0), opt_hints_global(NULL), part_info(NULL), + arena_for_set_stmt(0), mem_root_for_set_stmt(0), json_table(NULL), + analyze_stmt(0), default_used(0), with_rownum(0), is_lex_started(0), without_validation(0), option_type(OPT_DEFAULT), context_analysis_only(0), sphead(0), sp_mem_root_ptr(nullptr), limit_rows_examined_cnt(ULONGLONG_MAX) @@ -5966,6 +5984,84 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) return all_merged; } +/** + Returns which subquery execution strategies can be used for this query block. + + @param thd Pointer to THD object for session. + Used to access optimizer_switch + + @retval SUBS_MATERIALIZATION Subquery Materialization should be used + @retval SUBS_IN_TO_EXISTS In-to-exists execution should be used + @retval SUBS_MATERIALIZATION | SUBS_IN_TO_EXISTS A cost-based decision + should be made +*/ +uint st_select_lex::subquery_strategies_allowed(THD *thd) const +{ + if (opt_hints_qb && opt_hints_qb->subquery_strategy != SUBS_NOT_TRANSFORMED) + return opt_hints_qb->subquery_strategy; + + // No SUBQUERY hint given, base possible strategies on optimizer_switch + uint strategy = SUBS_NOT_TRANSFORMED; + if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) + strategy |= SUBS_MATERIALIZATION; + if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS)) + strategy |= SUBS_IN_TO_EXISTS; + return strategy; +} + +/** + Returns whether semi-join is enabled for this query block + + @see @c Opt_hints_qb::semijoin_enabled for details on how hints + affect this decision. If there are no hints for this query block, + optimizer_switch setting determines whether semi-join is used. + + @param thd Pointer to THD object for session. + Used to access optimizer_switch + + @return true if semijoin is enabled, + false otherwise +*/ +bool st_select_lex::semijoin_enabled(THD *thd) const +{ + return opt_hints_qb ? + opt_hints_qb->semijoin_enabled(thd) : + optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN); +} + +/** + Update available semijoin strategies for semijoin nests. + + Available semijoin strategies needs to be updated on every execution since + optimizer_switch setting may have changed. + + @param thd Pointer to THD object for session. + Used to access optimizer_switch +*/ +void st_select_lex::update_available_semijoin_strategies(THD *thd) +{ + uint sj_strategy_mask= OPTIMIZER_SWITCH_FIRSTMATCH | + OPTIMIZER_SWITCH_LOOSE_SCAN | OPTIMIZER_SWITCH_MATERIALIZATION | + OPTIMIZER_SWITCH_DUPSWEEDOUT; + + uint opt_switches= thd->variables.optimizer_switch & sj_strategy_mask; + + List_iterator sj_list_it(sj_nests); + TABLE_LIST *sj_nest; + while ((sj_nest= sj_list_it++)) + { + /* + After semi-join transformation, original SELECT_LEX with hints is lost. + Fetch hints from first table in semijoin nest. + */ + List_iterator table_list(sj_nest->nested_join->join_list); + TABLE_LIST *table= table_list++; + sj_nest->nested_join->sj_enabled_strategies= table->opt_hints_qb ? + table->opt_hints_qb->sj_enabled_strategies(opt_switches) : opt_switches; + } +} + + /* This is used by SHOW EXPLAIN|ANALYZE. It assumes query plan has been already collected into QPF structures and we only need to print it out. @@ -10859,6 +10955,7 @@ bool LEX::parsed_insert_select(SELECT_LEX *first_select) return true; // fix "main" select + resolve_optimizer_hints_in_last_select(); SELECT_LEX *blt __attribute__((unused))= pop_select(); DBUG_ASSERT(blt == &builtin_select); push_select(first_select); @@ -12600,3 +12697,68 @@ bool SELECT_LEX_UNIT::is_derived_eliminated() const return true; return derived->table->map & outer_select()->join->eliminated_tables; } + + +/* + Parse optimizer hints and return as Hint_list allocated on thd->mem_root. + + The caller should check both return value and thd->is_error() + to know what happened, as follows: + + Return value thd->is_error() Meaning + ------------ --------------- ------- + rc != nullptr false the hints were parsed without errors + rc != nullptr true not possible + rc == nullptr false no hints, empty hints, hint parse error + rc == nullptr true fatal error, such as EOM +*/ +Optimizer_hint_parser_output * +LEX::parse_optimizer_hints(const Lex_comment_st &hints_str) +{ + DBUG_ASSERT(!hints_str.str || hints_str.length >= 5); + if (!hints_str.str) + return nullptr; // There were no a hint comment + + // Instantiate the query hint parser. + // Remove the leading '/*+' and trailing '*/' + // when passing hints to the parser. + Optimizer_hint_parser p(thd, thd->charset(), + Lex_cstring(hints_str.str + 3, hints_str.length - 5)); + // Parse hints + Optimizer_hint_parser_output hints(&p); + DBUG_ASSERT(!p.is_error() || !hints); + + if (p.is_fatal_error()) + { + /* + Fatal error (e.g. EOM), have the caller fail. + The SQL error should be in DA already. + */ + DBUG_ASSERT(thd->is_error()); + return nullptr; // Continue, the caller will test thd->is_error() + } + + if (!hints) // Hint parsing failed with a syntax error + { + p.push_warning_syntax_error(thd, hints_str.lineno); + return nullptr; // Continue and ignore hints. + } + + // Hints were not empty and were parsed without errors + return new (thd->mem_root) Optimizer_hint_parser_output(std::move(hints)); +} + + +void LEX::resolve_optimizer_hints_in_last_select() +{ + SELECT_LEX *select_lex; + if (likely(select_stack_top)) + select_lex= select_stack[select_stack_top - 1]; + else + select_lex= nullptr; + if (select_lex && select_lex->parsed_optimizer_hints) + { + Parse_context pc(thd, select_lex); + select_lex->parsed_optimizer_hints->resolve(&pc); + } +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2ea96fd176431..c2a12f9645d25 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -48,6 +48,17 @@ typedef Bitmap nesting_map; /* YACC and LEX Definitions */ +struct Lex_comment_st: public LEX_CSTRING +{ + uint lineno; + void init() + { + LEX_CSTRING::operator=({nullptr, 0}); + lineno= 0; + } +}; + + struct Lex_column_list_privilege_st { List *m_columns; @@ -167,6 +178,9 @@ class With_clause; class my_var; class select_handler; class Pushdown_select; +class Opt_hints_global; +class Opt_hints_qb; +class Optimizer_hint_parser_output; #define ALLOC_ROOT_SET 1024 @@ -1246,6 +1260,15 @@ class st_select_lex: public st_select_lex_node */ table_map select_list_tables; + /* + Parse tree of optimizer hints that were specified in this SELECT. Note + that hints specified in one select can prescribe how to execute another. + */ + const Optimizer_hint_parser_output *parsed_optimizer_hints; + + /* Optimizer hints that prescribe how to execute this SELECT */ + Opt_hints_qb *opt_hints_qb; + /* Set to 1 if any field in field list has ROWNUM() */ bool rownum_in_field_list; @@ -1255,8 +1278,8 @@ class st_select_lex: public st_select_lex_node index_clause_map current_index_hint_clause; /* it is for correct printing SELECT options */ - thr_lock_type lock_type; - + thr_lock_type lock_type; + /** System Versioning */ int vers_setup_conds(THD *thd, TABLE_LIST *tables); /* push new Item_field into item_list */ @@ -1372,6 +1395,7 @@ class st_select_lex: public st_select_lex_node uint get_cardinality_of_ref_ptrs_slice(uint order_group_num_arg); void print(THD *thd, String *str, enum_query_type query_type); void print_lock_type(String *str); + void print_hints(THD *thd, String *hint_str); void print_item_list(THD *thd, String *str, enum_query_type query_type); void print_set_clause(THD *thd, String *str, enum_query_type query_type); void print_on_duplicate_key_clause(THD *thd, String *str, @@ -1553,6 +1577,13 @@ class st_select_lex: public st_select_lex_node bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); } void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; } bool is_sj_conversion_prohibited(THD *thd); + void set_optimizer_hints(Optimizer_hint_parser_output *hl) + { + parsed_optimizer_hints= hl; + } + uint subquery_strategies_allowed(THD *thd) const; + bool semijoin_enabled(THD *thd) const; + void update_available_semijoin_strategies(THD *thd); }; typedef class st_select_lex SELECT_LEX; @@ -2824,6 +2855,11 @@ class Lex_input_stream */ bool multi_statements:1; + /** + TRUE if hint comments should be returned as a token. + */ + bool hint_comment:1; + /** Current line number. */ uint yylineno; @@ -3182,6 +3218,9 @@ struct LEX: public Query_tables_list LEX_USER *grant_user; XID *xid; THD *thd; + + /* Optimizer hints */ + Opt_hints_global *opt_hints_global; /* maintain a list of used plugins for this LEX */ DYNAMIC_ARRAY plugins; @@ -3716,6 +3755,8 @@ struct LEX: public Query_tables_list DBUG_RETURN(select_lex); } + void resolve_optimizer_hints_in_last_select(); + SELECT_LEX *current_select_or_default() { return current_select ? current_select : &builtin_select; @@ -4906,6 +4947,9 @@ struct LEX: public Query_tables_list { return nullptr; } + + Optimizer_hint_parser_output * + parse_optimizer_hints(const Lex_comment_st &hint); }; diff --git a/sql/sql_list.h b/sql/sql_list.h index 48456dc894556..d3ae0c5538b55 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -293,7 +293,7 @@ class base_list :public Sql_alloc inline list_node* first_node() { return first;} inline void *head() { return first->info; } inline void **head_ref() { return first != &end_of_list ? &first->info : 0; } - inline bool is_empty() { return first == &end_of_list ; } + inline bool is_empty() const { return first == &end_of_list ; } inline list_node *last_ref() { return &end_of_list; } template inline bool add_unique(T *info, bool (*eq)(T *a, T *b)) @@ -532,7 +532,6 @@ template class List :public base_list class Iterator; using value_type= T; using iterator= Iterator; - iterator begin() const { return iterator(first); } iterator end() const { return iterator(); } @@ -734,7 +733,7 @@ class base_ilist public: inline void empty() { first= &last; last.prev= &first; } base_ilist() { empty(); } - inline bool is_empty() { return first == &last; } + inline bool is_empty() const { return first == &last; } // Returns true if p is the last "real" object in the list, // i.e. p->next points to the sentinel. inline bool is_last(ilink *p) { return p->next == NULL || p->next == &last; } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index a785df0347cdb..019231610c070 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -419,7 +419,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list, thd->lex->first_select_lex()->leaf_tables, FALSE, INSERT_ACL | UPDATE_ACL, - INSERT_ACL | UPDATE_ACL, FALSE)) + INSERT_ACL | UPDATE_ACL, false, true)) DBUG_RETURN(-1); if (!table_list->table || // do not support join view !table_list->single_table_updatable() || // and derived tables diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 787873c4d729a..1d884789e7845 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3847,7 +3847,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) thd->query_plan_flags|= QPLAN_ADMIN; /* Start timeouts */ - thd->set_query_timer(); + thd->set_query_timer_if_needed(); #ifdef WITH_WSREP /* Check wsrep_mode rules before command execution. */ diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 42b95bccd1ded..9791fbe2fc83d 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -140,41 +140,42 @@ #define OPTIMIZER_SWITCH_DERIVED_WITH_KEYS (1ULL << 7) #define OPTIMIZER_SWITCH_FIRSTMATCH (1ULL << 8) #define OPTIMIZER_SWITCH_LOOSE_SCAN (1ULL << 9) -#define OPTIMIZER_SWITCH_MATERIALIZATION (1ULL << 10) -#define OPTIMIZER_SWITCH_IN_TO_EXISTS (1ULL << 11) -#define OPTIMIZER_SWITCH_SEMIJOIN (1ULL << 12) -#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1ULL << 13) -#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1ULL << 14) -#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1ULL << 15) +#define OPTIMIZER_SWITCH_DUPSWEEDOUT (1ULL << 10) +#define OPTIMIZER_SWITCH_MATERIALIZATION (1ULL << 11) +#define OPTIMIZER_SWITCH_IN_TO_EXISTS (1ULL << 12) +#define OPTIMIZER_SWITCH_SEMIJOIN (1ULL << 13) +#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1ULL << 14) +#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1ULL << 15) +#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1ULL << 16) /** If this is off, MRR is never used. */ -#define OPTIMIZER_SWITCH_MRR (1ULL << 16) +#define OPTIMIZER_SWITCH_MRR (1ULL << 17) /** If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is off, MRR is "forced" (i.e. used as long as the storage engine is capable of doing it). */ -#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 17) -#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 18) -#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 19) -#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 20) -#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 21) -#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 22) -#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 23) -#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 24) -#define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 25) -#define OPTIMIZER_SWITCH_EXTENDED_KEYS (1ULL << 26) -#define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 27) -#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 28) -#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 29) -#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 30) -#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY (1ULL << 31) -#define OPTIMIZER_SWITCH_USE_ROWID_FILTER (1ULL << 32) -#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING (1ULL << 33) -#define OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN (1ULL << 34) -#define OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY (1ULL << 35) -#define OPTIMIZER_SWITCH_CSET_NARROWING (1ULL << 36) -#define OPTIMIZER_SWITCH_SARGABLE_CASEFOLD (1ULL << 37) +#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 18) +#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 19) +#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 20) +#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 21) +#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 22) +#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 23) +#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 24) +#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 25) +#define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 26) +#define OPTIMIZER_SWITCH_EXTENDED_KEYS (1ULL << 27) +#define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28) +#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) +#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) +#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31) +#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY (1ULL << 32) +#define OPTIMIZER_SWITCH_USE_ROWID_FILTER (1ULL << 33) +#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING (1ULL << 34) +#define OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN (1ULL << 35) +#define OPTIMIZER_SWITCH_HASH_JOIN_CARDINALITY (1ULL << 36) +#define OPTIMIZER_SWITCH_CSET_NARROWING (1ULL << 37) +#define OPTIMIZER_SWITCH_SARGABLE_CASEFOLD (1ULL << 38) /* @@ -208,6 +209,7 @@ OPTIMIZER_SWITCH_SEMIJOIN | \ OPTIMIZER_SWITCH_FIRSTMATCH | \ OPTIMIZER_SWITCH_LOOSE_SCAN | \ + OPTIMIZER_SWITCH_DUPSWEEDOUT | \ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index d7ca573606596..01cad99bb611b 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -40,11 +40,22 @@ struct TABLE_PAIR TABLE_LIST *from, *to; }; +enum Result_code : int +{ + SUCCESS= 0, + SPECIFIED_ERROR= 1, // I.e., Prepared_error of Rename_result must be issued + IGNORABLE_ERROR= 2, + UNSPECIFIED_ERROR= 3 +}; + +using Rename_result= std::pair; + +static Rename_result rename_tables(THD *thd, TABLE_LIST *table_list, + DDL_LOG_STATE *ddl_log_state, + bool skip_error, bool if_exits, + bool *force_if_exists); + -static bool rename_tables(THD *thd, TABLE_LIST *table_list, - DDL_LOG_STATE *ddl_log_state, - bool skip_error, bool if_exits, - bool *force_if_exists); /* Every two entries in the table_list form a pair of original name and @@ -54,12 +65,13 @@ static bool rename_tables(THD *thd, TABLE_LIST *table_list, bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, bool if_exists) { - bool error= 1; bool binlog_error= 0, force_if_exists; TABLE_LIST *ren_table= 0; int to_table; const char *rename_log_table[2]= {NULL, NULL}; DDL_LOG_STATE ddl_log_state; + Rename_result result; + result.first= SUCCESS; DBUG_ENTER("mysql_rename_tables"); /* @@ -155,17 +167,16 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, 0)) goto err; - error=0; bzero(&ddl_log_state, sizeof(ddl_log_state)); /* An exclusive lock on table names is satisfactory to ensure no other thread accesses this table. */ - error= rename_tables(thd, table_list, &ddl_log_state, - 0, if_exists, &force_if_exists); + result= rename_tables(thd, table_list, &ddl_log_state, + 0, if_exists, &force_if_exists); - if (likely(!silent && !error)) + if (likely(!silent && result.first == SUCCESS)) { ulonglong save_option_bits= thd->variables.option_bits; if (force_if_exists && ! if_exists) @@ -183,7 +194,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, ddl_log_update_xid(&ddl_log_state, thd->binlog_xid); binlog_error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); if (binlog_error) - error= 1; + result.first= UNSPECIFIED_ERROR; thd->binlog_xid= 0; thd->variables.option_bits= save_option_bits; debug_crash_here("ddl_log_rename_after_binlog"); @@ -192,7 +203,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, my_ok(thd); } - if (likely(!error)) + if (likely(result.first == SUCCESS)) { query_cache_invalidate3(thd, table_list, 0); ddl_log_complete(&ddl_log_state); @@ -201,14 +212,21 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, { /* Revert the renames of normal tables with the help of the ddl log */ ddl_log_revert(thd, &ddl_log_state); + + /* + After finishing of the DDL reversion process print the error + that caused the renaming failure + */ + if (result.first == SPECIFIED_ERROR) + my_error_issue(&result.second); } err: - DBUG_RETURN(error || binlog_error); + DBUG_RETURN(result.first || binlog_error); } -static bool +static Rename_result do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table) { LEX_CSTRING *new_alias; @@ -219,12 +237,16 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table) if (thd->find_temporary_table(new_table, THD::TMP_TABLE_ANY)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias->str); - DBUG_RETURN(1); // This can't be skipped + DBUG_RETURN(Rename_result( + SPECIFIED_ERROR, my_error_prepare(ER_TABLE_EXISTS_ERROR, MYF(0), + new_alias->str))); } - DBUG_RETURN(thd->rename_temporary_table(ren_table->table, - &new_table->db, new_alias)); + Rename_result res(SUCCESS, Prepared_error{}); + if (thd->rename_temporary_table(ren_table->table, + &new_table->db, new_alias)) + res.first= UNSPECIFIED_ERROR; + DBUG_RETURN(res); } @@ -252,13 +274,13 @@ struct rename_param @param new_table_alias The new table/view alias @param if_exists If not set, give an error if the table does not exists. If set, just give a warning in this case. - @return - @retval 0 ok - @retval >0 Error (from table doesn't exists or to table exists) - @retval <0 Can't do rename, but no error + + @return Rename_result object. + res.first indicates status of the operation (SUCCESS, ERROR etc) + res.second contains prepared error structure for printing */ -static int +static Rename_result check_rename(THD *thd, rename_param *param, const TABLE_LIST *ren_table, const Lex_ident_db &new_db, @@ -269,7 +291,6 @@ check_rename(THD *thd, rename_param *param, DBUG_ENTER("check_rename"); DBUG_PRINT("enter", ("if_exists: %d", (int) if_exists)); - if (lower_case_table_names == 2) { param->old_alias= ren_table->alias; @@ -286,9 +307,16 @@ check_rename(THD *thd, rename_param *param, ¶m->old_version, ¶m->from_table_hton) || !param->from_table_hton) { - my_error(ER_NO_SUCH_TABLE, MYF(if_exists ? ME_NOTE : 0), - ren_table->db.str, param->old_alias.str); - DBUG_RETURN(if_exists ? -1 : 1); + if (if_exists) + { + // Push a note (warning) + my_error(ER_NO_SUCH_TABLE, MYF(ME_NOTE), ren_table->db.str, + param->old_alias.str); + } + DBUG_RETURN(Rename_result( + (if_exists ? IGNORABLE_ERROR : SPECIFIED_ERROR), + my_error_prepare(ER_NO_SUCH_TABLE, MYF(if_exists ? ME_NOTE : 0), + ren_table->db.str, param->old_alias.str))); } if (param->from_table_hton != view_pseudo_hton && @@ -300,15 +328,16 @@ check_rename(THD *thd, rename_param *param, */ tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str); quick_rm_table(thd, 0, &ren_table->db, ¶m->old_alias, QRMT_FRM); - DBUG_RETURN(-1); + DBUG_RETURN(Rename_result(IGNORABLE_ERROR, Prepared_error{})); } if (ha_table_exists(thd, &new_db, ¶m->new_alias)) { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), param->new_alias.str); - DBUG_RETURN(1); // This can't be skipped + DBUG_RETURN(Rename_result( + SPECIFIED_ERROR, + my_error_prepare(ER_TABLE_EXISTS_ERROR, MYF(0), param->new_alias.str))); } - DBUG_RETURN(0); + DBUG_RETURN(Rename_result(SUCCESS, Prepared_error{})); } @@ -481,18 +510,24 @@ do_rename(THD *thd, const rename_param *param, DDL_LOG_STATE *ddl_log_state, the name taken from list element+1. Note that the table_list may be empty. - RETURN - 0 Ok - 1 error - All tables are reverted to their original names + NOTE + Possible errors are not printed immediately because my_error() sets + `thd->is_error()` flag which may harm the DDL reversion (for atomic DDL). + Instead, the prepared error is returned in the Rename_result and can be + issued after the sequence of atomic DDLs is reverted + + RETURN Rename_result object. + res.first indicates status of the operation (SUCCESS, ERROR etc) + res.second contains prepared error structure for printing */ -static bool +static Rename_result rename_tables(THD *thd, TABLE_LIST *table_list, DDL_LOG_STATE *ddl_log_state, bool skip_error, bool if_exists, bool *force_if_exists) { TABLE_LIST *ren_table, *new_table; List tmp_tables; + Rename_result rename_res(SUCCESS, Prepared_error()); DBUG_ENTER("rename_tables"); *force_if_exists= 0; @@ -500,7 +535,6 @@ rename_tables(THD *thd, TABLE_LIST *table_list, DDL_LOG_STATE *ddl_log_state, for (ren_table= table_list; ren_table; ren_table= new_table->next_local) { new_table= ren_table->next_local; - if (is_temporary_table(ren_table)) { /* @@ -515,34 +549,42 @@ rename_tables(THD *thd, TABLE_LIST *table_list, DDL_LOG_STATE *ddl_log_state, pair->from= ren_table; pair->to= new_table; - if (do_rename_temporary(thd, ren_table, new_table)) + rename_res= do_rename_temporary(thd, ren_table, new_table); + if (rename_res.first != SUCCESS) goto revert_rename; } else { - int error; rename_param param; - error= check_rename(thd, ¶m, ren_table, new_table->db, + rename_res= check_rename(thd, ¶m, ren_table, new_table->db, new_table->table_name, new_table->alias, (skip_error || if_exists)); - if (error < 0) - continue; // Ignore rename (if exists) - if (error > 0) + if (rename_res.first == IGNORABLE_ERROR) + { + // Ignore rename (if exists) + rename_res.first= SUCCESS; + rename_res.second= Prepared_error(); + continue; + } + if (rename_res.first == SPECIFIED_ERROR || + rename_res.first == UNSPECIFIED_ERROR) goto revert_rename; if (do_rename(thd, ¶m, ddl_log_state, ren_table, &new_table->db, skip_error, force_if_exists)) + { + rename_res.first= UNSPECIFIED_ERROR; goto revert_rename; + } } } - DBUG_RETURN(0); + DBUG_RETURN(rename_res); revert_rename: /* Revert temporary tables. Normal tables are reverted in the caller */ List_iterator_fast it(tmp_tables); while (TABLE_PAIR *pair= it++) do_rename_temporary(thd, pair->to, pair->from); - - DBUG_RETURN(1); + DBUG_RETURN(rename_res); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index be3efae83e7c7..5e0a6499cdfc8 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -68,6 +68,7 @@ #include "create_tmp_table.h" #include "optimizer_defaults.h" #include "derived_handler.h" +#include "opt_hints.h" /* A key part number that means we're using a fulltext scan. @@ -1467,7 +1468,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, if (!(select_options & OPTION_SETUP_TABLES_DONE) && setup_tables_and_check_access(thd, &select_lex->context, join_list, tables_list, select_lex->leaf_tables, - FALSE, SELECT_ACL, SELECT_ACL, FALSE)) + false, SELECT_ACL, SELECT_ACL, false, true)) DBUG_RETURN(-1); /* System Versioning: handle FOR SYSTEM_TIME clause. */ @@ -2193,6 +2194,9 @@ JOIN::optimize_inner() trace_prepare.add_select_number(select_lex->select_number); Json_writer_array trace_steps(thd, "steps"); + if (select_lex->opt_hints_qb) + select_lex->opt_hints_qb->trace_hints(thd); + /* Needed in case optimizer short-cuts, set properly in make_aggr_tables_info() @@ -5600,6 +5604,7 @@ make_join_statistics(JOIN *join, List &tables_list, DBUG_ENTER("make_join_statistics"); table_count=join->table_count; + const uint sj_nests= join->select_lex->sj_nests.elements; // Changed by pull-out /* best_extension_by_limited_search need sort space for 2POSITIION @@ -5760,54 +5765,12 @@ make_join_statistics(JOIN *join, List &tables_list, stat_vector[i]=0; join->outer_join=outer_join; - if (join->outer_join) + if (join->propagate_dependencies(stat)) { - /* - Build transitive closure for relation 'to be dependent on'. - This will speed up the plan search for many cases with outer joins, - as well as allow us to catch illegal cross references/ - Warshall's algorithm is used to build the transitive closure. - As we use bitmaps to represent the relation the complexity - of the algorithm is O((number of tables)^2). - - The classic form of the Warshall's algorithm would look like: - for (i= 0; i < table_count; i++) - { - for (j= 0; j < table_count; j++) - { - for (k= 0; k < table_count; k++) - { - if (bitmap_is_set(stat[j].dependent, i) && - bitmap_is_set(stat[i].dependent, k)) - bitmap_set_bit(stat[j].dependent, k); - } - } - } - */ - - for (s= stat ; s < stat_end ; s++) - { - TABLE *table= s->table; - for (JOIN_TAB *t= stat ; t < stat_end ; t++) - { - if (t->dependent & table->map) - t->dependent |= table->reginfo.join_tab->dependent; - } - if (outer_join & s->table->map) - s->table->maybe_null= 1; - } - /* Catch illegal cross references for outer joins */ - for (i= 0, s= stat ; i < table_count ; i++, s++) - { - if (s->dependent & s->table->map) - { - join->table_count=0; // Don't use join->table - my_message(ER_WRONG_OUTER_JOIN, - ER_THD(join->thd, ER_WRONG_OUTER_JOIN), MYF(0)); - goto error; - } - s->key_dependent= s->dependent; - } + // Illegal cross-references found + table_count= 0; + my_message(ER_WRONG_OUTER_JOIN, ER_THD(thd, ER_WRONG_OUTER_JOIN), MYF(0)); + goto error; } { @@ -5821,9 +5784,6 @@ make_join_statistics(JOIN *join, List &tables_list, } } - if (unlikely(thd->trace_started())) - trace_table_dependencies(thd, stat, join->table_count); - if (join->conds || outer_join) { if (update_ref_and_keys(thd, keyuse_array, stat, join->table_count, @@ -6315,6 +6275,16 @@ make_join_statistics(JOIN *join, List &tables_list, join->const_tables=const_count; join->found_const_table_map=found_const_table_map; + if (join->select_lex->opt_hints_qb) + join->select_lex->opt_hints_qb->apply_join_order_hints(join); + join->update_key_dependencies(); + + if (unlikely(thd->trace_started())) + trace_table_dependencies(thd, join->join_tab, join->table_count); + + if (sj_nests) + join->select_lex->update_available_semijoin_strategies(thd); + if (join->const_tables != join->table_count) optimize_keyuse(join, keyuse_array); @@ -6405,6 +6375,102 @@ make_join_statistics(JOIN *join, List &tables_list, } +/* + Propagate dependencies between tables. + + @returns false if success, true if error + + Build transitive closure for relation 'to be dependent on'. + This will speed up the plan search for many cases with outer joins, + as well as allow us to catch illegal cross references/ + Warshall's algorithm is used to build the transitive closure. + As we use bitmaps to represent the relation the complexity + of the algorithm is O((number of tables)^2). + + The classic form of the Warshall's algorithm would look like: + for (i= 0; i < table_count; i++) + { + for (j= 0; j < table_count; j++) + { + for (k= 0; k < table_count; k++) + { + if (bitmap_is_set(stat[j].dependent, i) && + bitmap_is_set(stat[i].dependent, k)) + bitmap_set_bit(stat[j].dependent, k); + } + } + } +*/ + +bool JOIN::propagate_dependencies(JOIN_TAB *stat) +{ + for (JOIN_TAB *s= stat; s < stat + table_count; s++) + { + TABLE *table= s->table; + if (outer_join & s->table->map) + s->table->maybe_null= 1; + + if (!table->reginfo.join_tab->dependent) + continue; + // Add my dependencies to other tables depending on me + for (JOIN_TAB *t= stat; t < stat + table_count; t++) + { + if (t->dependent & table->map) + t->dependent |= table->reginfo.join_tab->dependent; + } + } + // Catch illegal cross references + for (JOIN_TAB *s= stat; s < stat + table_count; s++) + { + if (s->dependent & s->table->map) + return true; + } + return false; +} + + +void JOIN::update_key_dependencies() +{ + for (JOIN_TAB *tab= join_tab; tab < join_tab + table_count; tab++) + tab->key_dependent |= tab->dependent; +} + + +/* + Export dependencies of the JOIN tables to a newly allocated array of bitmaps + (table_map's). + This array may be used to restore the original dependencies + (see restore_table_dependencies()) +*/ + +table_map *JOIN::export_table_dependencies() const +{ + table_map *orig_dep_array= + (table_map *)thd->alloc(sizeof(table_map) * table_count); + + if (orig_dep_array == nullptr) + return nullptr; + + for (uint i= 0; i < table_count; i++) + orig_dep_array[i]= join_tab[i].dependent; + + return orig_dep_array; +} + + +/* + Restore dependencies of the JOIN tables from a previously exported array + of bitmaps (table_map's) (see export_table_dependencies()). + This function overwrites the existing dependencies with those from the array. +*/ + +void JOIN::restore_table_dependencies(table_map *orig_dep_array) +{ + for (uint i = 0; i < table_count; i++) + join_tab[i].dependent= orig_dep_array[i]; +} + + /***************************************************************************** Check with keys are used and with tables references with tables Updates in stat: @@ -6661,7 +6727,7 @@ add_key_field(JOIN *join, { uint optimize= 0; if (eq_func && - ((join->is_allowed_hash_join_access() && + ((join->is_allowed_hash_join_access(field->table) && field->hash_join_is_possible() && !(field->table->pos_in_table_list->is_materialized_derived() && field->table->is_created())) || @@ -9427,8 +9493,7 @@ best_access_path(JOIN *join, (2) s is inner table of outer join -> join cache is allowed for outer joins */ if (idx > join->const_tables && best.key == 0 && - (join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && - join->max_allowed_join_cache_level > 2 && + join->is_allowed_hash_join_access(table) && !bitmap_is_clear_all(eq_join_set) && !disable_jbuf && (!s->emb_sj_nest || join->allowed_semijoin_with_cache) && // (1) @@ -11467,15 +11532,19 @@ get_costs_for_tables(JOIN *join, table_map remaining_tables, uint idx, JOIN_TAB *s; table_map found_tables= 0; bool found_eq_ref= 0; - bool disable_jbuf= join->thd->variables.join_cache_level == 0; DBUG_ENTER("get_plans_for_tables"); + table_map remaining_allowed_tables= + (join->emb_sjm_nest ? + (join->emb_sjm_nest->sj_inner_tables & + ~join->const_table_map & remaining_tables): + remaining_tables); s= *pos; do { table_map real_table_bit= s->table->map; if ((*allowed_tables & real_table_bit) && - !(remaining_tables & s->dependent)) + !(remaining_allowed_tables & s->dependent)) { #ifdef DBUG_ASSERT_EXISTS DBUG_ASSERT(!check_interleaving_with_nj(s)); @@ -11484,6 +11553,11 @@ get_costs_for_tables(JOIN *join, table_map remaining_tables, uint idx, sort_end->join_tab= pos; sort_end->position= sort_position; + bool hint_forces_jbuf= + hint_table_state(join->thd, s->table, BNL_HINT_ENUM, false); + + bool disable_jbuf= + (join->thd->variables.join_cache_level == 0) && !hint_forces_jbuf; Json_writer_object wrapper(thd); /* Find the best access method from 's' to the current partial plan */ @@ -14463,11 +14537,17 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) add_cond_and_fix(thd, &tmp, tab->select_cond); } + uint max_jcl= join->max_allowed_join_cache_level; + bool is_hash_allowed= join->allowed_join_cache_types & + JOIN_CACHE_HASHED_BIT; + bool is_bnlh_enabled= ((max_jcl == 3 || max_jcl == 4) && + is_hash_allowed) || + hint_table_state(thd, tab->table, BNL_HINT_ENUM, false); + bool is_bkah_enabled= (max_jcl > 4 && is_hash_allowed) || + hint_table_state(thd, tab->table, BKA_HINT_ENUM, false); is_hj= (tab->type == JT_REF || tab->type == JT_EQ_REF) && - (join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && - ((join->max_allowed_join_cache_level+1)/2 == 2 || - ((join->max_allowed_join_cache_level+1)/2 > 2 && - is_hash_join_key_no(tab->ref.key))) && + (is_bnlh_enabled || + (is_bkah_enabled && is_hash_join_key_no(tab->ref.key))) && (!tab->emb_sj_nest || join->allowed_semijoin_with_cache) && (!(tab->table->map & join->outer_join) || @@ -15511,11 +15591,13 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) join_cache_hashed, join_cache_bka, are set on or off - the join cache level set for the query - - the join 'options'. + - the join 'options' + - combination of optimizer hints (see section "Optimizer hints" below). In any case join buffer is not used if the number of the joined table is - greater than 'no_jbuf_after'. It's also never used if the value of - join_cache_level is equal to 0. + greater than 'no_jbuf_after'. It's also not used if the value of + join_cache_level is equal to 0 and BNL() hint is not specified for + the table (see section below). If the optimizer switch outer_join_with_cache is off no join buffer is used for outer join operations. If the optimizer switch semijoin_with_cache is off no join buffer is used @@ -15525,10 +15607,12 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) If the optimizer switch join_cache_hashed is off then the optimizer uses neither BNLH algorithm, nor BKAH algorithm to perform join operations. - If the optimizer switch join_cache_bka is off then the optimizer uses - neither BKA algorithm, nor BKAH algorithm to perform join operation. + If the optimizer switch join_cache_bka is off and BKA() hint is + not specified then, the optimizer uses neither BKA algorithm, + nor BKAH algorithm to perform join operation. The valid settings for join_cache_level lay in the interval 0..8. - If it set to 0 no join buffers are used to perform join operations. + If it set to 0 and BNL() hint is not specified, no join buffers are used + to perform join operations. Currently we differentiate between join caches of 8 levels: 1 : non-incremental join cache used for BNL join algorithm 2 : incremental join cache used for BNL join algorithm @@ -15546,14 +15630,18 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) the following rules are applied. If join_cache_level==1|2 then join buffer is used for inner joins, outer joins and semi-joins with 'JT_ALL' access method. In this case a - JOIN_CACHE_BNL object is employed. + JOIN_CACHE_BNL object is employed (unless NO_BNL() hint is specified + for the table). If join_cache_level==3|4 and then join buffer is used for a join operation (inner join, outer join, semi-join) with 'JT_REF'/'JT_EQREF' access method - then a JOIN_CACHE_BNLH object is employed. + then a JOIN_CACHE_BNLH object is employed (unless NO_BNL() hint + is specified). If an index is used to access rows of the joined table and the value of - join_cache_level==5|6 then a JOIN_CACHE_BKA object is employed. + join_cache_level==5|6 then a JOIN_CACHE_BKA object is employed (unless + NO_BKA() hint is specified). If an index is used to access rows of the joined table and the value of - join_cache_level==7|8 then a JOIN_CACHE_BKAH object is employed. + join_cache_level==7|8 then a JOIN_CACHE_BKAH object is employed (unless + NO_BKA() hint is specified). If the value of join_cache_level is odd then creation of a non-linked join cache is forced. @@ -15581,6 +15669,27 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) The functions changes the value the fields tab->icp_other_tables_ok and tab->idx_cond_fact_out to FALSE if the chosen join cache algorithm requires it. + + OPTIMIZER_HINTS + The following hints may influence the choice of join buffering: + BNL(t1,t2,..): enables BNL and BNLH buffers for the specified tables + when join_cache_level < 4. It effectively increases + join_cache_level to 4 for given tables. + NO_BNL(t1,t2,..): disables BNL/BNLH join buffers, which could have been + chosen for the specified tables otherwise. Does not + prevent employing of BKA/BKAH buffers + BKA(t1,t2,..): enables BKA and BKAH buffers for the specified tables + when join_cache_level < 5 and/or + optimizer switch join_cache_bka=off. + NO_BKA(t1,t2,..): disables BKA/BKAH join buffers, which could have been + chosen for the specified tables otherwise. However, + does not prevent employing of BNL/BNLH buffers. + + Optimizer hints do not break the rules of join buffer application, such as + a chain of linked buffers or applying buffers to an outer join tables like + in the case described in Notes below. + If a hint cannot be applied it is either ignored or other tables' join + buffering choices are revised to ensure the consistency of buffers chains. NOTES An inner table of a nested outer join or a nested semi-join can be currently @@ -15640,20 +15749,37 @@ uint check_join_cache_usage(JOIN_TAB *tab, !(join->allowed_join_cache_types & JOIN_CACHE_INCREMENTAL_BIT); bool no_hashed_cache= !(join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT); - bool no_bka_cache= - !(join->allowed_join_cache_types & JOIN_CACHE_BKA_BIT); - + bool hint_disables_bnl= !hint_table_state(join->thd, tab->tab_list->table, + BNL_HINT_ENUM, true); + bool no_bka_cache= !hint_table_state(join->thd, tab->tab_list->table, + BKA_HINT_ENUM, join->allowed_join_cache_types & JOIN_CACHE_BKA_BIT); + bool hint_forces_bka= hint_table_state(join->thd, tab->tab_list->table, + BKA_HINT_ENUM, false); join->return_tab= 0; - if (tab->no_forced_join_cache) + if (tab->no_forced_join_cache || (hint_disables_bnl && no_bka_cache)) goto no_join_cache; + if (cache_level < 4 && hint_table_state(join->thd, tab->tab_list->table, + BNL_HINT_ENUM, false)) + { + cache_level= 4; // BNL() hint present, raise join_cache_level to BNLH + } + /* - Don't use join cache if @@join_cache_level==0 or this table is the first + Don't use join cache if @@join_cache_level==0 and hint BKA() + is not specified or this table is the first one join suborder (either at top level or inside a bush) */ - if (cache_level == 0 || !prev_tab) - return 0; + if ((cache_level == 0 && !hint_forces_bka) || !prev_tab) + { + /* + We could have cache_level==0 but join cache was forced for some previous + table (PT) with a hint. Proceed to cancel join cache for PT if it is not + allowed to use join cache for PT without using it for this table. + */ + goto no_join_cache; + } if (force_unlinked_cache && (cache_level%2 == 0)) cache_level--; @@ -15732,6 +15858,12 @@ uint check_join_cache_usage(JOIN_TAB *tab, if (tab->loosescan_match_tab || tab->bush_children) goto no_join_cache; + /* + An inner table of an outer join nest must not use join buffering if + the first inner table of that outer join nest does not use join buffering + or if the tables in the embedding outer join nest do not use join buffering. + Also see revise_cache_usage() + */ for (JOIN_TAB *first_inner= tab->first_inner; first_inner; first_inner= first_inner->first_upper) { @@ -15769,6 +15901,8 @@ uint check_join_cache_usage(JOIN_TAB *tab, case JT_NEXT: case JT_ALL: case JT_RANGE: + if (hint_disables_bnl) + goto no_join_cache; if (cache_level == 1) prev_cache= 0; if ((tab->cache= new (root) JOIN_CACHE_BNL(join, tab, prev_cache))) @@ -15784,6 +15918,8 @@ uint check_join_cache_usage(JOIN_TAB *tab, case JT_CONST: case JT_REF: case JT_EQ_REF: + if (hint_forces_bka) + cache_level= 8; // Increase to BKAH incremental if (cache_level <=2 || (no_hashed_cache && no_bka_cache)) goto no_join_cache; if (tab->ref.is_access_triggered()) @@ -15805,6 +15941,8 @@ uint check_join_cache_usage(JOIN_TAB *tab, tab->is_ref_for_hash_join() || ((flags & HA_MRR_NO_ASSOCIATION) && cache_level <=6)) { + if (hint_disables_bnl) + goto no_join_cache; if (!tab->hash_join_is_possible() || tab->make_scan_filter()) goto no_join_cache; @@ -32059,6 +32197,11 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) return; } + if (sel_type == SELECT_CMD || + sel_type == INSERT_CMD || + sel_type == REPLACE_CMD) + print_hints(thd, str); + /* First add options */ if (options & SELECT_STRAIGHT_JOIN) str->append(STRING_WITH_LEN("straight_join ")); @@ -32112,7 +32255,10 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) query_type); } if (sel_type == UPDATE_CMD || sel_type == DELETE_CMD) + { str->append(get_explainable_cmd_name(sel_type)); + print_hints(thd, str); + } if (sel_type == DELETE_CMD) { str->append(STRING_WITH_LEN(" from ")); @@ -32238,6 +32384,36 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) } +void st_select_lex::print_hints(THD *thd, + String *str) +{ + if (!thd->lex->opt_hints_global) + return; + + constexpr LEX_CSTRING header={STRING_WITH_LEN("/*+ ")}; + str->append(header); + uint32 len_before_hints= str->length(); + if (select_number == 1) + { + if (opt_hints_qb) + opt_hints_qb->append_qb_hint(thd, str); + thd->lex->opt_hints_global->print(thd, str); + } + else if (opt_hints_qb) + opt_hints_qb->append_qb_hint(thd, str); + + if (str->length() > len_before_hints) + { + // Some hints were printed, close the hint string + str->append(STRING_WITH_LEN("*/ ")); + } + else + { + // No hints were added, rollback the previouly added header + str->length(len_before_hints - header.length); + } +} + /** Change the select_result object of the JOIN. @@ -32301,6 +32477,22 @@ void JOIN::set_allowed_join_cache_types() max_allowed_join_cache_level= thd->variables.join_cache_level; } +bool JOIN::is_allowed_hash_join_access(const TABLE *table) +{ + /* + If both NO_BNL() and NO_BKA() hints are specified then + hash join is not allowed + */ + if (!hint_table_state(thd, table, BNL_HINT_ENUM, true) && + !hint_table_state(thd, table, BKA_HINT_ENUM, true)) + { + return false; + } + return allowed_join_cache_types & JOIN_CACHE_HASHED_BIT && + (max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT || + hint_table_state(thd, table, BNL_HINT_ENUM, false) || + hint_table_state(thd, table, BKA_HINT_ENUM, false)); +} /** Save a query execution plan so that the caller can revert to it if needed, diff --git a/sql/sql_select.h b/sql/sql_select.h index b20a48eaaa304..415ef5b83955b 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -378,8 +378,9 @@ typedef struct st_join_table { */ table_map dependent; /* - key_dependent is dependent but add those tables that are used to compare - with a key field in a simple expression. See add_key_field(). + Normally `key_dependent` is the same as `dependent` but may also include + tables that are used to compare with a key field in a simple expression + (see add_key_field()). It is only used to prune searches in best_extension_by_limited_search() */ table_map key_dependent; @@ -805,6 +806,12 @@ class Semi_join_strategy_picker virtual void mark_used() = 0; + /* + Returns TRUE if the strategy is disabled by either optimizer switch + setting or an optimizer hint + */ + virtual bool is_disabled() const { return false; } + virtual ~Semi_join_strategy_picker() = default; }; @@ -825,12 +832,15 @@ class Duplicate_weedout_picker : public Semi_join_strategy_picker table_map dupsweedout_tables; bool is_used; + + bool disabled; // See comment for Semi_join_strategy_picker::is_disabled() public: void set_empty() override { dupsweedout_tables= 0; first_dupsweedout_table= MAX_TABLES; is_used= FALSE; + disabled= FALSE; } void set_from_prev(POSITION *prev) override; @@ -845,6 +855,9 @@ class Duplicate_weedout_picker : public Semi_join_strategy_picker POSITION *loose_scan_pos) override; void mark_used() override { is_used= TRUE; } + + bool is_disabled() const override { return disabled; } + friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join); }; @@ -1885,11 +1898,7 @@ class JOIN :public Sql_alloc ulonglong curr_space, ulonglong needed_space); void set_allowed_join_cache_types(); - bool is_allowed_hash_join_access() - { - return MY_TEST(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) && - max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; - } + bool is_allowed_hash_join_access(const TABLE *table); /* Check if we need to create a temporary table. This has to be done if all tables are not already read (const tables) @@ -1947,6 +1956,10 @@ class JOIN :public Sql_alloc bool optimize_upper_rownum_func(); void calc_allowed_top_level_tables(SELECT_LEX *lex); table_map get_allowed_nj_tables(uint idx); + bool propagate_dependencies(JOIN_TAB *stat); + void update_key_dependencies(); + table_map *export_table_dependencies() const; + void restore_table_dependencies(table_map *orig_dep_array); private: /** diff --git a/sql/sql_string.h b/sql/sql_string.h index 9f97923c194b1..b2f9fe3a8fbd5 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -1015,7 +1015,6 @@ class String: public Charset, public Binary_string { return Binary_string::append(s); } - inline bool append(char chr) { return Binary_string::append_char(chr); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 42172a5947ed5..a747857c147de 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1666,7 +1666,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) if (setup_tables_and_check_access(thd, &select_lex->context, &select_lex->top_join_list, table_list, select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) + false, UPDATE_ACL, SELECT_ACL, true, false)) DBUG_RETURN(1); if (table_list->has_period() && @@ -3105,7 +3105,7 @@ bool Sql_cmd_update::prepare_inner(THD *thd) DBUG_RETURN(TRUE); if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, select_lex->leaf_tables, false, false)) + table_list, select_lex->leaf_tables, false, false, true)) DBUG_RETURN(TRUE); if (select_lex->vers_setup_conds(thd, table_list)) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c95af7682e01b..55f26fcf0eef4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -206,6 +206,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)() /* structs */ LEX_CSTRING lex_str; + Lex_comment_st lex_comment; Lex_ident_cli_st kwd; Lex_ident_cli_st ident_cli; Lex_ident_sys_st ident_sys; @@ -278,6 +279,7 @@ void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)() TABLE_LIST *table_list; Table_ident *table; Qualified_column_ident *qualified_column_ident; + Optimizer_hint_parser_output *opt_hints; char *simple_string; const char *const_simple_string; chooser_compare_func_creator boolfunc2creator; @@ -385,6 +387,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token '@' +%token HINT_COMMENT + /* Special purpose tokens */ @@ -1334,6 +1338,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left EMPTY_FROM_CLAUSE %right INTO +%type + HINT_COMMENT opt_hint_comment + %type DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM HEX_STRING @@ -1598,6 +1605,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type expr_lex +%type + opt_optimizer_hint + %destructor { /* @@ -8964,14 +8974,30 @@ table_value_constructor: } ; +opt_hint_comment: + /*empty */ { $$.init(); } + | HINT_COMMENT { $$= $1; } + ; + +opt_optimizer_hint: + { YYLIP->hint_comment= true; } + opt_hint_comment + { + YYLIP->hint_comment= false; + if (!($$= Lex->parse_optimizer_hints($2)) && thd->is_error()) + MYSQL_YYABORT; + } + ; + query_specification_start: - SELECT_SYM + SELECT_SYM opt_optimizer_hint { SELECT_LEX *sel; LEX *lex= Lex; if (!(sel= lex->alloc_select(TRUE)) || lex->push_select(sel)) MYSQL_YYABORT; sel->init_select(); + sel->set_optimizer_hints($2); sel->braces= FALSE; } select_options @@ -8992,6 +9018,7 @@ query_specification: opt_having_clause opt_window_clause { + Lex->resolve_optimizer_hints_in_last_select(); $$= Lex->pop_select(); } ; @@ -9005,6 +9032,7 @@ select_into_query_specification: opt_having_clause opt_window_clause { + Lex->resolve_optimizer_hints_in_last_select(); $$= Lex->pop_select(); } ; @@ -13569,7 +13597,7 @@ opt_temporary: */ insert: - INSERT + INSERT opt_optimizer_hint { Lex->sql_command= SQLCOM_INSERT; Lex->duplicates= DUP_ERROR; @@ -13578,18 +13606,20 @@ insert: } insert_start insert_lock_option opt_ignore opt_into insert_table { - Select->set_lock_for_tables($4, true, false); + Lex->first_select_lex()->set_optimizer_hints($2); + Select->set_lock_for_tables($5, true, false); } insert_field_spec opt_insert_update opt_returning - stmt_end + insert_stmt_end { + Lex->resolve_optimizer_hints_in_last_select(); Lex->mark_first_table_as_inserting(); thd->get_stmt_da()->reset_current_row_for_warning(0); } ; replace: - REPLACE + REPLACE opt_optimizer_hint { Lex->sql_command = SQLCOM_REPLACE; Lex->duplicates= DUP_REPLACE; @@ -13598,11 +13628,13 @@ replace: } insert_start replace_lock_option opt_into insert_table { - Select->set_lock_for_tables($4, true, false); + Lex->first_select_lex()->set_optimizer_hints($2); + Select->set_lock_for_tables($5, true, false); } insert_field_spec opt_returning - stmt_end + insert_stmt_end { + Lex->resolve_optimizer_hints_in_last_select(); Lex->mark_first_table_as_inserting(); thd->get_stmt_da()->reset_current_row_for_warning(0); } @@ -13618,6 +13650,14 @@ insert_start: { ; stmt_end: { + Lex->resolve_optimizer_hints_in_last_select(); + Lex->pop_select(); //main select + if (Lex->check_main_unit_semantics()) + MYSQL_YYABORT; + } + ; + +insert_stmt_end: { Lex->pop_select(); //main select if (Lex->check_main_unit_semantics()) MYSQL_YYABORT; @@ -13873,12 +13913,13 @@ update_table_list: /* Update rows in a table */ update: - UPDATE_SYM + UPDATE_SYM opt_optimizer_hint { LEX *lex= Lex; if (Lex->main_select_push()) MYSQL_YYABORT; lex->init_select(); + Lex->first_select_lex()->set_optimizer_hints($2); lex->sql_command= SQLCOM_UPDATE; lex->duplicates= DUP_ERROR; } @@ -13908,12 +13949,12 @@ update: be too pessimistic. We will decrease lock level if possible later while processing the statement. */ - slex->set_lock_for_tables($3, slex->table_list.elements == 1, false); + slex->set_lock_for_tables($4, slex->table_list.elements == 1, false); } opt_where_clause opt_order_clause delete_limit_clause { - if ($10) - Select->order_list= *($10); + if ($11) + Select->order_list= *($11); } stmt_end {} ; @@ -13960,7 +14001,7 @@ opt_low_priority: /* Delete rows from a table */ delete: - DELETE_SYM + DELETE_SYM opt_optimizer_hint { LEX *lex= Lex; YYPS->m_lock_type= TL_WRITE_DEFAULT; @@ -13970,11 +14011,13 @@ delete: mysql_init_delete(lex); lex->ignore= 0; lex->first_select_lex()->order_list.empty(); + lex->first_select_lex()->set_optimizer_hints($2); } delete_part2 { if (Lex->check_cte_dependencies_and_resolve_references()) MYSQL_YYABORT; + Lex->resolve_optimizer_hints_in_last_select(); } ; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 99c8300748426..3c51512d2be8f 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3015,7 +3015,8 @@ export const char *optimizer_switch_names[]= "index_merge_intersection","index_merge_sort_intersection", "index_condition_pushdown", "derived_merge", "derived_with_keys", - "firstmatch","loosescan","materialization","in_to_exists","semijoin", + "firstmatch","loosescan","duplicateweedout","materialization", + "in_to_exists","semijoin", "partial_match_rowid_merge", "partial_match_table_scan", "subquery_cache", diff --git a/sql/table.h b/sql/table.h index f398fe697d1f4..934259c6d5350 100644 --- a/sql/table.h +++ b/sql/table.h @@ -84,6 +84,8 @@ class Table_function_json_table; class Open_table_context; class MYSQL_LOG; struct rpl_group_info; +class Opt_hints_qb; +class Opt_hints_table; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -2967,6 +2969,11 @@ struct TABLE_LIST List *partition_names; #endif /* WITH_PARTITION_STORAGE_ENGINE */ + /** Table level optimizer hints for this table. */ + Opt_hints_table *opt_hints_table; + /* Hints for query block of this table. */ + Opt_hints_qb *opt_hints_qb; + void calc_md5(char *buffer); int view_check_option(THD *thd, bool ignore_failure); bool create_field_translation(THD *thd); @@ -3415,6 +3422,10 @@ typedef struct st_nested_join table_map sj_corr_tables; table_map direct_children_map; List sj_outer_expr_list; + + /// Bitmap of which strategies are enabled for this semi-join nest + uint sj_enabled_strategies; + /** True if this join nest node is completely covered by the query execution plan. This means two things. diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index b131b9817ede7..6b4da3c1747c7 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -20050,6 +20050,33 @@ static void test_bug17512527() } #endif +/** + Parser for optimizer hints +*/ +static void test_optimizer_hints() +{ + MYSQL_RES *result; + int rc; + + myheader("test_optimizer_hints"); + + rc= mysql_query(mysql, "SELECT /*+ "); + DIE_UNLESS(rc); + + rc= mysql_query(mysql, "SELECT /*+ ICP(`test"); + DIE_UNLESS(rc); + + rc= mysql_query(mysql, "SELECT /*+ ICP(`test*/ 1"); + myquery(rc); + result= mysql_store_result(mysql); + mytest(result); + (void) my_process_result_set(result); + mysql_free_result(result); + + rc= mysql_query(mysql, "SELECT /*+ ICP(`test*/`*/ 1"); + DIE_UNLESS(rc); +} + /* Check compressed protocol @@ -23252,6 +23279,7 @@ static struct my_tests_st my_tests[]= { #ifndef _WIN32 { "test_bug17512527", test_bug17512527}, #endif + { "test_optimizer_hints", test_optimizer_hints}, { "test_compressed_protocol", test_compressed_protocol }, { "test_big_packet", test_big_packet }, { "test_prepare_analyze", test_prepare_analyze },