From 9097bd2f08abb21b302d56d3d954ad45c247bf2a Mon Sep 17 00:00:00 2001 From: "tsmith@.mysql.com" <> Date: Sat, 13 Aug 2005 05:08:29 +0200 Subject: [PATCH 1/2] sql_parse.cc: Don't skip SET command even if --replicate-wild* says to. E.g., SET ONE_SHOT TIME_ZONE = 'XYZ' should be executed (BUG #12542) --- sql/sql_parse.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cd87b097038..9129cf584f9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1975,11 +1975,16 @@ mysql_execute_command(THD *thd) /* Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated. - Exception is DROP TEMPORARY TABLE IF EXISTS: we always execute it - (otherwise we have stale files on slave caused by exclusion of one tmp - table). + + Exceptions are: + + - SET: we always execute it (e.g., SET ONE_SHOT TIME_ZONE = 'XYZ') + + - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we + have stale files on slave caused by exclusion of one tmp table). */ - if (!(lex->sql_command == SQLCOM_DROP_TABLE && + if (lex->sql_command != SQLCOM_SET_OPTION && + !(lex->sql_command == SQLCOM_DROP_TABLE && lex->drop_temporary && lex->drop_if_exists) && all_tables_not_ok(thd,tables)) { From 1e248ceaf1e65521e424e389c6a85ccb5f85e0d7 Mon Sep 17 00:00:00 2001 From: "bell@50.0.168.192.in-addr.arpa" <> Date: Sat, 13 Aug 2005 07:45:14 +0300 Subject: [PATCH 2/2] fixed convertion and handling IN subqueries with rows (BUG#11867) --- mysql-test/r/subselect.result | 60 ++++++++++- mysql-test/t/subselect.test | 13 ++- sql/item.h | 9 ++ sql/item_cmpfunc.h | 5 + sql/item_subselect.cc | 183 ++++++++++++++++++++++++++-------- sql/sql_select.cc | 3 +- 6 files changed, 226 insertions(+), 47 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index f0b4a8cc06b..8a2ae8e171a 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -2742,13 +2742,26 @@ one two flag 5 6 N 7 8 N insert into t2 values (null,null,'N'); +insert into t2 values (null,3,'0'); +insert into t2 values (null,5,'0'); +insert into t2 values (10,null,'0'); +insert into t1 values (10,3,'0'); +insert into t1 values (10,5,'0'); +insert into t1 values (10,10,'0'); SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N') as 'test' from t1; one two test -1 2 0 -2 3 0 -3 4 0 +1 2 NULL +2 3 NULL +3 4 NULL 5 6 1 7 8 1 +10 3 NULL +10 5 NULL +10 10 NULL +SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); +one two +5 6 +7 8 SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N' group by one,two) as 'test' from t1; one two test 1 2 NULL @@ -2756,6 +2769,47 @@ one two test 3 4 NULL 5 6 1 7 8 1 +10 3 NULL +10 5 NULL +10 10 NULL +SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0') as 'test' from t1; +one two test +1 2 0 +2 3 NULL +3 4 0 +5 6 0 +7 8 0 +10 3 NULL +10 5 NULL +10 10 NULL +SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; +one two test +1 2 0 +2 3 NULL +3 4 0 +5 6 0 +7 8 0 +10 3 NULL +10 5 NULL +10 10 NULL +explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0') as 'test' from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 8 +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where +Warnings: +Note 1003 select test.t1.one AS `one`,test.t1.two AS `two`,((test.t1.one,test.t1.two),(select test.t2.one AS `one`,test.t2.two AS `two` from test.t2 where ((test.t2.flag = _latin1'0') and (((test.t1.one) = test.t2.one) or isnull(test.t2.one)) and (((test.t1.two) = test.t2.two) or isnull(test.t2.two))) having ((test.t2.one) and (test.t2.two)))) AS `test` from test.t1 +explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where +Warnings: +Note 1003 select test.t1.one AS `one`,test.t1.two AS `two` from test.t1 where ((test.t1.one,test.t1.two),(select test.t2.one AS `one`,test.t2.two AS `two` from test.t2 where ((test.t2.flag = _latin1'N') and ((test.t1.one) = test.t2.one) and ((test.t1.two) = test.t2.two)))) +explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 8 +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select test.t1.one AS `one`,test.t1.two AS `two`,((test.t1.one,test.t1.two),(select test.t2.one AS `one`,test.t2.two AS `two` from test.t2 where (test.t2.flag = _latin1'0') group by test.t2.one,test.t2.two having ((((test.t1.one) = test.t2.one) or isnull(test.t2.one)) and (((test.t1.two) = test.t2.two) or isnull(test.t2.two)) and (test.t2.one) and (test.t2.two)))) AS `test` from test.t1 DROP TABLE t1,t2; CREATE TABLE t1 (a char(5), b char(5)); INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa'); diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 1b7e01627cd..4bb917ab58c 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1769,9 +1769,20 @@ SELECT * FROM t1 WHERE ROW(one,two) IN (SELECT DISTINCT one,two FROM t1 WHERE flag = 'N'); insert into t2 values (null,null,'N'); +insert into t2 values (null,3,'0'); +insert into t2 values (null,5,'0'); +insert into t2 values (10,null,'0'); +insert into t1 values (10,3,'0'); +insert into t1 values (10,5,'0'); +insert into t1 values (10,10,'0'); SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N') as 'test' from t1; +SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N' group by one,two) as 'test' from t1; - +SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0') as 'test' from t1; +SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; +explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0') as 'test' from t1; +explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N'); +explain extended SELECT one,two,ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = '0' group by one,two) as 'test' from t1; DROP TABLE t1,t2; # diff --git a/sql/item.h b/sql/item.h index 825b37fe64c..c71769b5129 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1023,6 +1023,15 @@ public: String* val_str(String* s); bool get_date(TIME *ltime, uint fuzzydate); void print(String *str); + /* + we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE + */ + table_map used_tables() const + { + return (depended_from ? + OUTER_REF_TABLE_BIT : + (*ref)->used_tables() | RAND_TABLE_BIT); + } }; class Item_null_helper :public Item_ref_null_helper diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 525f269e528..47884f6064e 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -832,6 +832,11 @@ public: longlong val_int(); const char *func_name() const { return ""; } void update_used_tables(); + /* + we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE + */ + table_map used_tables() const + { return used_tables_cache | RAND_TABLE_BIT; } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6ec8b11d863..c8405a9f8f4 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -859,6 +859,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, argument (reference) to fix_fields() */ select_lex->where= join->conds= and_items(join->conds, item); + select_lex->where->top_level_item(); if (join->conds->fix_fields(thd, join->tables_list, 0)) DBUG_RETURN(RES_ERROR); } @@ -912,10 +913,13 @@ Item_in_subselect::single_value_transformer(JOIN *join, Item_subselect::trans_res Item_in_subselect::row_value_transformer(JOIN *join) { - DBUG_ENTER("Item_in_subselect::row_value_transformer"); - - Item *item= 0; SELECT_LEX *select_lex= join->select_lex; + Item *having_item= 0; + uint cols_num= left_expr->cols(); + bool is_having_used= (join->having || select_lex->with_sum_func || + select_lex->group_list.first || + !select_lex->table_list.elements); + DBUG_ENTER("Item_in_subselect::row_value_transformer"); if (select_lex->item_list.elements != left_expr->cols()) { @@ -946,61 +950,156 @@ Item_in_subselect::row_value_transformer(JOIN *join) } select_lex->uncacheable|= UNCACHEABLE_DEPENDENT; + if (is_having_used) { - uint n= left_expr->cols(); - List_iterator_fast li(select_lex->item_list); - for (uint i= 0; i < n; i++) + /* + (l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) => + EXISTS (SELECT ... HAVING having and + (l1 = v1 or is null v1) and + (l2 = v2 or is null v2) and + (l3 = v3 or is null v3) and + is_not_null_test(v1) and + is_not_null_test(v2) and + is_not_null_test(v3)) + where is_not_null_test used to register nulls in case if we have + not found matching to return correct NULL value + */ + Item *item_having_part2= 0; + for (uint i= 0; i < cols_num; i++) { - Item *func; DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->el(i)->cols())) DBUG_RETURN(RES_ERROR); - if (join->having || select_lex->with_sum_func || - select_lex->group_list.elements) - func= new Item_ref_null_helper(this, - select_lex->ref_pointer_array+i, - (char *) "", - (char *) ""); - else - func= li++; - func= - eq_creator.create(new Item_direct_ref((*optimizer->get_cache())-> - addr(i), - (char *)"", - (char *)in_left_expr_name), - func); - item= and_items(item, func); + Item *item_eq= + new Item_func_eq(new + Item_direct_ref((*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)in_left_expr_name), + new + Item_direct_ref(select_lex->ref_pointer_array + i, + (char *)"", + (char *)"") + ); + Item *item_isnull= + new Item_func_isnull(new + Item_direct_ref( select_lex-> + ref_pointer_array+i, + (char *)"", + (char *)"") + ); + having_item= + and_items(having_item, + new Item_cond_or(item_eq, item_isnull)); + item_having_part2= + and_items(item_having_part2, + new + Item_is_not_null_test(this, + new + Item_direct_ref(select_lex-> + ref_pointer_array + i, + (char *)"", + (char *)"") + ) + ); + item_having_part2->top_level_item(); } - } - if (join->having || select_lex->with_sum_func || - select_lex->group_list.first || - !select_lex->table_list.elements) - { - /* - AND can't be changed during fix_fields() - we can assign select_lex->having here, and pass 0 as last - argument (reference) to fix_fields() - */ - select_lex->having= join->having= and_items(join->having, item); - select_lex->having_fix_field= 1; - if (join->having->fix_fields(thd, join->tables_list, 0)) - { - select_lex->having_fix_field= 0; - DBUG_RETURN(RES_ERROR); - } - select_lex->having_fix_field= 0; + having_item= and_items(having_item, item_having_part2); + having_item->top_level_item(); } else { + /* + (l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) => + EXISTS (SELECT ... WHERE where and + (l1 = v1 or is null v1) and + (l2 = v2 or is null v2) and + (l3 = v3 or is null v3) + HAVING is_not_null_test(v1) and + is_not_null_test(v2) and + is_not_null_test(v3)) + where is_not_null_test register NULLs values but reject rows + + in case when we do not need correct NULL, we have simplier construction: + EXISTS (SELECT ... WHERE where and + (l1 = v1) and + (l2 = v2) and + (l3 = v3) + */ + Item *where_item= 0; + for (uint i= 0; i < cols_num; i++) + { + Item *item, *item_isnull; + DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed); + if (select_lex->ref_pointer_array[i]-> + check_cols(left_expr->el(i)->cols())) + DBUG_RETURN(RES_ERROR); + item= + new Item_func_eq(new + Item_direct_ref((*optimizer->get_cache())-> + addr(i), + (char *)"", + (char *)in_left_expr_name), + new + Item_direct_ref( select_lex-> + ref_pointer_array+i, + (char *)"", + (char *)"") + ); + if (!abort_on_null) + { + having_item= + and_items(having_item, + new + Item_is_not_null_test(this, + new + Item_direct_ref(select_lex-> + ref_pointer_array + i, + (char *)"", + (char *)"") + ) + ); + item_isnull= new + Item_func_isnull(new + Item_direct_ref( select_lex-> + ref_pointer_array+i, + (char *)"", + (char *)"") + ); + + item= new Item_cond_or(item, item_isnull); + } + + where_item= and_items(where_item, item); + } + /* + AND can't be changed during fix_fields() + we can assign select_lex->where here, and pass 0 as last + argument (reference) to fix_fields() + */ + select_lex->where= join->conds= and_items(join->conds, where_item); + select_lex->where->top_level_item(); + if (join->conds->fix_fields(thd, join->tables_list, 0)) + DBUG_RETURN(RES_ERROR); + } + if (having_item) + { + bool res; + select_lex->having= join->having= and_items(join->having, having_item); + select_lex->having->top_level_item(); /* AND can't be changed during fix_fields() we can assign select_lex->having here, and pass 0 as last argument (reference) to fix_fields() */ - select_lex->where= join->conds= and_items(join->conds, item); - if (join->conds->fix_fields(thd, join->tables_list, 0)) + select_lex->having_fix_field= 1; + res= join->having->fix_fields(thd, join->tables_list, 0); + select_lex->having_fix_field= 0; + if (res) + { DBUG_RETURN(RES_ERROR); + } } DBUG_RETURN(RES_OK); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 353ccf79ab8..a7984b525d1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1439,7 +1439,8 @@ JOIN::exec() curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having, ~ (table_map) 0, ~used_tables); - DBUG_EXECUTE("where",print_where(conds,"having after sort");); + DBUG_EXECUTE("where",print_where(curr_join->tmp_having, + "having after sort");); } } {