From c85db2c4943b644c34cc4c67a95cfb5e5f0a09a4 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Nov 2013 16:40:46 +0200 Subject: [PATCH 1/2] MDEV-5103: server crashed on singular Item_equal Singular Item_equal support added. The problem was that during constant table substitution Item_equal become containing only one constant which was not supported internally. --- mysql-test/r/func_equal.result | 44 ++++++++++++++++++++++++++++ mysql-test/t/func_equal.test | 52 +++++++++++++++++++++++++++++++++- sql/item_cmpfunc.cc | 22 +++++++++++++- sql/item_cmpfunc.h | 1 + 4 files changed, 117 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_equal.result b/mysql-test/r/func_equal.result index 4750af6e8d8..02593529a91 100644 --- a/mysql-test/r/func_equal.result +++ b/mysql-test/r/func_equal.result @@ -42,3 +42,47 @@ select * from t1 where a in ('4828532208463511553'); a 4828532208463511553 drop table t1; +#End of 4.1 tests +# +# MDEV-5103: server crashed on singular Item_equal +# +CREATE TABLE `t1` ( +`tipo` enum('p','r') NOT NULL DEFAULT 'r', +`arquivo_id` bigint(20) unsigned NOT NULL DEFAULT '0', +`arquivo_md5` char(32) NOT NULL, +`conteudo` longblob NOT NULL, +`usuario` varchar(15) NOT NULL, +`datahora_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', +`tipo_arquivo` varchar(255) NOT NULL, +`nome_arquivo` varchar(255) NOT NULL, +`tamanho_arquivo` bigint(20) unsigned NOT NULL DEFAULT '0', +PRIMARY KEY (`tipo`,`arquivo_id`), +UNIQUE KEY `tipo` (`tipo`,`arquivo_md5`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1; +INSERT INTO `t1` (`tipo`, `arquivo_id`, `arquivo_md5`, `conteudo`, `usuario`, `datahora_gmt`, `tipo_arquivo`, `nome_arquivo`, `tamanho_arquivo`) VALUES +('r', 1, 'ad18832202b199728921807033a8a515', '', 'rspadim', '2013-10-05 13:55:50', '001_cbr643', 'CBR6431677410201314132.ret', 21306); +CREATE TABLE `t2` ( +`tipo` enum('p','r') NOT NULL DEFAULT 'p', +`arquivo_id` bigint(20) NOT NULL DEFAULT '0', +`usuario` varchar(25) NOT NULL, +`datahora` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', +`erros` longblob NOT NULL, +`importados` bigint(20) unsigned NOT NULL DEFAULT '0', +`n_importados` bigint(20) unsigned NOT NULL DEFAULT '0', +PRIMARY KEY (`tipo`,`arquivo_id`,`datahora`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1; +INSERT INTO `t2` (`tipo`, `arquivo_id`, `usuario`, `datahora`, `erros`, `importados`, `n_importados`) VALUES +('r', 1, 'rspadim', '2013-10-05 14:25:30', '', 32, 0); +SELECT +arquivo_id,usuario,datahora_gmt,tipo_arquivo,nome_arquivo,tamanho_arquivo +FROM t1 AS a +WHERE datahora_gmt>='0000-00-00 00:00:00' AND +datahora_gmt<='2013-10-07 02:59:59' AND tipo='r' AND +(tipo_arquivo,arquivo_id) NOT IN +(SELECT tipo_arquivo,arquivo_id +FROM t2 +WHERE (tipo_arquivo,arquivo_id)=(a.tipo_arquivo,a.arquivo_id)) +ORDER BY arquivo_id DESC; +arquivo_id usuario datahora_gmt tipo_arquivo nome_arquivo tamanho_arquivo +drop table t2, t1; +#End of 5.3 tests diff --git a/mysql-test/t/func_equal.test b/mysql-test/t/func_equal.test index 1c219af0254..990b6d8e74e 100644 --- a/mysql-test/t/func_equal.test +++ b/mysql-test/t/func_equal.test @@ -43,4 +43,54 @@ select * from t1 where a = '4828532208463511553'; select * from t1 where a in ('4828532208463511553'); drop table t1; -# End of 4.1 tests +--echo #End of 4.1 tests + +--echo # +--echo # MDEV-5103: server crashed on singular Item_equal +--echo # + +CREATE TABLE `t1` ( + `tipo` enum('p','r') NOT NULL DEFAULT 'r', + `arquivo_id` bigint(20) unsigned NOT NULL DEFAULT '0', + `arquivo_md5` char(32) NOT NULL, + `conteudo` longblob NOT NULL, + `usuario` varchar(15) NOT NULL, + `datahora_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `tipo_arquivo` varchar(255) NOT NULL, + `nome_arquivo` varchar(255) NOT NULL, + `tamanho_arquivo` bigint(20) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`tipo`,`arquivo_id`), + UNIQUE KEY `tipo` (`tipo`,`arquivo_md5`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1; + +INSERT INTO `t1` (`tipo`, `arquivo_id`, `arquivo_md5`, `conteudo`, `usuario`, `datahora_gmt`, `tipo_arquivo`, `nome_arquivo`, `tamanho_arquivo`) VALUES + ('r', 1, 'ad18832202b199728921807033a8a515', '', 'rspadim', '2013-10-05 13:55:50', '001_cbr643', 'CBR6431677410201314132.ret', 21306); + + +CREATE TABLE `t2` ( + `tipo` enum('p','r') NOT NULL DEFAULT 'p', + `arquivo_id` bigint(20) NOT NULL DEFAULT '0', + `usuario` varchar(25) NOT NULL, + `datahora` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', + `erros` longblob NOT NULL, + `importados` bigint(20) unsigned NOT NULL DEFAULT '0', + `n_importados` bigint(20) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`tipo`,`arquivo_id`,`datahora`) +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1; + +INSERT INTO `t2` (`tipo`, `arquivo_id`, `usuario`, `datahora`, `erros`, `importados`, `n_importados`) VALUES + ('r', 1, 'rspadim', '2013-10-05 14:25:30', '', 32, 0); + +SELECT +arquivo_id,usuario,datahora_gmt,tipo_arquivo,nome_arquivo,tamanho_arquivo + FROM t1 AS a + WHERE datahora_gmt>='0000-00-00 00:00:00' AND + datahora_gmt<='2013-10-07 02:59:59' AND tipo='r' AND + (tipo_arquivo,arquivo_id) NOT IN + (SELECT tipo_arquivo,arquivo_id + FROM t2 + WHERE (tipo_arquivo,arquivo_id)=(a.tipo_arquivo,a.arquivo_id)) + ORDER BY arquivo_id DESC; + +drop table t2, t1; +--echo #End of 5.3 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d1075c3c47f..22554c71d90 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5564,6 +5564,12 @@ void Item_equal::add_const(Item *c, Item *f) func->quick_fix_field(); cond_false= !func->val_int(); } + /* + TODO: also support the case where Item_equal becomes singular with + this->is_cond_true()=1. When I attempted to mark the item as constant, + the optimizer attempted to remove it, however it is still referenced from + COND_EQUAL and I got a crash. + */ if (cond_false) const_item_cache= 1; } @@ -5768,7 +5774,8 @@ void Item_equal::merge_into_list(List *list, void Item_equal::sort(Item_field_cmpfunc compare, void *arg) { - bubble_sort(&equal_items, compare, arg); + if (equal_items.elements > 1) + bubble_sort(&equal_items, compare, arg); } @@ -5868,6 +5875,12 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) void Item_equal::update_used_tables() { not_null_tables_cache= used_tables_cache= 0; + /* + TODO: also support the case where Item_equal becomes singular with + this->is_cond_true()=1. When I attempted to mark the item as constant, + the optimizer attempted to remove it, however it is still referenced from + COND_EQUAL and I got a crash. + */ if ((const_item_cache= cond_false)) return; Item_equal_fields_iterator it(*this); @@ -5916,6 +5929,8 @@ longlong Item_equal::val_int() { if (cond_false) return 0; + if (is_cond_true()) + return 1; Item *item= get_const(); Item_equal_fields_iterator it(*this); if (!item) @@ -5940,6 +5955,11 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= get_first(NO_PARTICULAR_TAB, NULL); + if (!item) + { + DBUG_ASSERT(is_cond_true()); // it should be the only constant + item= equal_items.head(); + } eval_item= cmp_item::get_comparator(item->cmp_type(), item, item->collation.collation); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c1ef680f3ee..cdb4963e857 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1725,6 +1725,7 @@ public: Item_equal(Item_equal *item_equal); /* Currently the const item is always the first in the list of equal items */ inline Item* get_const() { return with_const ? equal_items.head() : NULL; } + inline bool is_cond_true() { return equal_items.elements == 1; } void add_const(Item *c, Item *f = NULL); /** Add a non-constant item to the multiple equality */ void add(Item *f) { equal_items.push_back(f); } From c98a054fdeab9c2d3a637cf4fce57a2f9756dcc8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Nov 2013 17:28:14 +0200 Subject: [PATCH 2/2] MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution of PS with LEFT JOIN and MERGE view or SELECT SQ 1. Transformation of row IN subquery made the same as single value. 2. replace_where_subcondition() made working on several layers of OR/AND because it called on expression before fix_fields(). --- mysql-test/r/view.result | 31 +++++++++++++++++++++++++++++++ mysql-test/t/view.test | 28 ++++++++++++++++++++++++++++ sql/item_cmpfunc.cc | 11 ++++++++--- sql/item_subselect.cc | 38 +++++++++++++++++++------------------- sql/opt_subselect.cc | 19 +++++++++++++------ 5 files changed, 99 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e0323c305f5..c98d8dfd8a4 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -4709,6 +4709,37 @@ q 1 q q 1 q drop view v1; drop table t1,t2; +# +# MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution +# of PS with LEFT JOIN and MERGE view or SELECT SQ +# +CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,'foo'),(2,'bar'); +CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('foobar'),('qux'); +CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ; +PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)'; +EXECUTE stmt; +i1 c1 i1 c1 +1 foo NULL NULL +2 bar NULL NULL +EXECUTE stmt; +i1 c1 i1 c1 +1 foo NULL NULL +2 bar NULL NULL +drop view v1; +CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ; +EXECUTE stmt; +i1 c1 i1 c1 +1 foo NULL NULL +2 bar NULL NULL +EXECUTE stmt; +i1 c1 i1 c1 +1 foo NULL NULL +2 bar NULL NULL +deallocate prepare stmt; +drop view v1; +drop table t1,t2; # ----------------------------------------------------------------- # -- End of 5.3 tests. # ----------------------------------------------------------------- diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 0547004cd9c..2027f22e7c5 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -4647,6 +4647,34 @@ SELECT * FROM t2 LEFT JOIN v1 ON ( c=b AND a IN ( 1,6 ) ); drop view v1; drop table t1,t2; +--echo # +--echo # MDEV-5153: Server crashes in Item_ref::fix_fields on 2nd execution +--echo # of PS with LEFT JOIN and MERGE view or SELECT SQ +--echo # + +CREATE TABLE t1 (i1 INT, c1 VARCHAR(6)) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1,'foo'),(2,'bar'); + +CREATE TABLE t2 (c2 VARCHAR(6)) ENGINE=MyISAM; +INSERT INTO t2 VALUES ('foobar'),('qux'); + +CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1 ) IN ( SELECT c2 FROM t2 ) AND i1 <= 2 ; + +PREPARE stmt FROM 'SELECT * FROM t1 LEFT JOIN v1 ON (v1.i1 = t1.i1)'; + +EXECUTE stmt; +EXECUTE stmt; + +drop view v1; +CREATE ALGORITHM=MERGE VIEW v1 AS SELECT * FROM t1 WHERE ( c1, c1 ) IN ( SELECT c2, c2 FROM t2 ) AND i1 <= 2 ; + +EXECUTE stmt; +EXECUTE stmt; + +deallocate prepare stmt; +drop view v1; +drop table t1,t2; + --echo # ----------------------------------------------------------------- --echo # -- End of 5.3 tests. --echo # ----------------------------------------------------------------- diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 22554c71d90..49e56a46e84 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1436,9 +1436,11 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) bool Item_in_optimizer::fix_left(THD *thd, Item **ref) { + DBUG_ENTER("Item_in_optimizer::fix_left"); if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || (!cache && !(cache= Item_cache::get_cache(args[0])))) - return 1; + DBUG_RETURN(1); + DBUG_PRINT("info", ("actual fix fields")); cache->setup(args[0]); if (cache->cols() == 1) @@ -1460,10 +1462,13 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SUBQUERY in ROW in left expression of IN/ALL/ANY"); - return 1; + DBUG_RETURN(1); } if (args[0]->element_index(i)->used_tables()) + { ((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT); + cache->set_used_tables(OUTER_REF_TABLE_BIT); + } else ((Item_cache *)cache->element_index(i))->set_used_tables(0); } @@ -1477,7 +1482,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) cache->store(args[0]); cache->cache_value(); } - return 0; + DBUG_RETURN(0); } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 0a4a06f8676..edb3f71583c 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2177,11 +2177,11 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, DBUG_RETURN(true); Item *item_eq= new Item_func_eq(new - Item_ref(&select_lex->context, - (*optimizer->get_cache())-> - addr(i), - (char *)"", - (char *)in_left_expr_name), + Item_direct_ref(&select_lex->context, + (*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)in_left_expr_name), new Item_ref(&select_lex->context, select_lex->ref_pointer_array + i, @@ -2462,7 +2462,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg) bool Item_in_subselect::select_in_like_transformer(JOIN *join) { - Query_arena *arena, backup; + Query_arena *arena= 0, backup; SELECT_LEX *current= thd->lex->current_select; const char *save_where= thd->where; bool trans_res= true; @@ -2484,9 +2484,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) } } - if (changed) - DBUG_RETURN(false); - thd->where= "IN/ALL/ANY subquery"; /* @@ -2497,25 +2494,29 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) note: we won't need Item_in_optimizer when handling degenerate cases like "... IN (SELECT 1)" */ + arena= thd->activate_stmt_arena_if_needed(&backup); if (!optimizer) { - arena= thd->activate_stmt_arena_if_needed(&backup); result= (!(optimizer= new Item_in_optimizer(left_expr, this))); - if (arena) - thd->restore_active_arena(arena, &backup); if (result) - goto err; + goto out; } thd->lex->current_select= current->return_after_parsing(); - result= (!left_expr->fixed && - left_expr->fix_fields(thd, optimizer->arguments())); + result= optimizer->fix_left(thd, optimizer->arguments()); /* fix_fields can change reference to left_expr, we need reassign it */ left_expr= optimizer->arguments()[0]; - thd->lex->current_select= current; + + if (changed) + { + trans_res= false; + goto out; + } + + if (result) - goto err; + goto out; /* Both transformers call fix_fields() only for Items created inside them, @@ -2524,7 +2525,6 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) of Item, we have to call fix_fields() for it only with original arena to avoid memory leack) */ - arena= thd->activate_stmt_arena_if_needed(&backup); if (left_expr->cols() == 1) trans_res= single_value_transformer(join); else @@ -2539,9 +2539,9 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) } trans_res= row_value_transformer(join); } +out: if (arena) thd->restore_active_arena(arena, &backup); -err: thd->where= save_where; DBUG_RETURN(trans_res); } diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index bedf770d3d9..c5f692f8128 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1252,11 +1252,11 @@ void get_delayed_table_estimates(TABLE *table, @brief Replaces an expression destructively inside the expression tree of the WHERE clase. - @note Because of current requirements for semijoin flattening, we do not - need to recurse here, hence this function will only examine the top-level - AND conditions. (see JOIN::prepare, comment starting with "Check if the - subquery predicate can be executed via materialization". - + @note We substitute AND/OR structure because it was copied by + copy_andor_structure and some changes could be done in the copy but + should be left permanent, also there could be several layers of AND over + AND and OR over OR because ::fix_field() possibly is not called. + @param join The top-level query. @param old_cond The expression to be replaced. @param new_cond The expression to be substituted. @@ -1284,13 +1284,20 @@ static bool replace_where_subcondition(JOIN *join, Item **expr, Item *item; while ((item= li++)) { - if (item == old_cond) + if (item == old_cond) { li.replace(new_cond); if (do_fix_fields) new_cond->fix_fields(join->thd, li.ref()); return FALSE; } + else if (item->type() == Item::COND_ITEM) + { + DBUG_ASSERT(!(*expr)->fixed); + replace_where_subcondition(join, li.ref(), + old_cond, new_cond, + do_fix_fields); + } } } /*