From 9b67de47972d2d7a40f2fe7a8898e3cbef9886a7 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 22 Jul 2013 00:55:06 +0500 Subject: [PATCH 1/5] MDEV-4478 check mysql-5.5 changes in spatial.cc. not_enough_points() introduced to check if the spatial object is incorrect. --- sql/spatial.cc | 88 ++++++++++++++++++++++---------------------------- sql/spatial.h | 24 +++++++++++--- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/sql/spatial.cc b/sql/spatial.cc index 52110960f96..95579b5dea2 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -394,19 +394,18 @@ const char *Geometry::append_points(String *txt, uint32 n_points, const char *Geometry::get_mbr_for_points(MBR *mbr, const char *data, uint offset) const { - uint32 n_points; + uint32 points; /* read number of points */ if (no_data(data, 4)) return 0; - n_points= uint4korr(data); + points= uint4korr(data); data+= 4; - if (n_points > max_n_points || - no_data(data, (POINT_DATA_SIZE + offset) * n_points)) + if (not_enough_points(data, points, offset)) return 0; /* Calculate MBR for points */ - while (n_points--) + while (points--) { data+= offset; mbr->add_xy(data, data + SIZEOF_STORED_DOUBLE); @@ -485,12 +484,16 @@ const Geometry::Class_info *Gis_point::get_class_info() const uint32 Gis_line_string::get_data_size() const { - uint32 n_points, size; - if (no_data(m_data, 4) || - (n_points= uint4korr(m_data)) > max_n_points || - no_data(m_data, (size= 4 + n_points * POINT_DATA_SIZE))) + uint32 n_points; + if (no_data(m_data, 4)) return GET_SIZE_ERROR; - return size; + + n_points= uint4korr(m_data); + + if (not_enough_points(m_data + 4, n_points)) + return GET_SIZE_ERROR; + + return 4 + n_points * POINT_DATA_SIZE; } @@ -529,9 +532,8 @@ uint Gis_line_string::init_from_wkb(const char *wkb, uint len, const char *wkb_end; Gis_point p; - if (len < 4 || - (n_points= wkb_get_uint(wkb, bo)) < 1 || - n_points > max_n_points) + if (len < 4 || (n_points= wkb_get_uint(wkb, bo)) < 1 || + ((len - 4) / POINT_DATA_SIZE) < n_points) return 0; proper_length= 4 + n_points * POINT_DATA_SIZE; @@ -560,8 +562,8 @@ bool Gis_line_string::get_data_as_wkt(String *txt, const char **end) const n_points= uint4korr(data); data += 4; - if (n_points < 1 || n_points > max_n_points || - no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points) || + if (n_points < 1 || + not_enough_points(data, n_points) || txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1)*2 + 1) * n_points)) return 1; @@ -598,8 +600,7 @@ int Gis_line_string::geom_length(double *len) const return 1; n_points= uint4korr(data); data+= 4; - if (n_points < 1 || n_points > max_n_points || - no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) + if (n_points < 1 || not_enough_points(data, n_points)) return 1; get_point(&prev_x, &prev_y, data); @@ -633,8 +634,7 @@ int Gis_line_string::is_closed(int *closed) const return 0; } data+= 4; - if (n_points == 0 || n_points > max_n_points || - no_data(data, SIZEOF_STORED_DOUBLE * 2 * n_points)) + if (n_points == 0 || not_enough_points(data, n_points)) return 1; /* Get first point */ @@ -669,8 +669,7 @@ int Gis_line_string::end_point(String *result) const if (no_data(m_data, 4)) return 1; n_points= uint4korr(m_data); - if (n_points == 0 || n_points > max_n_points || - no_data(m_data, POINT_DATA_SIZE * n_points)) + if (n_points == 0 || not_enough_points(m_data+4, n_points)) return 1; return create_point(result, m_data + 4 + (n_points - 1) * POINT_DATA_SIZE); } @@ -683,9 +682,7 @@ int Gis_line_string::point_n(uint32 num, String *result) const return 1; num--; n_points= uint4korr(m_data); - if (num >= n_points || - num > max_n_points || // means (num > n_points || num < 1) - no_data(m_data, num * POINT_DATA_SIZE)) + if (num >= n_points || not_enough_points(m_data+4, n_points)) return 1; return create_point(result, m_data + 4 + num*POINT_DATA_SIZE); @@ -713,7 +710,7 @@ uint32 Gis_polygon::get_data_size() const while (n_linear_rings--) { if (no_data(data, 4) || - (n_points= uint4korr(data)) > max_n_points) + not_enough_points(data+4, n_points= uint4korr(data))) return GET_SIZE_ERROR; data+= 4 + n_points*POINT_DATA_SIZE; } @@ -813,8 +810,7 @@ bool Gis_polygon::get_data_as_wkt(String *txt, const char **end) const return 1; n_points= uint4korr(data); data+= 4; - if (n_points > max_n_points || - no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points) || + if (not_enough_points(data, n_points) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); @@ -868,8 +864,8 @@ int Gis_polygon::area(double *ar, const char **end_of_data) const if (no_data(data, 4)) return 1; n_points= uint4korr(data); - if (n_points == 0 || n_points > max_n_points || - no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) + if (n_points == 0 || + not_enough_points(data, n_points)) return 1; get_point(&prev_x, &prev_y, data+4); data+= (4+SIZEOF_STORED_DOUBLE*2); @@ -905,8 +901,7 @@ int Gis_polygon::exterior_ring(String *result) const n_points= uint4korr(data); data+= 4; length= n_points * POINT_DATA_SIZE; - if (n_points > max_n_points || - no_data(data, length) || result->reserve(1+4+4+ length)) + if (not_enough_points(data, n_points) || result->reserve(1+4+4+ length)) return 1; result->q_append((char) wkb_ndr); @@ -952,7 +947,7 @@ int Gis_polygon::interior_ring_n(uint32 num, String *result) const n_points= uint4korr(data); points_size= n_points * POINT_DATA_SIZE; data+= 4; - if (no_data(data, points_size) || result->reserve(1+4+4+ points_size)) + if (not_enough_points(data, n_points) || result->reserve(1+4+4+ points_size)) return 1; result->q_append((char) wkb_ndr); @@ -989,8 +984,7 @@ int Gis_polygon::centroid_xy(double *x, double *y) const return 1; org_n_points= n_points= uint4korr(data); data+= 4; - if (n_points == 0 || n_points > max_n_points || - no_data(data, (SIZEOF_STORED_DOUBLE*2) * n_points)) + if (n_points == 0 || not_enough_points(data, n_points)) return 1; get_point(&prev_x, &prev_y, data); data+= (SIZEOF_STORED_DOUBLE*2); @@ -1050,13 +1044,12 @@ const Geometry::Class_info *Gis_polygon::get_class_info() const uint32 Gis_multi_point::get_data_size() const { uint32 n_points; - uint32 size; if (no_data(m_data, 4) || - (n_points= uint4korr(m_data)) > max_n_points || - no_data(m_data, (size= 4 + n_points*(POINT_DATA_SIZE + WKB_HEADER_SIZE)))) + not_enough_points(m_data+4, (n_points= uint4korr(m_data)), + WKB_HEADER_SIZE)) return GET_SIZE_ERROR; - return size; + return 4 + n_points * (POINT_DATA_SIZE + WKB_HEADER_SIZE); } @@ -1124,9 +1117,7 @@ bool Gis_multi_point::get_data_as_wkt(String *txt, const char **end) const return 1; n_points= uint4korr(m_data); - if (n_points > max_n_points || - no_data(m_data+4, - n_points * (SIZEOF_STORED_DOUBLE * 2 + WKB_HEADER_SIZE)) || + if (not_enough_points(m_data+4, n_points, WKB_HEADER_SIZE) || txt->reserve(((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; *end= append_points(txt, n_points, m_data+4, WKB_HEADER_SIZE); @@ -1189,7 +1180,8 @@ uint32 Gis_multi_line_string::get_data_size() const while (n_line_strings--) { if (no_data(data, WKB_HEADER_SIZE + 4) || - (n_points= uint4korr(data + WKB_HEADER_SIZE)) > max_n_points) + not_enough_points(data + WKB_HEADER_SIZE+4, + (n_points= uint4korr(data + WKB_HEADER_SIZE)))) return GET_SIZE_ERROR; data+= (WKB_HEADER_SIZE + 4 + n_points*POINT_DATA_SIZE); } @@ -1286,8 +1278,7 @@ bool Gis_multi_line_string::get_data_as_wkt(String *txt, return 1; n_points= uint4korr(data + WKB_HEADER_SIZE); data+= WKB_HEADER_SIZE + 4; - if (n_points > max_n_points || - no_data(data, n_points * (SIZEOF_STORED_DOUBLE*2)) || + if (not_enough_points(data, n_points) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points)) return 1; txt->qs_append('('); @@ -1348,7 +1339,7 @@ int Gis_multi_line_string::geometry_n(uint32 num, String *result) const return 1; n_points= uint4korr(data + WKB_HEADER_SIZE); length= WKB_HEADER_SIZE + 4+ POINT_DATA_SIZE * n_points; - if (n_points > max_n_points || no_data(data, length)) + if (not_enough_points(data+WKB_HEADER_SIZE+4, n_points)) return 1; if (!--num) break; @@ -1448,7 +1439,7 @@ uint32 Gis_multi_polygon::get_data_size() const while (n_linear_rings--) { if (no_data(data, 4) || - (n_points= uint4korr(data)) > max_n_points) + not_enough_points(data+4, (n_points= uint4korr(data)))) return GET_SIZE_ERROR; data+= 4 + n_points * POINT_DATA_SIZE; } @@ -1552,8 +1543,7 @@ bool Gis_multi_polygon::get_data_as_wkt(String *txt, const char **end) const return 1; uint32 n_points= uint4korr(data); data+= 4; - if (n_points > max_n_points || - no_data(data, (SIZEOF_STORED_DOUBLE * 2) * n_points) || + if (not_enough_points(data, n_points) || txt->reserve(2 + ((MAX_DIGITS_IN_DOUBLE + 1) * 2 + 1) * n_points, 512)) return 1; @@ -1636,7 +1626,7 @@ int Gis_multi_polygon::geometry_n(uint32 num, String *result) const if (no_data(data, 4)) return 1; n_points= uint4korr(data); - if (n_points > max_n_points) + if (not_enough_points(data + 4, n_points)) return 1; data+= 4 + POINT_DATA_SIZE * n_points; } diff --git a/sql/spatial.h b/sql/spatial.h index d7632c11143..0b1b8afd35c 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -196,11 +196,6 @@ struct Geometry_buffer; class Geometry { -public: - // Maximum number of points in feature that can fit into String - static const uint32 max_n_points= - (uint32) (INT_MAX32 - WKB_HEADER_SIZE - 4 /* n_points */) / - POINT_DATA_SIZE; public: Geometry() {} /* Remove gcc warning */ virtual ~Geometry() {} /* Remove gcc warning */ @@ -326,6 +321,25 @@ protected: { return (cur_data + data_amount > m_data_end); } + + /** + Check if there're enough points remaining as requested + + Need to perform the calculation in logical units, since multiplication + can overflow the size data type. + + @arg data pointer to the begining of the points array + @arg expected_points number of points expected + @arg extra_point_space extra space for each point element in the array + @return true if there are not enough points + */ + inline bool not_enough_points(const char *data, uint32 expected_points, + uint32 extra_point_space = 0) const + { + return (m_data_end < data || + (expected_points > ((m_data_end - data) / + (POINT_DATA_SIZE + extra_point_space)))); + } const char *m_data; const char *m_data_end; }; From edcae48734058eadd5ed29330b0a525544fa36ed Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Aug 2013 09:25:50 +0300 Subject: [PATCH 2/5] MDEV-4823: Server crashes in Item_func_not::fix_fields on creating a table with a virtual column using NOT fix_field() call protocol was brocken (zero pointer passed as link to item which is possible only if you are sure that there can not be Items which transforms). --- mysql-test/suite/vcol/r/vcol_misc.result | 7 +++++++ mysql-test/suite/vcol/t/vcol_misc.test | 8 ++++++++ sql/table.cc | 10 ++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index 4c301795f5c..bcded410856 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -193,3 +193,10 @@ SELECT COUNT(*) FROM t1; COUNT(*) 2 DROP TABLE t1; +# +# MDEV-4823 Server crashes in Item_func_not::fix_fields on +# creating a table with a virtual column using NOT +# +CREATE TABLE t1 ( f1 INT, v4 INT AS ( NOT f1 ) VIRTUAL ); +drop table t1; +# end of 5.2 tests diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index 0a689795b4c..ca88dedc0f4 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -198,3 +198,11 @@ SELECT COUNT(*) FROM t1; DROP TABLE t1; +--echo # +--echo # MDEV-4823 Server crashes in Item_func_not::fix_fields on +--echo # creating a table with a virtual column using NOT +--echo # +CREATE TABLE t1 ( f1 INT, v4 INT AS ( NOT f1 ) VIRTUAL ); +drop table t1; + +--echo # end of 5.2 tests diff --git a/sql/table.cc b/sql/table.cc index dfc9c2d933c..b9089d58cc7 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1812,7 +1812,7 @@ bool fix_vcol_expr(THD *thd, bool result= TRUE; TABLE_LIST tables; TABLE_LIST *save_table_list, *save_first_table, *save_last_table; - int error; + int error= 0; Name_resolution_context *context; const char *save_where; char* db_name; @@ -1860,7 +1860,13 @@ bool fix_vcol_expr(THD *thd, save_use_only_table_context= thd->lex->use_only_table_context; thd->lex->use_only_table_context= TRUE; /* Fix fields referenced to by the virtual column function */ - error= func_expr->fix_fields(thd, (Item**)0); + if (!func_expr->fixed) + error= func_expr->fix_fields(thd, &vcol_info->expr_item); + + /* fix_fields could change the expression */ + func_expr= vcol_info->expr_item; + /* Number of columns will be checked later */ + /* Restore the original context*/ thd->lex->use_only_table_context= save_use_only_table_context; context->table_list= save_table_list; From 6dd9f049ab2c8771feabec447cff66b703392046 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 13 Aug 2013 15:21:11 -0700 Subject: [PATCH 3/5] Fixed bug mdev-4894. This a an old legacy performance bug. When a very selective range scan existed for the second table in a join, and, at the same time, there was another range condition depending on the fields of the first table, the optimizer chose a plan with 'Range checked for each record'. This plan was extremely inefficient in comparison with the regular selective range scan. As a matter of fact the range scan chosen for each record was the same as that selective range scan. Changed the test case for bug 24776 to preserve the old output for explain. --- mysql-test/r/range.result | 44 +++++++++++++++++++++++++++++++++++++-- mysql-test/t/range.test | 38 +++++++++++++++++++++++++++++++-- sql/sql_select.cc | 11 +++++----- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 5a964baf2c2..579cb3bfb79 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -700,14 +700,14 @@ INSERT INTO t1 VALUES 'd8c4177d09f8b11f5.52725521'); EXPLAIN SELECT s.oxid FROM t1 v, t1 s -WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND +WHERE v.oxrootid ='d8c4177d09f8b11f5.52725521' AND s.oxleft > v.oxleft AND s.oxleft < v.oxright; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE v ref OXLEFT,OXRIGHT,OXROOTID OXROOTID 34 const 5 Using where 1 SIMPLE s ALL OXLEFT NULL NULL NULL 6 Range checked for each record (index map: 0x4) SELECT s.oxid FROM t1 v, t1 s -WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND +WHERE v.oxrootid ='d8c4177d09f8b11f5.52725521' AND s.oxleft > v.oxleft AND s.oxleft < v.oxright; oxid @@ -1884,4 +1884,44 @@ AAA AAA AAA AAAA AAAA AAAA AAAAA AAAAA AAAAA DROP TABLE t1; +# +# mdev-4894: Poor performance with unnecessary +# (bug#70021) 'Range checked for each record' +# +create table t1( key1 int not null, INDEX i1(key1) ); +insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8); +insert into t1 select key1+8 from t1; +insert into t1 select key1+16 from t1; +insert into t1 select key1+32 from t1; +insert into t1 select key1+64 from t1; +insert into t1 select key1+128 from t1; +insert into t1 select key1+256 from t1; +insert into t1 select key1+512 from t1; +alter table t1 add key2 int not null, add index i2(key2); +update t1 set key2=key1; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +create table t2 (a int); +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8); +insert into t2 select a+16 from t2; +insert into t2 select a+32 from t2; +insert into t2 select a+64 from t2; +explain +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < 1000; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 64 +1 SIMPLE t1 range i1,i2 i1 4 NULL 78 Using where +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < 1000; +count(*) +128 +explain +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 64 +1 SIMPLE t1 range i1,i2 i1 4 NULL 78 Using where +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < t2.a; +count(*) +126 +drop table t1,t2; End of 5.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 30d15798336..f781adcec50 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -557,12 +557,12 @@ INSERT INTO t1 VALUES EXPLAIN SELECT s.oxid FROM t1 v, t1 s - WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND + WHERE v.oxrootid ='d8c4177d09f8b11f5.52725521' AND s.oxleft > v.oxleft AND s.oxleft < v.oxright; SELECT s.oxid FROM t1 v, t1 s - WHERE s.oxrootid = 'd8c4177d09f8b11f5.52725521' AND + WHERE v.oxrootid ='d8c4177d09f8b11f5.52725521' AND s.oxleft > v.oxleft AND s.oxleft < v.oxright; @@ -1468,4 +1468,38 @@ SELECT * FROM t1 IGNORE INDEX(PRIMARY) WHERE F1 BETWEEN 'A ' AND DROP TABLE t1; +--echo # +--echo # mdev-4894: Poor performance with unnecessary +--echo # (bug#70021) 'Range checked for each record' +--echo # + +create table t1( key1 int not null, INDEX i1(key1) ); +insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8); +insert into t1 select key1+8 from t1; +insert into t1 select key1+16 from t1; +insert into t1 select key1+32 from t1; +insert into t1 select key1+64 from t1; +insert into t1 select key1+128 from t1; +insert into t1 select key1+256 from t1; +insert into t1 select key1+512 from t1; + +alter table t1 add key2 int not null, add index i2(key2); +update t1 set key2=key1; +analyze table t1; + +create table t2 (a int); +insert into t2 values (1),(2),(3),(4),(5),(6),(7),(8); +insert into t2 select a+16 from t2; +insert into t2 select a+32 from t2; +insert into t2 select a+64 from t2; + +explain +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < 1000; +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < 1000; +explain +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < t2.a; +select count(*) from t2 left join t1 on (t1.key1 < 3 or t1.key1 > 1020) and t1.key2 < t2.a; + +drop table t1,t2; + --echo End of 5.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b76e8afac36..d8bbabd5f63 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6605,19 +6605,18 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) else { sel->needed_reg=tab->needed_reg; - sel->quick_keys.clear_all(); } + sel->quick_keys= tab->table->quick_keys; if (!sel->quick_keys.is_subset(tab->checked_keys) || !sel->needed_reg.is_subset(tab->checked_keys)) { - tab->keys=sel->quick_keys; - tab->keys.merge(sel->needed_reg); tab->use_quick= (!sel->needed_reg.is_clear_all() && - (select->quick_keys.is_clear_all() || - (select->quick && - (select->quick->records >= 100L)))) ? + (sel->quick_keys.is_clear_all() || + (sel->quick && + (sel->quick->records >= 100L)))) ? 2 : 1; sel->read_tables= used_tables & ~current_map; + sel->quick_keys.clear_all(); } if (i != join->const_tables && tab->use_quick != 2) { /* Read with cache */ From fa7f677218ae5ed38ef37b7b61140a069c1f8a44 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 15 Aug 2013 14:16:16 -0700 Subject: [PATCH 4/5] Fixed bug mdev-4355. This patch almost totally revised the patch for bug mdev-4177. The latter had too many defects. In particular, it did not propagate multiple equalities formed when merging a degenerate disjunct into underlying AND formula. --- mysql-test/r/select.result | 52 +++- mysql-test/r/select_jcl6.result | 52 +++- mysql-test/r/select_pkeycache.result | 52 +++- mysql-test/r/subselect_sj2_mat.result | 2 +- mysql-test/t/select.test | 32 ++- sql/item_cmpfunc.cc | 61 +++-- sql/item_cmpfunc.h | 5 +- sql/sql_select.cc | 361 ++++++++++++++++++++------ 8 files changed, 510 insertions(+), 107 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 953b1409827..dfc5236f794 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -5155,7 +5155,7 @@ a b c 8 8 8 DROP TABLE t1,t2; # -# Bug mdev-4413: another manifestations of bug mdev-2474 +# Bug mdev-4413: another manifestations of bug mdev-4274 # (valgrind complains) # CREATE TABLE t1 (a int, b int) ENGINE=MyISAM; @@ -5167,4 +5167,54 @@ WHERE c = a AND ( 0 OR ( b BETWEEN 45 AND 300 OR a > 45 AND a < 100 ) AND b = c ); a b c DROP TABLE t1, t2; +# +# Bug mdev-4355: equalities from the result of simplification of OR +# are not propagated to lower AND levels +# +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,101),(2,102),(3,103),(4,104),(5,11); +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 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 Impossible WHERE +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where 0 +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); +a b +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`b` = 5) and (`test`.`t1`.`a` = 5)) +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +a b +DROP TABLE t1; End of 5.3 tests diff --git a/mysql-test/r/select_jcl6.result b/mysql-test/r/select_jcl6.result index d2faa9e85e1..6eb0d65a72f 100644 --- a/mysql-test/r/select_jcl6.result +++ b/mysql-test/r/select_jcl6.result @@ -5166,7 +5166,7 @@ a b c 8 8 8 DROP TABLE t1,t2; # -# Bug mdev-4413: another manifestations of bug mdev-2474 +# Bug mdev-4413: another manifestations of bug mdev-4274 # (valgrind complains) # CREATE TABLE t1 (a int, b int) ENGINE=MyISAM; @@ -5178,6 +5178,56 @@ WHERE c = a AND ( 0 OR ( b BETWEEN 45 AND 300 OR a > 45 AND a < 100 ) AND b = c ); a b c DROP TABLE t1, t2; +# +# Bug mdev-4355: equalities from the result of simplification of OR +# are not propagated to lower AND levels +# +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,101),(2,102),(3,103),(4,104),(5,11); +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 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 Impossible WHERE +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where 0 +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); +a b +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`b` = 5) and (`test`.`t1`.`a` = 5)) +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +a b +DROP TABLE t1; End of 5.3 tests set join_cache_level=default; show variables like 'join_cache_level'; diff --git a/mysql-test/r/select_pkeycache.result b/mysql-test/r/select_pkeycache.result index 953b1409827..dfc5236f794 100644 --- a/mysql-test/r/select_pkeycache.result +++ b/mysql-test/r/select_pkeycache.result @@ -5155,7 +5155,7 @@ a b c 8 8 8 DROP TABLE t1,t2; # -# Bug mdev-4413: another manifestations of bug mdev-2474 +# Bug mdev-4413: another manifestations of bug mdev-4274 # (valgrind complains) # CREATE TABLE t1 (a int, b int) ENGINE=MyISAM; @@ -5167,4 +5167,54 @@ WHERE c = a AND ( 0 OR ( b BETWEEN 45 AND 300 OR a > 45 AND a < 100 ) AND b = c ); a b c DROP TABLE t1, t2; +# +# Bug mdev-4355: equalities from the result of simplification of OR +# are not propagated to lower AND levels +# +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,101),(2,102),(3,103),(4,104),(5,11); +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and (`test`.`t1`.`b` <> 1)) +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +a b +5 11 +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 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 Impossible WHERE +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where 0 +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); +a b +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`b` = 5) and (`test`.`t1`.`a` = 5)) +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +a b +DROP TABLE t1; End of 5.3 tests diff --git a/mysql-test/r/subselect_sj2_mat.result b/mysql-test/r/subselect_sj2_mat.result index 18e3f6ddd34..bf5b95d2d8f 100644 --- a/mysql-test/r/subselect_sj2_mat.result +++ b/mysql-test/r/subselect_sj2_mat.result @@ -1380,7 +1380,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 2 100.00 Using where 2 MATERIALIZED t3 hash_ALL NULL #hash#$hj 5 test.t2.i2 3 100.00 Using where; Using join buffer (flat, BNLH join) Warnings: -Note 1003 select `test`.`t1`.`i1` AS `i1` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where ((`test`.`t3`.`i3` = `test`.`t2`.`i2`) and (`test`.`t1`.`i1` = `test`.`t2`.`i2`) and (`test`.`t2`.`i2` > 0)) +Note 1003 select `test`.`t1`.`i1` AS `i1` from `test`.`t1` semi join (`test`.`t2` join `test`.`t3`) where ((`test`.`t3`.`i3` = `test`.`t2`.`i2`) and (`test`.`t1`.`i1` = `test`.`t2`.`i2`) and (`test`.`t3`.`i3` > 0)) SELECT * FROM t1 WHERE i1 IN (SELECT i3 FROM t2, t3 WHERE i3 > 0 AND i3 = i2 OR 1=2); i1 diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 54168332664..94e169821e9 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -4326,7 +4326,7 @@ SELECT * FROM t1 INNER JOIN t2 ON ( c = a ) DROP TABLE t1,t2; --echo # ---echo # Bug mdev-4413: another manifestations of bug mdev-2474 +--echo # Bug mdev-4413: another manifestations of bug mdev-4274 --echo # (valgrind complains) --echo # @@ -4342,4 +4342,34 @@ SELECT * FROM t1, t2 DROP TABLE t1, t2; +--echo # +--echo # Bug mdev-4355: equalities from the result of simplification of OR +--echo # are not propagated to lower AND levels +--echo # + +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,101),(2,102),(3,103),(4,104),(5,11); + +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); +SELECT * FROM t1 WHERE (1 != 1 OR a = 5) AND (b != 1 OR a = 1); + +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (1 != 1 OR a = 5); + +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); +SELECT * FROM t1 WHERE (b != 1 OR a = 1) AND (a = 5 OR 1 != 1); + +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); +SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); + +EXPLAIN EXTENDED +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); +SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); + +DROP TABLE t1; + --echo End of 5.3 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index b2a91c8ec1e..5614533fc8a 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5608,10 +5608,12 @@ void Item_equal::merge(Item_equal *item) @brief Merge members of another Item_equal object into this one - @param item multiple equality whose members are to be merged + @param item multiple equality whose members are to be merged + @param save_merged keep the list of equalities in 'item' intact + (e.g. for other merges) @details - If the Item_equal 'item' happened to have some elements of the list + If the Item_equal 'item' happens to have some elements of the list of equal items belonging to 'this' object then the function merges the equal items from 'item' into this list. If both lists contains constants and they are different then @@ -5626,24 +5628,45 @@ void Item_equal::merge(Item_equal *item) The method 'merge' just joins the list of equal items belonging to 'item' to the list of equal items belonging to this object assuming that the lists are disjoint. It would be more correct to call the method 'join'. - The method 'merge_with_check' really merges two lists of equal items if they - have common members. + The method 'merge_into_with_check' really merges two lists of equal items if + they have common members. */ -bool Item_equal::merge_with_check(Item_equal *item) +bool Item_equal::merge_with_check(Item_equal *item, bool save_merged) { bool intersected= FALSE; - Item_equal_fields_iterator_slow fi(*this); + Item_equal_fields_iterator_slow fi(*item); + while (fi++) { - if (item->contains(fi.get_curr_field())) + if (contains(fi.get_curr_field())) { - fi.remove(); intersected= TRUE; + if (!save_merged) + fi.remove(); } } if (intersected) - item->merge(this); + { + if (!save_merged) + merge(item); + else + { + Item *c= item->get_const(); + if (c) + add_const(c); + if (!cond_false) + { + Item *item; + fi.rewind(); + while ((item= fi++)) + { + if (!contains(fi.get_curr_field())) + add(item); + } + } + } + } return intersected; } @@ -5652,17 +5675,25 @@ bool Item_equal::merge_with_check(Item_equal *item) @brief Merge this object into a list of Item_equal objects - @param list the list of Item_equal objects to merge into + @param list the list of Item_equal objects to merge into + @param save_merged keep the list of equalities in 'this' intact + (e.g. for other merges) + @param only_intersected do not merge if there are no common members + in any of Item_equal objects from the list + and this Item_equal @details If the list of equal items from 'this' object contains common members with the lists of equal items belonging to Item_equal objects from 'list' then all involved Item_equal objects e1,...,ek are merged into one - Item equal that replaces e1,...,ek in the 'list'. Otherwise this + Item equal that replaces e1,...,ek in the 'list'. Otherwise, in the case + when the value of the parameter only_if_intersected is false, this Item_equal is joined to the 'list'. */ -void Item_equal::merge_into_list(List *list) +void Item_equal::merge_into_list(List *list, + bool save_merged, + bool only_intersected) { Item_equal *item; List_iterator it(*list); @@ -5671,16 +5702,16 @@ void Item_equal::merge_into_list(List *list) { if (!merge_into) { - if (merge_with_check(item)) + if (item->merge_with_check(this, save_merged)) merge_into= item; } else { - if (item->merge_with_check(merge_into)) + if (merge_into->merge_with_check(item, false)) it.remove(); } } - if (!merge_into) + if (!only_intersected && !merge_into) list->push_back(this); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1b8687cfcfc..0ed16c86c0a 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1761,8 +1761,9 @@ public: /** Get number of field items / references to field items in this object */ uint n_field_items() { return equal_items.elements-test(with_const); } void merge(Item_equal *item); - bool merge_with_check(Item_equal *equal_item); - void merge_into_list(List *list); + bool merge_with_check(Item_equal *equal_item, bool save_merged); + void merge_into_list(List *list, bool save_merged, + bool only_intersected); void update_const(); enum Functype functype() const { return MULT_EQUAL_FUNC; } longlong val_int(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 40021baa954..bc1756ce171 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13134,14 +13134,175 @@ optimize_cond(JOIN *join, COND *conds, /** - Remove const and eq items. + @brief + Propagate multiple equalities to the sub-expressions of a condition + @param thd thread handle + @param cond the condition where equalities are to be propagated + @param *new_equalities the multiple equalities to be propagated + @param inherited path to all inherited multiple equality items + @param[out] is_simplifiable_cond 'cond' may be simplified after the + propagation of the equalities + + @details + The function recursively traverses the tree of the condition 'cond' and + for each its AND sub-level of any depth the function merges the multiple + equalities from the list 'new_equalities' into the multiple equalities + attached to the AND item created for this sub-level. + The function also [re]sets references to the equalities formed by the + merges of multiple equalities in all field items occurred in 'cond' + that are encountered in the equalities. + If the result of any merge of multiple equalities is an impossible + condition the function returns TRUE in the parameter is_simplifiable_cond. +*/ + +void propagate_new_equalities(THD *thd, Item *cond, + List *new_equalities, + COND_EQUAL *inherited, + bool *is_simplifiable_cond) +{ + if (cond->type() == Item::COND_ITEM) + { + bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; + if (and_level) + { + Item_cond_and *cond_and= (Item_cond_and *) cond; + List *cond_equalities= &cond_and->cond_equal.current_level; + inherited= cond_and->cond_equal.upper_levels; + if (!cond_equalities->is_empty() && cond_equalities != new_equalities) + { + Item_equal *equal_item; + List_iterator it(*new_equalities); + while ((equal_item= it++)) + { + equal_item->merge_into_list(cond_equalities, true, true); + } + List_iterator ei(*cond_equalities); + while ((equal_item= ei++)) + { + if (equal_item->const_item() && !equal_item->val_int()) + { + *is_simplifiable_cond= true; + return; + } + } + } + } + + Item *item; + List_iterator li(*((Item_cond*) cond)->argument_list()); + while ((item= li++)) + { + propagate_new_equalities(thd, item, new_equalities, inherited, + is_simplifiable_cond); + } + } + else if (cond->type() == Item::FUNC_ITEM && + ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC) + { + Item_equal *equal_item; + List_iterator it(*new_equalities); + Item_equal *equality= (Item_equal *) cond; + while ((equal_item= it++)) + { + equality->merge_with_check(equal_item, true); + } + if (equality->const_item() && !equality->val_int()) + *is_simplifiable_cond= true; + } + else + { + uchar* is_subst_valid= (uchar *) Item::ANY_SUBST; + cond= cond->compile(&Item::subst_argument_checker, + &is_subst_valid, + &Item::equal_fields_propagator, + (uchar *) inherited); + cond->update_used_tables(); + } +} + + + +/** + @brief + Evaluate all constant boolean sub-expressions in a condition + + @param thd thread handle + @param cond condition where where to evaluate constant sub-expressions + @param[out] cond_value : the returned value of the condition + (TRUE/FALSE/UNKNOWN: + Item::COND_TRUE/Item::COND_FALSE/Item::COND_OK) @return - Return new item, or NULL if no condition @n - cond_value is set to according: - - COND_OK : query is possible (field = constant) - - COND_TRUE : always true ( 1 = 1 ) - - COND_FALSE : always false ( 1 = 2 ) + the item that is the result of the substitution of all inexpensive constant + boolean sub-expressions into cond, or, + NULL if the condition is constant and is evaluated to FALSE. + + @details + This function looks for all inexpensive constant boolean sub-expressions in + the given condition 'cond' and substitutes them for their values. + For example, the condition 2 > (5 + 1) or a < (10 / 2) + will be transformed to the condition a < (10 / 2). + Note that a constant sub-expression is evaluated only if it is constant and + inexpensive. A sub-expression with an uncorrelated subquery may be evaluated + only if the subquery is considered as inexpensive. + The function does not evaluate a constant sub-expression if it is not on one + of AND/OR levels of the condition 'cond'. For example, the subquery in the + condition a > (select max(b) from t1 where b > 5) will never be evaluated + by this function. + If a constant boolean sub-expression is evaluated to TRUE then: + - when the sub-expression is a conjunct of an AND formula it is simply + removed from this formula + - when the sub-expression is a disjunct of an OR formula the whole OR + formula is converted to TRUE + If a constant boolean sub-expression is evaluated to FALSE then: + - when the sub-expression is a disjunct of an OR formula it is simply + removed from this formula + - when the sub-expression is a conjuct of an AND formula the whole AND + formula is converted to FALSE + When a disjunct/conjunct is removed from an OR/AND formula it might happen + that there is only one conjunct/disjunct remaining. In this case this + remaining disjunct/conjunct must be merged into underlying AND/OR formula, + because AND/OR levels must alternate in the same way as they alternate + after fix_fields() is called for the original condition. + The specifics of merging a formula f into an AND formula A appears + when A contains multiple equalities and f contains multiple equalities. + In this case the multiple equalities from f and A have to be merged. + After this the resulting multiple equalities have to be propagated into + the all AND/OR levels of the formula A (see propagate_new_equalities()). + The propagation of multiple equalities might result in forming multiple + equalities that are always FALSE. This, in its turn, might trigger further + simplification of the condition. + + @note + EXAMPLE 1: + SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5 OR 1 != 1); + First 1 != 1 will be removed from the second conjunct: + => SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5 AND a = 5); + Then (b = 5 AND a = 5) will be merged into the top level condition: + => SELECT * FROM t1 WHERE (b = 1 OR a = 1) AND (b = 5) AND (a = 5); + Then (b = 5), (a = 5) will be propagated into the disjuncs of + (b = 1 OR a = 1): + => SELECT * FROM t1 WHERE ((b = 1) AND (b = 5) AND (a = 5) OR + (a = 1) AND (b = 5) AND (a = 5)) AND + (b = 5) AND (a = 5) + => SELECT * FROM t1 WHERE ((FALSE AND (a = 5)) OR + (FALSE AND (b = 5))) AND + (b = 5) AND (a = 5) + After this an additional call of remove_eq_conds() converts it + to FALSE + + EXAMPLE 2: + SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); + => SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5); + => SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5) AND (a = 5); + => SELECT * FROM t1 WHERE ((b = 1) AND (b = 5) AND (a = 5) OR + (a = 5) AND (b = 5) AND (a = 5)) AND + (b = 5) AND (a = 5) + => SELECT * FROM t1 WHERE ((FALSE AND (a = 5)) OR + ((b = 5) AND (a = 5))) AND + (b = 5) AND (a = 5) + After this an additional call of remove_eq_conds() converts it to + => SELECT * FROM t1 WHERE (b = 5) AND (a = 5) */ COND * @@ -13149,9 +13310,11 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) { if (cond->type() == Item::COND_ITEM) { + List new_equalities; bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; - List_iterator li(*((Item_cond*) cond)->argument_list()); + List *cond_arg_list= ((Item_cond*) cond)->argument_list(); + List_iterator li(*cond_arg_list); Item::cond_result tmp_cond_value; bool should_fix_fields=0; @@ -13161,92 +13324,72 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) { Item *new_item=remove_eq_conds(thd, item, &tmp_cond_value); if (!new_item) + { + /* This can happen only when item is converted to TRUE or FALSE */ li.remove(); + } else if (item != new_item) { - if (and_level) - { - /* - Take a special care of multiple equality predicates - that may be part of 'cond' and 'new_item'. - Those multiple equalities that have common members - must be merged. - */ - Item_cond_and *cond_and= (Item_cond_and *) cond; - List *cond_equal_items= - &cond_and->cond_equal.current_level; - List *cond_and_list= cond_and->argument_list(); - - if (new_item->type() == Item::COND_ITEM && - ((Item_cond*) new_item)->functype() == Item_func::COND_AND_FUNC) - { - Item_cond_and *new_item_and= (Item_cond_and *) new_item; - List *new_item_equal_items= - &new_item_and->cond_equal.current_level; - List *new_item_and_list= new_item_and->argument_list(); - cond_and_list->disjoin((List*) cond_equal_items); - new_item_and_list->disjoin((List*) new_item_equal_items); - Item_equal *equal_item; - List_iterator it(*new_item_equal_items); - while ((equal_item= it++)) - { - equal_item->merge_into_list(cond_equal_items); - } - if (new_item_and_list->is_empty()) - li.remove(); - else - { - Item *list_item; - Item *new_list_item; - uint cnt= new_item_and_list->elements; - List_iterator it(*new_item_and_list); - while ((list_item= it++)) - { - uchar* is_subst_valid= (uchar *) Item::ANY_SUBST; - new_list_item= - list_item->compile(&Item::subst_argument_checker, - &is_subst_valid, - &Item::equal_fields_propagator, - (uchar *) &cond_and->cond_equal); - if (new_list_item != list_item) - it.replace(new_list_item); - new_list_item->update_used_tables(); - } - li.replace(*new_item_and_list); - for (cnt--; cnt; cnt--) - item= li++; - } - cond_and_list->concat((List*) cond_equal_items); - } - else if (new_item->type() == Item::FUNC_ITEM && - ((Item_cond*) new_item)->functype() == - Item_func::MULT_EQUAL_FUNC) + /* + This can happen when: + - item was an OR formula converted to one disjunct + - item was an AND formula converted to one conjunct + In these cases the disjunct/conjunct must be merged into the + argument list of cond. + */ + if (new_item->type() == Item::COND_ITEM) + { + DBUG_ASSERT(((Item_cond *) cond)->functype() == + ((Item_cond *) new_item)->functype()); + List *new_item_arg_list= + ((Item_cond *) new_item)->argument_list(); + if (and_level) { - cond_and_list->disjoin((List*) cond_equal_items); - ((Item_equal *) new_item)->merge_into_list(cond_equal_items); - li.remove(); - cond_and_list->concat((List*) cond_equal_items); + /* + If new_item is an AND formula then multiple equalities + of new_item_arg_list must merged into multiple equalities + of cond_arg_list. + */ + List *new_item_equalities= + &((Item_cond_and *) new_item)->cond_equal.current_level; + if (!new_item_equalities->is_empty()) + { + /* + Cut the multiple equalities from the new_item_arg_list and + append them on the list new_equalities. Later the equalities + from this list will be merged into the multiple equalities + of cond_arg_list all together. + */ + new_item_arg_list->disjoin((List *) new_item_equalities); + new_equalities.concat(new_item_equalities); + } } - else - li.replace(new_item); + if (new_item_arg_list->is_empty()) + li.remove(); + else + { + uint cnt= new_item_arg_list->elements; + li.replace(*new_item_arg_list); + /* Make iterator li ignore new items */ + for (cnt--; cnt; cnt--) + li++; + should_fix_fields= 1; + } + } + else if (and_level && + new_item->type() == Item::FUNC_ITEM && + ((Item_cond*) new_item)->functype() == + Item_func::MULT_EQUAL_FUNC) + { + li.remove(); + new_equalities.push_back((Item_equal *) new_item); } else - { - if (new_item->type() == Item::COND_ITEM && - ((Item_cond*) new_item)->functype() == - ((Item_cond*) cond)->functype()) - { - List *arg_list= ((Item_cond*) new_item)->argument_list(); - uint cnt= arg_list->elements; - li.replace(*arg_list); - for ( cnt--; cnt; cnt--) - item= li++; - } - else - li.replace(new_item); + { + li.replace(new_item); + should_fix_fields= 1; } - should_fix_fields=1; - } + } if (*cond_value == Item::COND_UNDEF) *cond_value=tmp_cond_value; switch (tmp_cond_value) { @@ -13272,6 +13415,53 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) break; /* purecov: deadcode */ } } + if (!new_equalities.is_empty()) + { + DBUG_ASSERT(and_level); + /* + Merge multiple equalities that were cut from the results of + simplification of OR formulas converted into AND formulas. + These multiple equalities are to be merged into the + multiple equalities of cond_arg_list. + */ + COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->cond_equal; + List *cond_equalities= &cond_equal->current_level; + cond_arg_list->disjoin((List *) cond_equalities); + Item_equal *equality; + List_iterator_fast it(new_equalities); + while ((equality= it++)) + { + equality->merge_into_list(cond_equalities, false, false); + List_iterator_fast ei(*cond_equalities); + while ((equality= ei++)) + { + if (equality->const_item() && !equality->val_int()) + { + *cond_value= Item::COND_FALSE; + return (COND*) 0; + } + } + } + cond_arg_list->concat((List *) cond_equalities); + /* + Propagate the newly formed multiple equalities to + the all AND/OR levels of cond + */ + bool is_simplifiable_cond= true; + propagate_new_equalities(thd, cond, cond_equalities, + cond_equal->upper_levels, + &is_simplifiable_cond); + /* + If the above propagation of multiple equalities brings us + to multiple equalities that are always FALSE then try to + simplify the condition with remove_eq_cond() again. + */ + if (is_simplifiable_cond) + { + if (!(cond= remove_eq_conds(thd, cond, cond_value))) + return cond; + } + } if (should_fix_fields) cond->update_used_tables(); @@ -13383,6 +13573,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) return cond; // Point at next and level } + /** Check if equality can be used in removing components of GROUP BY/DISTINCT From 25c152018dceae35bf0d45de46a33a214048128c Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 16 Aug 2013 22:01:47 -0700 Subject: [PATCH 5/5] Fixed bug mdev-4418. After single row substitutions there might appear new equalities. They should be properly propagated to all AND/OR levels the WHERE condition. It's done now with an additional call of remove_eq_conds(). --- mysql-test/r/join_outer.result | 2 +- mysql-test/r/join_outer_jcl6.result | 2 +- mysql-test/r/select.result | 18 ++++++++++++++++++ mysql-test/r/select_jcl6.result | 18 ++++++++++++++++++ mysql-test/r/select_pkeycache.result | 18 ++++++++++++++++++ mysql-test/r/subselect.result | 2 +- mysql-test/r/subselect_no_mat.result | 2 +- mysql-test/r/subselect_no_opts.result | 2 +- mysql-test/r/subselect_no_scache.result | 2 +- mysql-test/r/subselect_no_semijoin.result | 2 +- mysql-test/r/table_elim.result | 4 ++-- mysql-test/r/view.result | 2 +- mysql-test/t/select.test | 17 +++++++++++++++++ sql/sql_select.cc | 17 +++++++++++++++++ 14 files changed, 98 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 723b2a88382..684bfb3836e 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -1649,7 +1649,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 1 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` left join `test`.`t2` on((6,(select `test`.`t3`.`a` from `test`.`t3` where (6 = `test`.`t3`.`a`)))) where 1 +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` left join `test`.`t2` on((6,(select `test`.`t3`.`a` from `test`.`t3` where (6 = `test`.`t3`.`a`)))) DROP TABLE t1,t2,t3; # # LP bug #817384 Wrong result with outer join + subquery in ON diff --git a/mysql-test/r/join_outer_jcl6.result b/mysql-test/r/join_outer_jcl6.result index d063720836d..4f4c16199e9 100644 --- a/mysql-test/r/join_outer_jcl6.result +++ b/mysql-test/r/join_outer_jcl6.result @@ -1660,7 +1660,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t2 ALL NULL NULL NULL NULL 1 100.00 Using where 2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00 Using where Warnings: -Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` left join `test`.`t2` on((6,(select `test`.`t3`.`a` from `test`.`t3` where (6 = `test`.`t3`.`a`)))) where 1 +Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t1` left join `test`.`t2` on((6,(select `test`.`t3`.`a` from `test`.`t3` where (6 = `test`.`t3`.`a`)))) DROP TABLE t1,t2,t3; # # LP bug #817384 Wrong result with outer join + subquery in ON diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index dfc5236f794..56a4a8d2bdc 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -5217,4 +5217,22 @@ Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); a b DROP TABLE t1; +# +# Bug mdev-4418: impossible multiple equality in OR formula +# after row substitution +# +CREATE TABLE t1 (a int, b varchar(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,'j'), (8,'v'); +CREATE TABLE t2 (c varchar(1), d varchar(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('k','k'); +EXPLAIN EXTENDED +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,'k' AS `c`,'k' AS `d` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`b` = 'k') and (`test`.`t1`.`a` = 136)) +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +a b c d +DROP TABLE t1,t2; End of 5.3 tests diff --git a/mysql-test/r/select_jcl6.result b/mysql-test/r/select_jcl6.result index 6eb0d65a72f..1a735299e0e 100644 --- a/mysql-test/r/select_jcl6.result +++ b/mysql-test/r/select_jcl6.result @@ -5228,6 +5228,24 @@ Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); a b DROP TABLE t1; +# +# Bug mdev-4418: impossible multiple equality in OR formula +# after row substitution +# +CREATE TABLE t1 (a int, b varchar(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,'j'), (8,'v'); +CREATE TABLE t2 (c varchar(1), d varchar(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('k','k'); +EXPLAIN EXTENDED +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,'k' AS `c`,'k' AS `d` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`b` = 'k') and (`test`.`t1`.`a` = 136)) +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +a b c d +DROP TABLE t1,t2; End of 5.3 tests set join_cache_level=default; show variables like 'join_cache_level'; diff --git a/mysql-test/r/select_pkeycache.result b/mysql-test/r/select_pkeycache.result index dfc5236f794..56a4a8d2bdc 100644 --- a/mysql-test/r/select_pkeycache.result +++ b/mysql-test/r/select_pkeycache.result @@ -5217,4 +5217,22 @@ Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); a b DROP TABLE t1; +# +# Bug mdev-4418: impossible multiple equality in OR formula +# after row substitution +# +CREATE TABLE t1 (a int, b varchar(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,'j'), (8,'v'); +CREATE TABLE t2 (c varchar(1), d varchar(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('k','k'); +EXPLAIN EXTENDED +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00 +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,'k' AS `c`,'k' AS `d` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`b` = 'k') and (`test`.`t1`.`a` = 136)) +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +a b c d +DROP TABLE t1,t2; End of 5.3 tests diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 8503164dde2..e4448d96657 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1322,7 +1322,7 @@ 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 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select (0,(select 1 from `test`.`t1` `a` where (0 = 1))) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select (0,(select 1 from `test`.`t1` `a` where 0)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index d0c9555d976..df6b884fbe0 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -1329,7 +1329,7 @@ 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 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select (0,(select 1 from `test`.`t1` `a` where (0 = 1))) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select (0,(select 1 from `test`.`t1` `a` where 0)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 47f6d3a0ed4..f9df463f6aa 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -1325,7 +1325,7 @@ 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 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select (0,(select 1 from `test`.`t1` `a` where (0 = 1))) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select (0,(select 1 from `test`.`t1` `a` where 0)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index f3a01618eff..70de4b26743 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -1328,7 +1328,7 @@ 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 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select (0,(select 1 from `test`.`t1` `a` where (0 = 1))) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select (0,(select 1 from `test`.`t1` `a` where 0)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 964704e0695..91071c98777 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -1325,7 +1325,7 @@ 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 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables Warnings: -Note 1003 select (0,(select 1 from `test`.`t1` `a` where (0 = 1))) AS `0 IN (SELECT 1 FROM t1 a)` +Note 1003 select (0,(select 1 from `test`.`t1` `a` where 0)) AS `0 IN (SELECT 1 FROM t1 a)` INSERT INTO t1 (pseudo) VALUES ('test1'); SELECT 0 IN (SELECT 1 FROM t1 a); 0 IN (SELECT 1 FROM t1 a) diff --git a/mysql-test/r/table_elim.result b/mysql-test/r/table_elim.result index 35acd30d76d..86e5bbe0060 100644 --- a/mysql-test/r/table_elim.result +++ b/mysql-test/r/table_elim.result @@ -17,7 +17,7 @@ explain extended select t1.a from t1 left join t2 on t2.a=t1.a; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Warnings: -Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where 1 +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` select t1.a from t1 left join t2 on t2.a=t1.a; a 0 @@ -62,7 +62,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t0 ALL NULL NULL NULL NULL 4 100.00 1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where Warnings: -Note 1003 select `test`.`t0`.`a` AS `a` from `test`.`t0` left join (`test`.`t1`) on((`test`.`t1`.`a` = `test`.`t0`.`a`)) where 1 +Note 1003 select `test`.`t0`.`a` AS `a` from `test`.`t0` left join (`test`.`t1`) on((`test`.`t1`.`a` = `test`.`t0`.`a`)) # Elimination with aggregate functions explain select count(*) from t1 left join t2 on t2.a=t1.a; id select_type table type possible_keys key key_len ref rows Extra diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 0f666f0e708..2d89c66a226 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -4300,7 +4300,7 @@ WHERE f1<>0 OR f2<>0 AND f4='v' AND (f2<>0 OR f3<>0 AND f5<>0 OR f4 LIKE '%b%'); 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 Warnings: -Note 1003 select 'r' AS `f4` from `test`.`t1` where ((20 <> 0) or 0) +Note 1003 select 'r' AS `f4` from `test`.`t1` where (20 <> 0) DROP VIEW v1; DROP TABLE t1; # diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 94e169821e9..c0e22cb8f4e 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -4372,4 +4372,21 @@ SELECT * FROM t1 WHERE (b = 1 OR a = 5) AND (b = 5 AND a = 5 OR 1 != 1); DROP TABLE t1; +--echo # +--echo # Bug mdev-4418: impossible multiple equality in OR formula +--echo # after row substitution +--echo # + +CREATE TABLE t1 (a int, b varchar(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,'j'), (8,'v'); + +CREATE TABLE t2 (c varchar(1), d varchar(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('k','k'); + +EXPLAIN EXTENDED +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); +SELECT * FROM t1, t2 WHERE c=b AND (1=2 OR ((b='h' OR a=136) AND d=b)); + +DROP TABLE t1,t2; + --echo End of 5.3 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 008e2e24c88..f94dbf858a3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1210,6 +1210,23 @@ JOIN::optimize() /* Handle the case where we have an OUTER JOIN without a WHERE */ conds=new Item_int((longlong) 1,1); // Always true } + + if (const_tables && conds) + { + conds= remove_eq_conds(thd, conds, &cond_value); + if (cond_value == Item::COND_FALSE) + { + zero_result_cause= + "Impossible WHERE noticed after reading const tables"; + select_lex->mark_const_derived(zero_result_cause); + if (select_options & SELECT_DESCRIBE) + { + conds=new Item_int((longlong) 0,1); + } + goto setup_subq_exit; + } + } + select= make_select(*table, const_table_map, const_table_map, conds, 1, &error); if (error)