diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index 8574b3da666..79c1ca56256 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -4823,7 +4823,7 @@ explain select t2.f1, t2.f2, t2.f3 from t1,t2 where t1.f1=t2.f1 and t2.f2 between t1.f1 and t2.f2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where -1 SIMPLE t2 ref f1 f1 4 test.t1.f1 3 Using index condition(BKA); Using join buffer +1 SIMPLE t2 ref f1 f1 4 test.t1.f1 3 Using index condition(BKA); Using where; Using join buffer set join_cache_level=8; 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; @@ -4836,7 +4836,7 @@ explain select t2.f1, t2.f2, t2.f3 from t1,t2 where t1.f1=t2.f1 and t2.f2 between t1.f1 and t2.f2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where -1 SIMPLE t2 ref f1 f1 4 test.t1.f1 3 Using index condition(BKA); Using join buffer +1 SIMPLE t2 ref f1 f1 4 test.t1.f1 3 Using index condition(BKA); Using where; Using join buffer drop table t1,t2; set join_cache_level=default; # @@ -5303,4 +5303,31 @@ NULL NULL set join_cache_level = default; DROP TABLE t1,t2; +# +# BUG#54359 "Extra rows with join_cache_level=7,8 and two joins +# --and multi-column index" +# +CREATE TABLE t1 ( +pk int NOT NULL, +a int DEFAULT NULL, +b varchar(16) DEFAULT NULL, +c varchar(16) DEFAULT NULL, +INDEX idx (b,a)) +; +INSERT INTO t1 VALUES (4,9,'k','k'); +INSERT INTO t1 VALUES (12,5,'k','k'); +set join_cache_level = 8; +EXPLAIN +SELECT t.a FROM t1 t, t1 s FORCE INDEX(idx) +WHERE s.pk AND s.a >= t.pk AND s.b = t.c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t ALL NULL NULL NULL NULL 2 Using where +1 SIMPLE s ref idx idx 19 test.t.c 1 Using index condition(BKA); Using where; Using join buffer +SELECT t.a FROM t1 t, t1 s FORCE INDEX(idx) +WHERE s.pk AND s.a >= t.pk AND s.b = t.c; +a +9 +9 +set join_cache_level = default; +DROP TABLE t1; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test index a71db1081a1..c9a6673c8c3 100755 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -2076,5 +2076,33 @@ SELECT t2.a FROM t1 LEFT JOIN t2 ON t2.b = t1.b; set join_cache_level = default; DROP TABLE t1,t2; +--echo # +--echo # BUG#54359 "Extra rows with join_cache_level=7,8 and two joins +--echo # --and multi-column index" +--echo # + +CREATE TABLE t1 ( + pk int NOT NULL, + a int DEFAULT NULL, + b varchar(16) DEFAULT NULL, + c varchar(16) DEFAULT NULL, + INDEX idx (b,a)) +; + +INSERT INTO t1 VALUES (4,9,'k','k'); +INSERT INTO t1 VALUES (12,5,'k','k'); + +set join_cache_level = 8; + +EXPLAIN +SELECT t.a FROM t1 t, t1 s FORCE INDEX(idx) + WHERE s.pk AND s.a >= t.pk AND s.b = t.c; + +SELECT t.a FROM t1 t, t1 s FORCE INDEX(idx) + WHERE s.pk AND s.a >= t.pk AND s.b = t.c; + +set join_cache_level = default; +DROP TABLE t1; + # this must be the last command in the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index 277343b81a5..cfd4ca246ce 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -272,12 +272,14 @@ Item *make_cond_remainder(Item *cond, bool exclude_index) in tab->select_cond keyno Index for which extract and push the condition other_tbls_ok TRUE <=> Fields of other non-const tables are allowed + factor_out TRUE <=> Factor out the extracted condition DESCRIPTION Try to extract and push the index condition down to table handler */ -void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok) +void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok, + bool factor_out) { DBUG_ENTER("push_index_cond"); Item *idx_cond; @@ -350,7 +352,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok) if (idx_remainder_cond != idx_cond) tab->ref.disable_cache= TRUE; - Item *row_cond= make_cond_remainder(tab->select_cond, TRUE); + Item *row_cond= factor_out ? make_cond_remainder(tab->select_cond, TRUE) : + tab->pre_idx_push_select_cond; DBUG_EXECUTE("where", print_where(row_cond, "remainder cond", QT_ORDINARY);); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bd50ef0f31d..7aab9591960 100755 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7422,6 +7422,8 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) no_jbuf_after don't use join buffering after table with this number icp_other_tables_ok OUT TRUE if condition pushdown supports other tables presence + idx_cond_fact_out OUT TRUE if condition pushed to the index is factored + out of the condition pushed to the table DESCRIPTION The function finds out whether the table 'tab' can be joined using a join @@ -7545,7 +7547,8 @@ static uint check_join_cache_usage(JOIN_TAB *tab, JOIN *join, ulonglong options, uint no_jbuf_after, - bool *icp_other_tables_ok) + bool *icp_other_tables_ok, + bool *idx_cond_fact_out) { uint flags; COST_VECT cost; @@ -7562,6 +7565,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, uint i= tab - join->join_tab; *icp_other_tables_ok= TRUE; + *idx_cond_fact_out= TRUE; if (cache_level == 0 || i == join->const_tables) return 0; @@ -7665,7 +7669,10 @@ uint check_join_cache_usage(JOIN_TAB *tab, if ((options & SELECT_DESCRIBE) || ((tab->cache= new JOIN_CACHE_BNLH(join, tab, prev_cache)) && !tab->cache->init())) + { + *icp_other_tables_ok= FALSE; return (4-test(!prev_cache)); + } goto no_join_cache; } if (cache_level > 4 && no_bka_cache) @@ -7694,7 +7701,10 @@ uint check_join_cache_usage(JOIN_TAB *tab, if ((options & SELECT_DESCRIBE) || ((tab->cache= new JOIN_CACHE_BKAH(join, tab, flags, prev_cache)) && !tab->cache->init())) + { + *idx_cond_fact_out= FALSE; return (8-test(!prev_cache)); + } goto no_join_cache; } } @@ -7754,6 +7764,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) JOIN_TAB *tab=join->join_tab+i; TABLE *table=tab->table; bool icp_other_tables_ok; + bool idx_cond_fact_out; tab->read_record.table= table; tab->read_record.file=table->file; tab->read_record.unlock_row= rr_unlock_row; @@ -7800,7 +7811,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) tab->read_first_record= tab->type == JT_SYSTEM ? join_read_system :join_read_const; if ((jcl= check_join_cache_usage(tab, join, options, - no_jbuf_after, &icp_other_tables_ok))) + no_jbuf_after, &icp_other_tables_ok, + &idx_cond_fact_out))) { tab->use_join_cache= TRUE; tab[-1].next_select=sub_select_cache; @@ -7812,13 +7824,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) table->file->extra(HA_EXTRA_KEYREAD); } else if (!jcl || jcl > 4) - push_index_cond(tab, tab->ref.key, icp_other_tables_ok); + push_index_cond(tab, tab->ref.key, + icp_other_tables_ok, idx_cond_fact_out); break; case JT_EQ_REF: tab->read_record.unlock_row= join_read_key_unlock_row; /* fall through */ if ((jcl= check_join_cache_usage(tab, join, options, - no_jbuf_after, &icp_other_tables_ok))) + no_jbuf_after, &icp_other_tables_ok, + &idx_cond_fact_out))) { tab->use_join_cache= TRUE; tab[-1].next_select=sub_select_cache; @@ -7830,7 +7844,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) table->file->extra(HA_EXTRA_KEYREAD); } else if (!jcl || jcl > 4) - push_index_cond(tab, tab->ref.key, icp_other_tables_ok ); + push_index_cond(tab, tab->ref.key, + icp_other_tables_ok, idx_cond_fact_out); break; case JT_REF_OR_NULL: case JT_REF: @@ -7842,7 +7857,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) delete tab->quick; tab->quick=0; if ((jcl= check_join_cache_usage(tab, join, options, - no_jbuf_after, &icp_other_tables_ok))) + no_jbuf_after, &icp_other_tables_ok, + &idx_cond_fact_out))) { tab->use_join_cache= TRUE; tab[-1].next_select=sub_select_cache; @@ -7851,7 +7867,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) !table->no_keyread) table->enable_keyread(); else if (!jcl || jcl > 4) - push_index_cond(tab, tab->ref.key, icp_other_tables_ok); + push_index_cond(tab, tab->ref.key, + icp_other_tables_ok, idx_cond_fact_out); break; case JT_ALL: /* @@ -7861,7 +7878,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) materialization nest. */ if (check_join_cache_usage(tab, join, options, no_jbuf_after, - &icp_other_tables_ok)) + &icp_other_tables_ok, &idx_cond_fact_out)) { tab->use_join_cache= TRUE; tab[-1].next_select=sub_select_cache; @@ -7938,7 +7955,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) } if (tab->select && tab->select->quick && tab->select->quick->index != MAX_KEY && ! tab->table->key_read) - push_index_cond(tab, tab->select->quick->index, icp_other_tables_ok); + push_index_cond(tab, tab->select->quick->index, + icp_other_tables_ok, idx_cond_fact_out); } break; case JT_FT: diff --git a/sql/sql_select.h b/sql/sql_select.h index b78603ca9ca..6262f0de01e 100755 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2449,7 +2449,8 @@ inline bool optimizer_flag(THD *thd, uint flag) void eliminate_tables(JOIN *join); /* Index Condition Pushdown entry point function */ -void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok); +void push_index_cond(JOIN_TAB *tab, uint keyno, bool other_tbls_ok, + bool factor_out); /**************************************************************************** Temporary table support for SQL Runtime