From 9ff33b5d93b64fa26470099b7c96414542e67f78 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Thu, 10 Aug 2006 16:45:02 +0300 Subject: [PATCH 01/11] Bug #16792 query with subselect, join, and group not returning proper values Treat queries with no FROM and aggregate functions as normal queries, so the aggregate function get correctly calculated as if there is 1 row. This means that they will be considered to have one row, so COUNT(*) will return 1 instead of 0. Other aggregates will behave in compatible manner. --- mysql-test/r/func_gconcat.result | 4 ++-- mysql-test/r/func_group.result | 24 ++++++++++++++++++++---- mysql-test/r/subselect.result | 4 ++-- mysql-test/t/func_group.test | 14 ++++++++++++++ sql/opt_sum.cc | 4 ++-- sql/sql_select.cc | 20 +++++++++++++++----- 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 2c79b8f8ab1..db0125b7d4f 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -559,14 +559,14 @@ COUNT(*) GROUP_CONCAT(DISTINCT t2.somename SEPARATOR ' |') DROP TABLE t1,t2; select * from (select group_concat('c') from DUAL) t; group_concat('c') -NULL +c create table t1 ( a int not null default 0); select * from (select group_concat(a) from t1) t2; group_concat(a) NULL select group_concat('x') UNION ALL select 1; group_concat('x') -NULL +x 1 drop table t1; CREATE TABLE t1 (id int, a varchar(9)); diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 932ef133087..04f6ebe6398 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -794,7 +794,7 @@ min(7) NULL select min(7) from DUAL; min(7) -NULL +7 explain select min(7) from t2m join t1m; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away @@ -809,7 +809,7 @@ max(7) NULL select max(7) from DUAL; max(7) -NULL +7 explain select max(7) from t2m join t1m; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away @@ -848,7 +848,7 @@ min(7) NULL select min(7) from DUAL; min(7) -NULL +7 explain select min(7) from t2i join t1i; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2i ALL NULL NULL NULL NULL 1 @@ -864,7 +864,7 @@ max(7) NULL select max(7) from DUAL; max(7) -NULL +7 explain select max(7) from t2i join t1i; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2i ALL NULL NULL NULL NULL 1 @@ -942,3 +942,19 @@ EXPLAIN SELECT MAX(b) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3); +SELECT (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT t1.b)) +1 +1 +SELECT (SELECT COUNT(DISTINCT 12)) FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT 12)) +1 +1 +SELECT AVG(2), BIT_AND(2), BIT_OR(2), BIT_XOR(2), COUNT(*), COUNT(12), +COUNT(DISTINCT 12), MIN(2),MAX(2),STD(2), VARIANCE(2),SUM(2), +GROUP_CONCAT(2),GROUP_CONCAT(DISTINCT 2); +AVG(2) BIT_AND(2) BIT_OR(2) BIT_XOR(2) COUNT(*) COUNT(12) COUNT(DISTINCT 12) MIN(2) MAX(2) STD(2) VARIANCE(2) SUM(2) GROUP_CONCAT(2) GROUP_CONCAT(DISTINCT 2) +2.0000 2 2 2 1 1 1 2 2 0.0000 0.0000 2 2 2 +DROP TABLE t1; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 983ad628425..e6b86980fbb 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1001,7 +1001,7 @@ INSERT INTO t1 VALUES (1); UPDATE t1 SET i=i+1 WHERE i=(SELECT MAX(i)); select * from t1; i -1 +2 drop table t1; CREATE TABLE t1 (a int(1)); EXPLAIN EXTENDED SELECT (SELECT RAND() FROM t1) FROM t1; @@ -1193,7 +1193,7 @@ UPDATE t1 SET t.i=i+(SELECT MAX(i) FROM (SELECT 1) t); ERROR 42S02: Unknown table 't' in field list select * from t1; i -1 +3 drop table t1; CREATE TABLE t1 ( id int(11) default NULL diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index f8a3ed0f25e..18cb5d0a430 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -617,4 +617,18 @@ SELECT MAX(b) FROM t1; EXPLAIN SELECT MAX(b) FROM t1; DROP TABLE t1; +# +# Bug #16792 query with subselect, join, and group not returning proper values +# +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3); + +SELECT (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; +SELECT (SELECT COUNT(DISTINCT 12)) FROM t1 GROUP BY t1.a; +# an attempt to test all aggregate function with no table. +SELECT AVG(2), BIT_AND(2), BIT_OR(2), BIT_XOR(2), COUNT(*), COUNT(12), + COUNT(DISTINCT 12), MIN(2),MAX(2),STD(2), VARIANCE(2),SUM(2), + GROUP_CONCAT(2),GROUP_CONCAT(DISTINCT 2); +DROP TABLE t1; + # End of 4.1 tests diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index b53fbfd3f80..bc98c96b5a8 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -182,7 +182,7 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) Type of range for the key part for this field will be returned in range_fl. */ - if ((outer_tables & table->map) || + if (table->file->inited || (outer_tables & table->map) || !find_key_for_maxmin(0, &ref, item_field->field, conds, &range_fl, &prefix_len)) { @@ -269,7 +269,7 @@ int opt_sum_query(TABLE_LIST *tables, List &all_fields,COND *conds) Type of range for the key part for this field will be returned in range_fl. */ - if ((outer_tables & table->map) || + if (table->file->inited || (outer_tables & table->map) || !find_key_for_maxmin(1, &ref, item_field->field, conds, &range_fl, &prefix_len)) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 605ef49bb07..6a3b9fe54cf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1131,7 +1131,7 @@ JOIN::exec() DBUG_VOID_RETURN; } - if (!tables_list) + if (!tables_list && (tables || !select_lex->with_sum_func)) { // Only test of functions if (select_options & SELECT_DESCRIBE) select_describe(this, FALSE, FALSE, FALSE, @@ -1170,7 +1170,12 @@ JOIN::exec() thd->examined_row_count= 0; DBUG_VOID_RETURN; } - thd->limit_found_rows= thd->examined_row_count= 0; + /* + don't reset the found rows count if there're no tables + as FOUND_ROWS() may be called. + */ + if (tables) + thd->limit_found_rows= thd->examined_row_count= 0; if (zero_result_cause) { @@ -1209,7 +1214,8 @@ JOIN::exec() having= tmp_having; select_describe(this, need_tmp, order != 0 && !skip_sort_order, - select_distinct); + select_distinct, + !tables ? "No tables used" : NullS); DBUG_VOID_RETURN; } @@ -6028,9 +6034,12 @@ do_select(JOIN *join,List *fields,TABLE *table,Procedure *procedure) else end_select=end_send; } - join->join_tab[join->tables-1].next_select=end_select; + if (join->tables) + { + join->join_tab[join->tables-1].next_select=end_select; - join_tab=join->join_tab+join->const_tables; + join_tab=join->join_tab+join->const_tables; + } join->send_records=0; if (join->tables == join->const_tables) { @@ -6048,6 +6057,7 @@ do_select(JOIN *join,List *fields,TABLE *table,Procedure *procedure) } else { + DBUG_ASSERT(join_tab); error= sub_select(join,join_tab,0); if (error >= 0) error= sub_select(join,join_tab,1); From c0389e7b0ce440b1d598d0a714d871d1fe6a7d96 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 31 Aug 2006 07:27:34 -0700 Subject: [PATCH 02/11] Fixed bug #16249: different results for a range with an without index when a range condition use an invalid DATETIME constant. Now we do not use invalid DATETIME constants to form end keys for range intervals: range analysis just ignores predicates with such constants. --- mysql-test/r/query_cache.result | 6 +++++ mysql-test/r/range.result | 45 +++++++++++++++++++++++++++++++++ mysql-test/t/range.test | 29 +++++++++++++++++++++ sql/opt_range.cc | 9 ++++++- 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 926a980f9c4..a735b52a26f 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -947,18 +947,24 @@ COUNT(*) Warnings: Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 +Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' +Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050328 invalid'; COUNT(*) 0 Warnings: Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1 +Warning 1292 Truncated incorrect DOUBLE value: '20050328 invalid' +Warning 1292 Truncated incorrect DOUBLE value: '20050328 invalid' SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050327 invalid'; COUNT(*) 0 Warnings: Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1 +Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' +Warning 1292 Truncated incorrect DOUBLE value: '20050327 invalid' show status like "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 0 diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 5c2c6e7e965..3edf56496fe 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -896,3 +896,48 @@ EXPLAIN SELECT * FROM t1 WHERE 0 NOT BETWEEN b AND c; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index_merge idx1,idx2 idx1,idx2 4,4 NULL 4 Using sort_union(idx1,idx2); Using where DROP TABLE t1; +CREATE TABLE t1 ( +item char(20) NOT NULL default '', +started datetime NOT NULL default '0000-00-00 00:00:00', +price decimal(16,3) NOT NULL default '0.000', +PRIMARY KEY (item,started) +) ENGINE=MyISAM; +INSERT INTO t1 VALUES +('A1','2005-11-01 08:00:00',1000), +('A1','2005-11-15 00:00:00',2000), +('A1','2005-12-12 08:00:00',3000), +('A2','2005-12-01 08:00:00',1000); +EXPLAIN SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 20 const 2 Using where +Warnings: +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +item started price +A1 2005-11-01 08:00:00 1000.000 +A1 2005-11-15 00:00:00 2000.000 +Warnings: +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-02 00:00:00'; +item started price +A1 2005-11-01 08:00:00 1000.000 +A1 2005-11-15 00:00:00 2000.000 +DROP INDEX `PRIMARY` ON t1; +EXPLAIN SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using where +Warnings: +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +item started price +A1 2005-11-01 08:00:00 1000.000 +A1 2005-11-15 00:00:00 2000.000 +Warnings: +Warning 1292 Incorrect datetime value: '2005-12-01 24:00:00' for column 'started' at row 1 +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-02 00:00:00'; +item started price +A1 2005-11-01 08:00:00 1000.000 +A1 2005-11-15 00:00:00 2000.000 +DROP TABLE t1; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 776c1a466ca..240851e6ac4 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -709,5 +709,34 @@ EXPLAIN SELECT * FROM t1 WHERE 0 NOT BETWEEN b AND c; DROP TABLE t1; +# +# Bug #16249: different results for a range with an without index +# when a range condition use an invalid datetime constant +# + +CREATE TABLE t1 ( + item char(20) NOT NULL default '', + started datetime NOT NULL default '0000-00-00 00:00:00', + price decimal(16,3) NOT NULL default '0.000', + PRIMARY KEY (item,started) +) ENGINE=MyISAM; + +INSERT INTO t1 VALUES +('A1','2005-11-01 08:00:00',1000), +('A1','2005-11-15 00:00:00',2000), +('A1','2005-12-12 08:00:00',3000), +('A2','2005-12-01 08:00:00',1000); + +EXPLAIN SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-02 00:00:00'; + +DROP INDEX `PRIMARY` ON t1; + +EXPLAIN SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-01 24:00:00'; +SELECT * FROM t1 WHERE item='A1' AND started<='2005-12-02 00:00:00'; + +DROP TABLE t1; # End of 5.0 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 71ba63dcf98..6189d0412b3 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -4129,6 +4129,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, MEM_ROOT *alloc= param->mem_root; char *str; ulong orig_sql_mode; + int err; DBUG_ENTER("get_mm_leaf"); /* @@ -4280,7 +4281,13 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, (field->type() == FIELD_TYPE_DATE || field->type() == FIELD_TYPE_DATETIME)) field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; - if (value->save_in_field_no_warnings(field, 1) < 0) + err= value->save_in_field_no_warnings(field, 1); + if (err > 0 && field->cmp_type() != value->result_type()) + { + tree= 0; + goto end; + } + if (err < 0) { field->table->in_use->variables.sql_mode= orig_sql_mode; /* This happens when we try to insert a NULL field in a not null column */ From e5bc4c7e779e81fe089e33a6207c907860f4b60d Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Thu, 31 Aug 2006 18:00:25 +0300 Subject: [PATCH 03/11] Bug#14654 : Cannot select from the same table twice within a UNION statement Made the parser to support parenthesis around UNION branches. This is done by amending the rules of the parser so it generates the correct structure. Currently it supports arbitrary subquery/join/parenthesis operations in the EXISTS clause. In the IN/scalar subquery case it will allow adding nested parenthesis only if there is an UNION clause after the parenthesis. Otherwise it will just treat the multiple nested parenthesis as a scalar expression. It adds extra lex level for ((SELECT ...) UNION ...) to accommodate for the UNION clause. --- mysql-test/r/subselect.result | 25 ++++++ mysql-test/t/subselect.test | 26 ++++++ sql/sql_yacc.yy | 146 +++++++++++++++++----------------- 3 files changed, 125 insertions(+), 72 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index b7f0866019c..ef11f1063e4 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3346,3 +3346,28 @@ ORDER BY t1.t DESC LIMIT 1); i1 i2 t i1 i2 t 24 1 2005-05-27 12:40:30 24 1 2006-06-20 12:29:40 DROP TABLE t1, t2; +CREATE TABLE t1 (i INT); +(SELECT i FROM t1) UNION (SELECT i FROM t1); +i +SELECT sql_no_cache * FROM t1 WHERE NOT EXISTS +( +(SELECT i FROM t1) UNION +(SELECT i FROM t1) +); +i +SELECT * FROM t1 +WHERE NOT EXISTS (((SELECT i FROM t1) UNION (SELECT i FROM t1))); +i +explain select ((select t11.i from t1 t11) union (select t12.i from t1 t12)) +from t1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'union (select t12.i from t1 t12)) +from t1' at line 1 +explain select * from t1 where not exists +((select t11.i from t1 t11) union (select t12.i from t1 t12)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table +4 UNION t12 system NULL NULL NULL NULL 0 const row not found +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +DROP TABLE t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index c9ed62f0e54..a47221dbd02 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2257,3 +2257,29 @@ SELECT * FROM t1,t2 ORDER BY t1.t DESC LIMIT 1); DROP TABLE t1, t2; + +# +# Bug#14654 : Cannot select from the same table twice within a UNION +# statement +# +CREATE TABLE t1 (i INT); + +(SELECT i FROM t1) UNION (SELECT i FROM t1); +SELECT sql_no_cache * FROM t1 WHERE NOT EXISTS + ( + (SELECT i FROM t1) UNION + (SELECT i FROM t1) + ); + +SELECT * FROM t1 +WHERE NOT EXISTS (((SELECT i FROM t1) UNION (SELECT i FROM t1))); + +#TODO:not supported +--error 1064 +explain select ((select t11.i from t1 t11) union (select t12.i from t1 t12)) + from t1; +#supported +explain select * from t1 where not exists + ((select t11.i from t1 t11) union (select t12.i from t1 t12)); + +DROP TABLE t1; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5d0a583f7d1..fd7e9fa1ab4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -725,8 +725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); predicate bit_expr bit_term bit_factor value_expr term factor table_wild simple_expr udf_expr expr_or_default set_expr_or_default interval_expr - param_marker singlerow_subselect singlerow_subselect_init - exists_subselect exists_subselect_init geometry_function + param_marker geometry_function signed_literal now_or_signed_literal opt_escape sp_opt_default simple_ident_nospvar simple_ident_q @@ -791,7 +790,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type internal_variable_name -%type in_subselect in_subselect_init +%type subselect subselect_init get_select_lex %type comp_op @@ -3914,12 +3913,14 @@ select_paren: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (sel->linkage == UNION_TYPE && - !sel->master_unit()->first_select()->braces) - { - yyerror(ER(ER_SYNTAX_ERROR)); - YYABORT; - } + if (sel->linkage == UNION_TYPE && + !sel->master_unit()->first_select()->braces && + sel->master_unit()->first_select()->linkage == + UNION_TYPE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } /* select in braces, can't contain global parameters */ if (sel->master_unit()->fake_select_lex) sel->master_unit()->global_parameters= @@ -4177,37 +4178,37 @@ bool_pri: | bool_pri EQUAL_SYM predicate { $$= new Item_func_equal($1,$3); } | bool_pri comp_op predicate %prec EQ { $$= (*$2)(0)->create($1,$3); } - | bool_pri comp_op all_or_any in_subselect %prec EQ - { $$= all_any_subquery_creator($1, $2, $3, $4); } + | bool_pri comp_op all_or_any '(' subselect ')' %prec EQ + { $$= all_any_subquery_creator($1, $2, $3, $5); } | predicate ; predicate: - bit_expr IN_SYM '(' expr_list ')' - { - if ($4->elements == 1) - $$= new Item_func_eq($1, $4->head()); - else - { - $4->push_front($1); - $$= new Item_func_in(*$4); - } - } - | bit_expr not IN_SYM '(' expr_list ')' + bit_expr IN_SYM '(' subselect ')' + { $$= new Item_in_subselect($1, $4); } + | bit_expr not IN_SYM '(' subselect ')' + { $$= negate_expression(YYTHD, new Item_in_subselect($1, $5)); } + | bit_expr IN_SYM '(' expr ')' { - if ($5->elements == 1) - $$= new Item_func_ne($1, $5->head()); - else - { - $5->push_front($1); - Item_func_in *item = new Item_func_in(*$5); + $$= new Item_func_eq($1, $4); + } + | bit_expr IN_SYM '(' expr ',' expr_list ')' + { + $6->push_front($4); + $6->push_front($1); + $$= new Item_func_in(*$6); + } + | bit_expr not IN_SYM '(' expr ')' + { + $$= new Item_func_ne($1, $5); + } + | bit_expr not IN_SYM '(' expr ',' expr_list ')' + { + $7->push_front($5); + $7->push_front($1); + Item_func_in *item = new Item_func_in(*$7); item->negate(); $$= item; - } } - | bit_expr IN_SYM in_subselect - { $$= new Item_in_subselect($1, $3); } - | bit_expr not IN_SYM in_subselect - { $$= negate_expression(YYTHD, new Item_in_subselect($1, $4)); } | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate { $$= new Item_func_between($1,$3,$5); } | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate @@ -4329,6 +4330,10 @@ simple_expr: | '-' simple_expr %prec NEG { $$= new Item_func_neg($2); } | '~' simple_expr %prec NEG { $$= new Item_func_bit_neg($2); } | not2 simple_expr %prec NEG { $$= negate_expression(YYTHD, $2); } + | '(' subselect ')' + { + $$= new Item_singlerow_subselect($2); + } | '(' expr ')' { $$= $2; } | '(' expr ',' expr_list ')' { @@ -4340,8 +4345,10 @@ simple_expr: $5->push_front($3); $$= new Item_row(*$5); } - | EXISTS exists_subselect { $$= $2; } - | singlerow_subselect { $$= $1; } + | EXISTS '(' subselect ')' + { + $$= new Item_exists_subselect($3); + } | '{' ident expr '}' { $$= $3; } | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' { $2->push_front($5); @@ -8863,49 +8870,38 @@ union_option: | ALL { $$=0; } ; -singlerow_subselect: - subselect_start singlerow_subselect_init - subselect_end - { - $$= $2; - }; +subselect: + SELECT_SYM subselect_start subselect_init subselect_end + { + $$= $3; + } + | '(' subselect_start subselect ')' + { + LEX *lex= Lex; + THD *thd= YYTHD; + /* + note that a local variable can't be used for + $3 as it's used in local variable construction + and some compilers can't guarnatee the order + in which the local variables are initialized. + */ + List_iterator it($3->item_list); + Item *item; + /* + we must fill the items list for the "derived table". + */ + while ((item= it++)) + add_item_to_list(thd, item); + } + union_clause subselect_end { $$= $3; }; -singlerow_subselect_init: - select_init2 - { - $$= new Item_singlerow_subselect(Lex->current_select-> - master_unit()->first_select()); - }; - -exists_subselect: - subselect_start exists_subselect_init - subselect_end - { - $$= $2; - }; - -exists_subselect_init: - select_init2 - { - $$= new Item_exists_subselect(Lex->current_select->master_unit()-> - first_select()); - }; - -in_subselect: - subselect_start in_subselect_init - subselect_end - { - $$= $2; - }; - -in_subselect_init: +subselect_init: select_init2 { $$= Lex->current_select->master_unit()->first_select(); }; subselect_start: - '(' SELECT_SYM { LEX *lex=Lex; if (lex->sql_command == (int)SQLCOM_HA_READ || @@ -8914,12 +8910,18 @@ subselect_start: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ if (mysql_new_select(Lex, 1)) YYABORT; }; subselect_end: - ')' { LEX *lex=Lex; lex->pop_context(); From 80cccd41aea09b26f76c74005ef6737781a5318f Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Fri, 1 Sep 2006 13:23:43 +0400 Subject: [PATCH 04/11] BUG#21477 "memory overruns for certain kinds of subqueries": make st_select_lex::setup_ref_array() take into account that Item_sum-descendant objects located within descendant SELECTs may be added into ref_pointer_array. --- sql/item_sum.cc | 4 +++- sql/sql_lex.cc | 8 ++++---- sql/sql_lex.h | 8 +++++++- sql/sql_yacc.yy | 2 ++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 0d2a5b3b080..bcd8270e52f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -290,7 +290,9 @@ Item_sum::Item_sum(THD *thd, Item_sum *item): void Item_sum::mark_as_sum_func() { - current_thd->lex->current_select->with_sum_func= 1; + SELECT_LEX *cur_select= current_thd->lex->current_select; + cur_select->n_sum_items++; + cur_select->with_sum_func= 1; with_sum_func= 1; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 563ebce4ff7..035c575724e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1521,10 +1521,10 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) */ Query_arena *arena= thd->stmt_arena; return (ref_pointer_array= - (Item **)arena->alloc(sizeof(Item*) * - (item_list.elements + - select_n_having_items + - order_group_num)* 5)) == 0; + (Item **)arena->alloc(sizeof(Item*) * (n_child_sum_items + + item_list.elements + + select_n_having_items + + order_group_num)*5)) == 0; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 220d928ccf7..fe6d60a218d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -548,6 +548,12 @@ public: bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ /* TRUE when having fix field called in processing of this SELECT */ bool having_fix_field; + + /* Number of Item_sum-derived objects in this SELECT */ + uint n_sum_items; + /* Number of Item_sum-derived objects in children and descendant SELECTs */ + uint n_child_sum_items; + /* explicit LIMIT clause was used */ bool explicit_limit; /* @@ -640,7 +646,7 @@ public: bool test_limit(); friend void lex_start(THD *thd, uchar *buf, uint length); - st_select_lex() {} + st_select_lex() : n_sum_items(0), n_child_sum_items(0) {} void make_empty_select() { init_query(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d2aca27c836..43204a33d70 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8927,8 +8927,10 @@ subselect_end: { LEX *lex=Lex; lex->pop_context(); + SELECT_LEX *child= lex->current_select; lex->current_select = lex->current_select->return_after_parsing(); lex->nest_level--; + lex->current_select->n_child_sum_items += child->n_sum_items; }; /************************************************************************** From 0e96978cf63a4e1668291a049ecebcc011e7c2ae Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Fri, 1 Sep 2006 04:23:04 -0700 Subject: [PATCH 05/11] Fixed bug #16081: row equalities were not taken into account by the optimizer. Now all row equalities are converted into conjunctions of equalities between row elements. They are taken into account by the optimizer together with the original regular equality predicates. --- mysql-test/r/join_outer.result | 2 +- mysql-test/r/row.result | 125 +++++++++ mysql-test/t/row.test | 47 ++++ sql/sql_list.h | 6 +- sql/sql_select.cc | 496 +++++++++++++++++++++------------ 5 files changed, 500 insertions(+), 176 deletions(-) diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 2d9652ff0e3..89bb26c4b3f 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -1126,7 +1126,7 @@ a b a b 7 8 7 5 EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 4 Using where +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 4 1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.a 1 EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b); id select_type table type possible_keys key key_len ref rows Extra diff --git a/mysql-test/r/row.result b/mysql-test/r/row.result index 1762587415d..5b5f8b7b954 100644 --- a/mysql-test/r/row.result +++ b/mysql-test/r/row.result @@ -181,3 +181,128 @@ SELECT ROW(1,1,1) = ROW(1,1,1) as `1`, ROW(1,1,1) = ROW(1,2,1) as `0`, ROW(1,NUL select row(NULL,1)=(2,0); row(NULL,1)=(2,0) 0 +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (1,2), (3,2), (3,3); +EXPLAIN SELECT * FROM t1 WHERE a=3 AND b=2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 8 const,const 1 Using index +EXPLAIN SELECT * FROM t1 WHERE (a,b)=(3,2); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const PRIMARY PRIMARY 8 const,const 1 Using index +SELECT * FROM t1 WHERE a=3 and b=2; +a b +3 2 +SELECT * FROM t1 WHERE (a,b)=(3,2); +a b +3 2 +CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c)); +INSERT INTO t2 VALUES +(1,1,2), (3,1,3), (1,2,2), (4,4,2), +(1,1,1), (3,1,1), (1,2,1); +EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 8 test.t1.a,test.t1.b 1 Using index +EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 8 test.t1.a,test.t1.b 1 Using index +SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b=t2.b; +a b a b c +1 1 1 1 1 +1 1 1 1 2 +1 2 1 2 1 +1 2 1 2 2 +3 1 3 1 1 +3 1 3 1 3 +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b); +a b a b c +1 1 1 1 1 +1 1 1 1 2 +1 2 1 2 1 +1 2 1 2 2 +3 1 3 1 1 +3 1 3 1 3 +EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 5 Using where; Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1 Using index +EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 5 Using where; Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1 Using index +SELECT * FROM t1,t2 WHERE t1.a=1 and t1.b=t2.b; +a b a b c +1 1 1 1 2 +1 1 3 1 3 +1 2 1 2 2 +1 1 1 1 1 +1 1 3 1 1 +1 2 1 2 1 +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2); +a b a b c +1 2 1 1 1 +1 2 1 1 2 +1 2 1 2 1 +1 2 1 2 2 +3 2 3 1 1 +3 2 3 1 3 +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 4 test.t1.a 1 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`a`) and (`test`.`t1`.`b` = (`test`.`t2`.`b` + 1))) +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1); +a b a b c +1 2 1 1 1 +1 2 1 1 2 +3 2 3 1 1 +3 2 3 1 3 +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 index NULL PRIMARY 12 NULL 7 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where (((`test`.`t1`.`a` - 1) = (`test`.`t2`.`a` - 1)) and (`test`.`t1`.`b` = (`test`.`t2`.`b` + 1))) +SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1); +a b a b c +1 2 1 1 2 +3 2 3 1 3 +1 2 1 1 1 +3 2 3 1 1 +EXPLAIN SELECT * FROM t2 WHERE a=3 AND b=2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ref PRIMARY PRIMARY 8 const,const 1 Using index +EXPLAIN SELECT * FROM t2 WHERE (a,b)=(3,2); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ref PRIMARY PRIMARY 8 const,const 1 Using index +SELECT * FROM t2 WHERE a=3 and b=2; +a b c +SELECT * FROM t2 WHERE (a,b)=(3,2); +a b c +EXPLAIN SELECT * FROM t1,t2 WHERE t2.a=t1.a AND t2.b=2 AND t2.c=1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 12 test.t1.a,const,const 1 Using index +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1)); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 12 test.t1.a,const,const 1 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`c` = 1) and (`test`.`t2`.`b` = 2) and (`test`.`t2`.`a` = `test`.`t1`.`a`)) +SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1)); +a b a b c +1 1 1 2 1 +1 2 1 2 1 +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index PRIMARY PRIMARY 8 NULL 6 Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 12 test.t1.a,const,const 1 Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`c` = 1) and (`test`.`t2`.`b` = 2) and (`test`.`t2`.`a` = `test`.`t1`.`a`)) +SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1); +a b a b c +1 1 1 2 1 +1 2 1 2 1 +DROP TABLE t1,t2; diff --git a/mysql-test/t/row.test b/mysql-test/t/row.test index 6301cc0f584..63c611e6be6 100644 --- a/mysql-test/t/row.test +++ b/mysql-test/t/row.test @@ -92,3 +92,50 @@ SELECT ROW(NULL,10) <=> ROW(3,NULL); # SELECT ROW(1,1,1) = ROW(1,1,1) as `1`, ROW(1,1,1) = ROW(1,2,1) as `0`, ROW(1,NULL,1) = ROW(2,2,1) as `0`, ROW(1,NULL,1) = ROW(1,2,2) as `0`, ROW(1,NULL,1) = ROW(1,2,1) as `null` ; select row(NULL,1)=(2,0); + +# +# Bug #16081: row equalities are to be used for query optimizations +# + +CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b)); +INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (1,2), (3,2), (3,3); + +EXPLAIN SELECT * FROM t1 WHERE a=3 AND b=2; +EXPLAIN SELECT * FROM t1 WHERE (a,b)=(3,2); +SELECT * FROM t1 WHERE a=3 and b=2; +SELECT * FROM t1 WHERE (a,b)=(3,2); + +CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c)); +INSERT INTO t2 VALUES + (1,1,2), (3,1,3), (1,2,2), (4,4,2), + (1,1,1), (3,1,1), (1,2,1); + +EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b; +EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b); +SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b=t2.b; +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b); + +EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=2; +EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2); +SELECT * FROM t1,t2 WHERE t1.a=1 and t1.b=t2.b; +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2); + +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1); +SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1); + +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1); +SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1); + +EXPLAIN SELECT * FROM t2 WHERE a=3 AND b=2; +EXPLAIN SELECT * FROM t2 WHERE (a,b)=(3,2); +SELECT * FROM t2 WHERE a=3 and b=2; +SELECT * FROM t2 WHERE (a,b)=(3,2); + +EXPLAIN SELECT * FROM t1,t2 WHERE t2.a=t1.a AND t2.b=2 AND t2.c=1; +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1)); +SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1)); + +EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1); +SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1); + +DROP TABLE t1,t2; diff --git a/sql/sql_list.h b/sql/sql_list.h index b2bcc4ea401..afad6d0f6ac 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -94,9 +94,9 @@ public: inline base_list() { empty(); } inline base_list(const base_list &tmp) :Sql_alloc() { - elements=tmp.elements; - first=tmp.first; - last=tmp.last; + elements= tmp.elements; + first= tmp.first; + last= elements ? tmp.last : &first; } inline base_list(bool error) { } inline bool push_back(void *info) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47eb19364ee..35b413e833c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6351,29 +6351,30 @@ finish: /* - Check whether an item is a simple equality predicate and if so - create/find a multiple equality for this predicate + Check whether an equality can be used to build multiple equalities SYNOPSIS - check_equality() - item item to check - cond_equal multiple equalities that must hold together with the predicate + check_simple_equality() + left_item left term of the quality to be checked + right_item right term of the equality to be checked + item equality item if the equality originates from a condition + predicate, 0 if the equality is the result of row elimination + cond_equal multiple equalities that must hold together with the equality DESCRIPTION - This function first checks whether an item is a simple equality i.e. - the one that equates a field with another field or a constant - (item=constant_item or item=field_item). - If this is the case the function looks a for a multiple equality + This function first checks whether the equality (left_item=right_item) + is a simple equality i.e. the one that equates a field with another field + or a constant (field=field_item or field=const_item). + If this is the case the function looks for a multiple equality in the lists referenced directly or indirectly by cond_equal inferring the given simple equality. If it doesn't find any, it builds a multiple equality that covers the predicate, i.e. the predicate can be inferred - from it. + from this multiple equality. The built multiple equality could be obtained in such a way: create a binary multiple equality equivalent to the predicate, then merge it, if possible, with one of old multiple equalities. This guarantees that the set of multiple equalities covering equality - predicates will - be minimal. + predicates will be minimal. EXAMPLE For the where condition @@ -6391,7 +6392,7 @@ finish: and will transform *cond_equal into (ptr(CE),[Item_equal(f,e)]). NOTES - Now only fields that have the same type defintions (verified by + Now only fields that have the same type definitions (verified by the Field::eq_def method) are placed to the same multiple equalities. Because of this some equality predicates are not eliminated and can be used in the constant propagation procedure. @@ -6424,11 +6425,263 @@ finish: copying would be much more complicated. RETURN - TRUE - if the predicate is a simple equality predicate - FALSE - otherwise + TRUE if the predicate is a simple equality predicate to be used + for building multiple equalities + FALSE otherwise */ -static bool check_equality(Item *item, COND_EQUAL *cond_equal) +static bool check_simple_equality(Item *left_item, Item *right_item, + Item *item, COND_EQUAL *cond_equal) +{ + if (left_item->type() == Item::REF_ITEM && + ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) + { + if (((Item_ref*)left_item)->depended_from) + return FALSE; + left_item= left_item->real_item(); + } + if (right_item->type() == Item::REF_ITEM && + ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF) + { + if (((Item_ref*)right_item)->depended_from) + return FALSE; + right_item= right_item->real_item(); + } + if (left_item->type() == Item::FIELD_ITEM && + right_item->type() == Item::FIELD_ITEM && + !((Item_field*)left_item)->depended_from && + !((Item_field*)right_item)->depended_from) + { + /* The predicate the form field1=field2 is processed */ + + Field *left_field= ((Item_field*) left_item)->field; + Field *right_field= ((Item_field*) right_item)->field; + + if (!left_field->eq_def(right_field)) + return FALSE; + + /* Search for multiple equalities containing field1 and/or field2 */ + bool left_copyfl, right_copyfl; + Item_equal *left_item_equal= + find_item_equal(cond_equal, left_field, &left_copyfl); + Item_equal *right_item_equal= + find_item_equal(cond_equal, right_field, &right_copyfl); + + /* As (NULL=NULL) != TRUE we can't just remove the predicate f=f */ + if (left_field->eq(right_field)) /* f = f */ + return (!(left_field->maybe_null() && !left_item_equal)); + + if (left_item_equal && left_item_equal == right_item_equal) + { + /* + The equality predicate is inference of one of the existing + multiple equalities, i.e the condition is already covered + by upper level equalities + */ + return TRUE; + } + + /* Copy the found multiple equalities at the current level if needed */ + if (left_copyfl) + { + /* left_item_equal of an upper level contains left_item */ + left_item_equal= new Item_equal(left_item_equal); + cond_equal->current_level.push_back(left_item_equal); + } + if (right_copyfl) + { + /* right_item_equal of an upper level contains right_item */ + right_item_equal= new Item_equal(right_item_equal); + cond_equal->current_level.push_back(right_item_equal); + } + + if (left_item_equal) + { + /* left item was found in the current or one of the upper levels */ + if (! right_item_equal) + left_item_equal->add((Item_field *) right_item); + else + { + /* Merge two multiple equalities forming a new one */ + left_item_equal->merge(right_item_equal); + /* Remove the merged multiple equality from the list */ + List_iterator li(cond_equal->current_level); + while ((li++) != right_item_equal); + li.remove(); + } + } + else + { + /* left item was not found neither the current nor in upper levels */ + if (right_item_equal) + right_item_equal->add((Item_field *) left_item); + else + { + /* None of the fields was found in multiple equalities */ + Item_equal *item= new Item_equal((Item_field *) left_item, + (Item_field *) right_item); + cond_equal->current_level.push_back(item); + } + } + return TRUE; + } + + { + /* The predicate of the form field=const/const=field is processed */ + Item *const_item= 0; + Item_field *field_item= 0; + if (left_item->type() == Item::FIELD_ITEM && + !((Item_field*)left_item)->depended_from && + right_item->const_item()) + { + field_item= (Item_field*) left_item; + const_item= right_item; + } + else if (right_item->type() == Item::FIELD_ITEM && + !((Item_field*)right_item)->depended_from && + left_item->const_item()) + { + field_item= (Item_field*) right_item; + const_item= left_item; + } + + if (const_item && + field_item->result_type() == const_item->result_type()) + { + bool copyfl; + + if (field_item->result_type() == STRING_RESULT) + { + CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset(); + if (!item) + { + Item_func_eq *eq_item; + if ((eq_item= new Item_func_eq(left_item, right_item))) + return FALSE; + eq_item->set_cmp_func(); + eq_item->quick_fix_field(); + item= eq_item; + } + if ((cs != ((Item_func *) item)->compare_collation()) || + !cs->coll->propagate(cs, 0, 0)) + return FALSE; + } + + Item_equal *item_equal = find_item_equal(cond_equal, + field_item->field, ©fl); + if (copyfl) + { + item_equal= new Item_equal(item_equal); + cond_equal->current_level.push_back(item_equal); + } + if (item_equal) + { + /* + The flag cond_false will be set to 1 after this, if item_equal + already contains a constant and its value is not equal to + the value of const_item. + */ + item_equal->add(const_item); + } + else + { + item_equal= new Item_equal(const_item, field_item); + cond_equal->current_level.push_back(item_equal); + } + return TRUE; + } + } + return FALSE; +} + + +/* + Convert row equalities into a conjunction of regular equalities + + SYNOPSIS + check_row_equality() + left_row left term of the row equality to be processed + right_row right term of the row equality to be processed + cond_equal multiple equalities that must hold together with the predicate + eq_list results of conversions of row equalities that are not simple + enough to form multiple equalities + + DESCRIPTION + The function converts a row equality of the form (E1,...,En)=(E'1,...,E'n) + into a list of equalities E1=E'1,...,En=E'n. For each of these equalities + Ei=E'i the function checks whether it is a simple equality or a row equality. + If it is a simple equality it is used to expand multiple equalities of + cond_equal. If it is a row equality it converted to a sequence of equalities + between row elements. If Ei=E'i is neither a simple equality nor a row + equality the item for this predicate is added to eq_list. + + RETURN + TRUE if conversion has succeeded (no fatal error) + FALSE otherwise +*/ + +static bool check_row_equality(Item *left_row, Item_row *right_row, + COND_EQUAL *cond_equal, List* eq_list) +{ + uint n= left_row->cols(); + for (uint i= 0 ; i < n; i++) + { + bool is_converted; + Item *left_item= left_row->el(i); + Item *right_item= right_row->el(i); + if (left_item->type() == Item::ROW_ITEM && + right_item->type() == Item::ROW_ITEM) + is_converted= check_row_equality((Item_row *) left_item, + (Item_row *) right_item, + cond_equal, eq_list); + else + is_converted= check_simple_equality(left_item, right_item, 0, cond_equal); + + if (!is_converted) + { + Item_func_eq *eq_item; + if (!(eq_item= new Item_func_eq(left_item, right_item))) + return FALSE; + eq_item->set_cmp_func(); + eq_item->quick_fix_field(); + eq_list->push_back(eq_item); + } + } + return TRUE; +} + + +/* + Eliminate row equalities and form multiple equalities predicates + + SYNOPSIS + check_equality() + item predicate to process + cond_equal multiple equalities that must hold together with the predicate + eq_list results of conversions of row equalities that are not simple + enough to form multiple equalities + + DESCRIPTION + This function checks whether the item is a simple equality + i.e. the one that equates a field with another field or a constant + (field=field_item or field=constant_item), or, a row equality. + For a simple equality the function looks for a multiple equality + in the lists referenced directly or indirectly by cond_equal inferring + the given simple equality. If it doesn't find any, it builds/expands + multiple equality that covers the predicate. + Row equalities are eliminated substituted for conjunctive regular equalities + which are treated in the same way as original equality predicates. + + RETURN + TRUE if re-writing rules have been applied + FALSE otherwise, i.e. + if the predicate is not an equality, + or, if the equality is neither a simple one nor a row equality, + or, if the procedure fails by a fatal error. +*/ + +static bool check_equality(Item *item, COND_EQUAL *cond_equal, + List *eq_list) { if (item->type() == Item::FUNC_ITEM && ((Item_func*) item)->functype() == Item_func::EQ_FUNC) @@ -6436,158 +6689,18 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) Item *left_item= ((Item_func*) item)->arguments()[0]; Item *right_item= ((Item_func*) item)->arguments()[1]; - if (left_item->type() == Item::REF_ITEM && - ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF) - { - if (((Item_ref*)left_item)->depended_from) - return FALSE; - left_item= left_item->real_item(); - } - if (right_item->type() == Item::REF_ITEM && - ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF) - { - if (((Item_ref*)right_item)->depended_from) - return FALSE; - right_item= right_item->real_item(); - } - if (left_item->type() == Item::FIELD_ITEM && - right_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && - !((Item_field*)right_item)->depended_from) - { - /* The predicate the form field1=field2 is processed */ - - Field *left_field= ((Item_field*) left_item)->field; - Field *right_field= ((Item_field*) right_item)->field; - - if (!left_field->eq_def(right_field)) - return FALSE; - - if (left_field->eq(right_field)) /* f = f */ - return TRUE; - - /* Search for multiple equalities containing field1 and/or field2 */ - bool left_copyfl, right_copyfl; - Item_equal *left_item_equal= - find_item_equal(cond_equal, left_field, &left_copyfl); - Item_equal *right_item_equal= - find_item_equal(cond_equal, right_field, &right_copyfl); - - if (left_item_equal && left_item_equal == right_item_equal) - { - /* - The equality predicate is inference of one of the existing - multiple equalities, i.e the condition is already covered - by upper level equalities - */ - return TRUE; - } - - /* Copy the found multiple equalities at the current level if needed */ - if (left_copyfl) - { - /* left_item_equal of an upper level contains left_item */ - left_item_equal= new Item_equal(left_item_equal); - cond_equal->current_level.push_back(left_item_equal); - } - if (right_copyfl) - { - /* right_item_equal of an upper level contains right_item */ - right_item_equal= new Item_equal(right_item_equal); - cond_equal->current_level.push_back(right_item_equal); - } - - if (left_item_equal) - { - /* left item was found in the current or one of the upper levels */ - if (! right_item_equal) - left_item_equal->add((Item_field *) right_item); - else - { - /* Merge two multiple equalities forming a new one */ - left_item_equal->merge(right_item_equal); - /* Remove the merged multiple equality from the list */ - List_iterator li(cond_equal->current_level); - while ((li++) != right_item_equal); - li.remove(); - } - } - else - { - /* left item was not found neither the current nor in upper levels */ - if (right_item_equal) - right_item_equal->add((Item_field *) left_item); - else - { - /* None of the fields was found in multiple equalities */ - Item_equal *item= new Item_equal((Item_field *) left_item, - (Item_field *) right_item); - cond_equal->current_level.push_back(item); - } - } - return TRUE; - } - - { - /* The predicate of the form field=const/const=field is processed */ - Item *const_item= 0; - Item_field *field_item= 0; - if (left_item->type() == Item::FIELD_ITEM && - !((Item_field*)left_item)->depended_from && - right_item->const_item()) - { - field_item= (Item_field*) left_item; - const_item= right_item; - } - else if (right_item->type() == Item::FIELD_ITEM && - !((Item_field*)right_item)->depended_from && - left_item->const_item()) - { - field_item= (Item_field*) right_item; - const_item= left_item; - } - - if (const_item && - field_item->result_type() == const_item->result_type()) - { - bool copyfl; - - if (field_item->result_type() == STRING_RESULT) - { - CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset(); - if ((cs != ((Item_cond *) item)->compare_collation()) || - !cs->coll->propagate(cs, 0, 0)) - return FALSE; - } - - Item_equal *item_equal = find_item_equal(cond_equal, - field_item->field, ©fl); - if (copyfl) - { - item_equal= new Item_equal(item_equal); - cond_equal->current_level.push_back(item_equal); - } - if (item_equal) - { - /* - The flag cond_false will be set to 1 after this, if item_equal - already contains a constant and its value is not equal to - the value of const_item. - */ - item_equal->add(const_item); - } - else - { - item_equal= new Item_equal(const_item, field_item); - cond_equal->current_level.push_back(item_equal); - } - return TRUE; - } - } - } + if (left_item->type() == Item::ROW_ITEM && + right_item->type() == Item::ROW_ITEM) + return check_row_equality((Item_row *) left_item, + (Item_row *) right_item, + cond_equal, eq_list); + else + return check_simple_equality(left_item, right_item, item, cond_equal); + } return FALSE; } + /* Replace all equality predicates in a condition by multiple equality items @@ -6659,10 +6772,12 @@ static COND *build_equal_items_for_cond(COND *cond, Item_equal *item_equal; uint members; COND_EQUAL cond_equal; + COND *new_cond; cond_equal.upper_levels= inherited; if (cond->type() == Item::COND_ITEM) { + List eq_list; bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; List *args= ((Item_cond*) cond)->argument_list(); @@ -6685,7 +6800,7 @@ static COND *build_equal_items_for_cond(COND *cond, structure here because it's restored before each re-execution of any prepared statement/stored procedure. */ - if (check_equality(item, &cond_equal)) + if (check_equality(item, &cond_equal, &eq_list)) li.remove(); } @@ -6732,10 +6847,14 @@ static COND *build_equal_items_for_cond(COND *cond, } } if (and_level) + { + args->concat(&eq_list); args->concat((List *)&cond_equal.current_level); + } } else if (cond->type() == Item::FUNC_ITEM) { + List eq_list; /* If an equality predicate forms the whole and level, we call it standalone equality and it's processed here. @@ -6746,12 +6865,45 @@ static COND *build_equal_items_for_cond(COND *cond, for WHERE a=b AND c=d AND (b=c OR d=5) b=c is replaced by =(a,b,c,d). */ - if (check_equality(cond, &cond_equal) && - (item_equal= cond_equal.current_level.pop())) + if (check_equality(cond, &cond_equal, &eq_list)) { - item_equal->fix_length_and_dec(); - item_equal->update_used_tables(); - return item_equal; + int n= cond_equal.current_level.elements + eq_list.elements; + if (n == 0) + return new Item_int((longlong) 1,1); + else if (n == 1) + { + if ((item_equal= cond_equal.current_level.pop())) + { + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); + return item_equal; + } + else + return eq_list.pop(); + } + else + { + /* + Here a new AND level must be created. It can happen only + when a row equality is processed as a standalone predicate. + */ + Item_cond_and *and_cond= new Item_cond_and(eq_list); + and_cond->quick_fix_field(); + List *args= and_cond->argument_list(); + List_iterator_fast it(cond_equal.current_level); + while ((item_equal= it++)) + { + item_equal->fix_length_and_dec(); + item_equal->update_used_tables(); + members= item_equal->members(); + if (cond_equal.max_members < members) + cond_equal.max_members= members; + } + and_cond->cond_equal= cond_equal; + args->concat((List *)&cond_equal.current_level); + + return and_cond; + } } /* For each field reference in cond, not from equal item predicates, @@ -7038,7 +7190,7 @@ static Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, /* Substitute every field reference in a condition by the best equal field - and eliminate all multiplle equality predicates + and eliminate all multiple equality predicates SYNOPSIS substitute_for_best_equal_field() From 3758b975f8c535917c9507b005ccc8ff5a76bfb8 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Mon, 4 Sep 2006 18:40:30 +0300 Subject: [PATCH 06/11] Bug #21392: multi-table delete with alias table name fails with 1003: Incorrect table name in multi-table DELETE the set of tables to delete from actually references then tables in the other list, e.g: DELETE alias_of_t1 FROM t1 alias_of_t1 WHERE .... is a valid statement. So we must turn off table name syntactical validity check for alias_of_t1 because it's not a table name (even if it looks like one). In order to do that we add a special flag (TL_OPTION_ALIAS) to disable the name checking for the aliases in multi-table DELETE. --- mysql-test/r/delete.result | 4 ++++ mysql-test/t/delete.test | 10 ++++++++++ sql/mysql_priv.h | 1 + sql/sql_parse.cc | 4 +++- sql/sql_yacc.yy | 7 +++++-- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 411cd52b4ca..cb632fcd6c8 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -172,3 +172,7 @@ a 0 2 DROP TABLE t1; +create table t1 (a int); +delete `4.t1` from t1 as `4.t1` where `4.t1`.a = 5; +delete FROM `4.t1` USING t1 as `4.t1` where `4.t1`.a = 5; +drop table t1; diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 98e4c4e35fa..d4eb01cab23 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -153,4 +153,14 @@ DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a LIMIT 1; SELECT * FROM t1; DROP TABLE t1; +# +# Bug #21392: multi-table delete with alias table name fails with +# 1003: Incorrect table name +# + +create table t1 (a int); +delete `4.t1` from t1 as `4.t1` where `4.t1`.a = 5; +delete FROM `4.t1` USING t1 as `4.t1` where `4.t1`.a = 5; +drop table t1; + # End of 4.1 tests diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9c5bcc2d53f..4a5658c5ccf 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -305,6 +305,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); #define TL_OPTION_UPDATING 1 #define TL_OPTION_FORCE_INDEX 2 #define TL_OPTION_IGNORE_LEAVES 4 +#define TL_OPTION_ALIAS 8 /* Some portable defines */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cf1f50aed63..c84c96e1e75 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4863,6 +4863,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) table_options A set of the following bits: TL_OPTION_UPDATING Table will be updated TL_OPTION_FORCE_INDEX Force usage of index + TL_OPTION_ALIAS an alias in multi table DELETE lock_type How table should be locked use_index List of indexed used in USE INDEX ignore_index List of indexed used in IGNORE INDEX @@ -4888,7 +4889,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (!table) DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; - if (check_table_name(table->table.str,table->table.length) || + if (!test(table_options & TL_OPTION_ALIAS) && + check_table_name(table->table.str,table->table.length) || table->db.str && check_db_name(table->db.str)) { net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 162b4183c84..53e7b103f32 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4345,14 +4345,17 @@ table_wild_one: ident opt_wild opt_table_alias { if (!Select->add_table_to_list(YYTHD, new Table_ident($1), $3, - TL_OPTION_UPDATING, Lex->lock_option)) + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, Lex->lock_option)) YYABORT; } | ident '.' ident opt_wild opt_table_alias { if (!Select->add_table_to_list(YYTHD, new Table_ident(YYTHD, $1, $3, 0), - $5, TL_OPTION_UPDATING, + $5, + TL_OPTION_UPDATING | + TL_OPTION_ALIAS, Lex->lock_option)) YYABORT; } From 7eaa08e0920f15245333746aaf12580f5a2191ce Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Tue, 5 Sep 2006 19:07:55 +0300 Subject: [PATCH 07/11] Merge bug #16792 4.1->5.0 --- mysql-test/r/func_group.result | 32 ++++++++++++++++---------------- mysql-test/r/innodb_mysql.result | 8 ++++---- mysql-test/r/union.result | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index f97d15a24ae..98eb9e9e2b3 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -845,22 +845,6 @@ EXPLAIN SELECT MAX(b) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT); -INSERT INTO t1 VALUES (1,1),(1,2),(2,3); -SELECT (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; -(SELECT COUNT(DISTINCT t1.b)) -1 -1 -SELECT (SELECT COUNT(DISTINCT 12)) FROM t1 GROUP BY t1.a; -(SELECT COUNT(DISTINCT 12)) -1 -1 -SELECT AVG(2), BIT_AND(2), BIT_OR(2), BIT_XOR(2), COUNT(*), COUNT(12), -COUNT(DISTINCT 12), MIN(2),MAX(2),STD(2), VARIANCE(2),SUM(2), -GROUP_CONCAT(2),GROUP_CONCAT(DISTINCT 2); -AVG(2) BIT_AND(2) BIT_OR(2) BIT_XOR(2) COUNT(*) COUNT(12) COUNT(DISTINCT 12) MIN(2) MAX(2) STD(2) VARIANCE(2) SUM(2) GROUP_CONCAT(2) GROUP_CONCAT(DISTINCT 2) -2.0000 2 2 2 1 1 1 2 2 0.0000 0.0000 2 2 2 -DROP TABLE t1; CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; INSERT INTO t1 VALUES (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), @@ -872,6 +856,22 @@ EXPLAIN SELECT MAX(b) FROM t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 4 DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3); +SELECT (SELECT COUNT(DISTINCT t1.b)) FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT t1.b)) +0 +2 +SELECT (SELECT COUNT(DISTINCT 12)) FROM t1 GROUP BY t1.a; +(SELECT COUNT(DISTINCT 12)) +1 +1 +SELECT AVG(2), BIT_AND(2), BIT_OR(2), BIT_XOR(2), COUNT(*), COUNT(12), +COUNT(DISTINCT 12), MIN(2),MAX(2),STD(2), VARIANCE(2),SUM(2), +GROUP_CONCAT(2),GROUP_CONCAT(DISTINCT 2); +AVG(2) BIT_AND(2) BIT_OR(2) BIT_XOR(2) COUNT(*) COUNT(12) COUNT(DISTINCT 12) MIN(2) MAX(2) STD(2) VARIANCE(2) SUM(2) GROUP_CONCAT(2) GROUP_CONCAT(DISTINCT 2) +2.00000 2 2 2 1 1 1 2 2 0.00000 0.00000 2 2 2 +DROP TABLE t1; create table t2 (ff double); insert into t2 values (2.2); select cast(sum(distinct ff) as decimal(5,2)) from t2; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index e7d097a1d2f..b4101e037f2 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -118,7 +118,7 @@ min(7) NULL select min(7) from DUAL; min(7) -NULL +7 explain select min(7) from t2m join t1m; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away @@ -133,7 +133,7 @@ max(7) NULL select max(7) from DUAL; max(7) -NULL +7 explain select max(7) from t2m join t1m; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away @@ -172,7 +172,7 @@ min(7) NULL select min(7) from DUAL; min(7) -NULL +7 explain select min(7) from t2i join t1i; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2i ALL NULL NULL NULL NULL 1 @@ -188,7 +188,7 @@ max(7) NULL select max(7) from DUAL; max(7) -NULL +7 explain select max(7) from t2i join t1i; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2i ALL NULL NULL NULL NULL 1 diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 42a3874db08..12463658bf9 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1365,7 +1365,7 @@ drop table t1, t2; (select avg(1)) union (select avg(1)) union (select avg(1)) union (select avg(1)) union (select avg(1)) union (select avg(1)); avg(1) -NULL +1.0000 select _utf8'12' union select _latin1'12345'; 12 12 From b7ded1e34fe28d76a26b0372867b15393c5c725c Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Wed, 6 Sep 2006 08:21:43 -0700 Subject: [PATCH 08/11] Fixed bug #5500: EXPLAIN returned a wrong select_type for queries using views. Select_type in the EXPLAIN output for the query SELECT * FROM t1 was 'SIMPLE', while for the query SELECT * FROM v1, where the view v1 was defined as SELECT * FROM t1, the EXPLAIN output contained 'PRIMARY' for the select_type column. --- mysql-test/r/group_by.result | 4 +- mysql-test/r/information_schema.result | 2 +- mysql-test/r/olap.result | 2 +- mysql-test/r/range.result | 8 ++-- mysql-test/r/view.result | 51 ++++++++++++++++++-------- mysql-test/r/view_grant.result | 6 +-- mysql-test/t/view.test | 18 +++++++++ sql/sql_select.cc | 11 ++++-- 8 files changed, 71 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 57cb09fe44c..ef057e04d1a 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -807,8 +807,8 @@ explain SELECT straight_join sql_no_cache v1.a, v1.b, v1.real_b from t2, v1 where t2.b=v1.a GROUP BY t2.b; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 index b b 2 NULL 10 Using index -1 PRIMARY t1 eq_ref PRIMARY PRIMARY 1 test.t2.b 1 +1 SIMPLE t2 index b b 2 NULL 10 Using index +1 SIMPLE t1 eq_ref PRIMARY PRIMARY 1 test.t2.b 1 SELECT straight_join sql_no_cache v1.a, v1.b, v1.real_b from t2, v1 where t2.b=v1.a GROUP BY t2.b; a b real_b diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 652af1c8387..407f8a040b7 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -337,7 +337,7 @@ mysql test explain select * from v0; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY # ALL NULL NULL NULL NULL 2 +1 SIMPLE # ALL NULL NULL NULL NULL 2 create view v1 (c) as select table_name from information_schema.tables where table_name="v1"; select * from v1; diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result index 28c1dc59540..7cdd5e1b152 100644 --- a/mysql-test/r/olap.result +++ b/mysql-test/r/olap.result @@ -611,7 +611,7 @@ C NULL EXPLAIN SELECT type FROM v1 GROUP BY type WITH ROLLUP; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 10 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 10 Using filesort DROP VIEW v1; DROP TABLE t1; CREATE TABLE t1 (a int(11) NOT NULL); diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 3edf56496fe..add95613d62 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -750,13 +750,13 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index EXPLAIN SELECT a,b FROM v1 WHERE a < 2 and b=3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index EXPLAIN SELECT a,b FROM t1 WHERE a < 2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index EXPLAIN SELECT a,b FROM v1 WHERE a < 2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 4 Using where; Using index SELECT a,b FROM t1 WHERE a < 2 and b=3; a b 1 3 @@ -799,13 +799,13 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index explain select * from v1 where a in (3,4) and b in (1,2,3); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index +1 SIMPLE t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index explain select * from t1 where a between 3 and 4 and b between 1 and 2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index explain select * from v1 where a between 3 and 4 and b between 1 and 2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index +1 SIMPLE t1 range PRIMARY PRIMARY 8 NULL # Using where; Using index drop view v1; drop table t1; create table t3 (a int); diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index f9267b85134..55406e76586 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -49,7 +49,7 @@ select v1.b from v1; ERROR 42S22: Unknown column 'v1.b' in 'field list' explain extended select c from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Warnings: Note 1003 select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1` create algorithm=temptable view v2 (c) as select b+1 from t1; @@ -83,7 +83,7 @@ c 12 explain extended select c from v3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Warnings: Note 1003 select ((`test`.`t1`.`b` + 1) + 1) AS `c` from `test`.`t1` create algorithm=temptable view v4 (c) as select c+1 from v2; @@ -376,7 +376,7 @@ c 30 explain extended select * from v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where +1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using where Warnings: Note 1003 select `test`.`t1`.`b` AS `c` from `test`.`t1` where (`test`.`t1`.`a` < 3) update v1 set c=c+1; @@ -1391,9 +1391,9 @@ a a b 4 NULL NULL explain extended select * from t3 left join v3 on (t3.a = v3.a); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 3 -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 -1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 ALL NULL NULL NULL NULL 3 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Warnings: Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `b` from `test`.`t3` left join (`test`.`t1` left join `test`.`t2` on((`test`.`t1`.`a` = `test`.`t2`.`a`))) on((`test`.`t3`.`a` = `test`.`t1`.`a`)) where 1 create view v1 (a) as select a from t1; @@ -1406,9 +1406,9 @@ a a b 4 NULL NULL explain extended select * from t3 left join v4 on (t3.a = v4.a); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 3 -1 PRIMARY t1 ALL NULL NULL NULL NULL 3 -1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 ALL NULL NULL NULL NULL 3 +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Warnings: Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `b` from `test`.`t3` left join (`test`.`t1` left join (`test`.`t2`) on((`test`.`t1`.`a` = `test`.`t2`.`a`))) on((`test`.`t3`.`a` = `test`.`t1`.`a`)) where 1 prepare stmt1 from "select * from t3 left join v4 on (t3.a = v4.a);"; @@ -2321,12 +2321,12 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using where; Using index EXPLAIN SELECT * FROM v1 WHERE a=1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ref a a 5 const 1 Using where; Using index -1 PRIMARY t2 ref a a 10 const,test.t1.b 2 Using where; Using index +1 SIMPLE t1 ref a a 5 const 1 Using where; Using index +1 SIMPLE t2 ref a a 10 const,test.t1.b 2 Using where; Using index EXPLAIN SELECT * FROM v2 WHERE a=1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ref a a 5 const 1 Using where; Using index -1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where +1 SIMPLE t1 ref a a 5 const 1 Using where; Using index +1 SIMPLE t3 ALL NULL NULL NULL NULL 3 Using where DROP VIEW v1,v2; DROP TABLE t1,t2,t3; create table t1 (f1 int); @@ -2409,7 +2409,7 @@ insert into t1 values (1),(2); create view v1 as select * from t1; explain select id from v1 order by id; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 index NULL PRIMARY 4 NULL 2 Using index +1 SIMPLE t1 index NULL PRIMARY 4 NULL 2 Using index drop view v1; drop table t1; create table t1(f1 int, f2 int); @@ -2480,7 +2480,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away EXPLAIN SELECT MAX(a) FROM v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away SELECT MIN(a) FROM t1; MIN(a) 0 @@ -2492,7 +2492,7 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away EXPLAIN SELECT MIN(a) FROM v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away DROP VIEW v1; DROP TABLE t1; CREATE TABLE t1 (x varchar(10)); @@ -2879,3 +2879,22 @@ View Create View v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`f1` AS `f1` from `t1` where (`t1`.`f1` between now() and (now() + interval 1 minute)) drop view v1; drop table t1; +CREATE TABLE t1 (s1 int); +CREATE VIEW v1 AS SELECT * FROM t1; +EXPLAIN SELECT * FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found +EXPLAIN SELECT * FROM v1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found +INSERT INTO t1 VALUES (1), (3), (2); +EXPLAIN SELECT * FROM t1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t ALL NULL NULL NULL NULL 3 Using where +2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 +EXPLAIN SELECT * FROM v1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where +2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 7f63d790fb8..35e7afc0a7b 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -103,7 +103,7 @@ ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for tabl grant select on mysqltest.t1 to mysqltest_1@localhost; explain select c from mysqltest.v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found show create view mysqltest.v1; ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for table 'v1' explain select c from mysqltest.v2; @@ -123,7 +123,7 @@ ERROR 42000: SHOW VIEW command denied to user 'mysqltest_1'@'localhost' for tabl grant show view on mysqltest.* to mysqltest_1@localhost; explain select c from mysqltest.v1; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found +1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found show create view mysqltest.v1; View Create View v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` @@ -136,7 +136,7 @@ View Create View v2 CREATE ALGORITHM=TEMPTABLE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1` explain select c from mysqltest.v3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t2 system NULL NULL NULL NULL 0 const row not found +1 SIMPLE t2 system NULL NULL NULL NULL 0 const row not found show create view mysqltest.v3; View Create View v3 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `mysqltest`.`v3` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2` diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index fae3f856cb8..22f9f2e9ab7 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2760,3 +2760,21 @@ create view v1 as select * from t1 where f1 between now() and now() + interval 1 show create view v1; drop view v1; drop table t1; + +# +# Bug #5500: wrong select_type in EXPLAIN output for queries over views +# + +CREATE TABLE t1 (s1 int); +CREATE VIEW v1 AS SELECT * FROM t1; + +EXPLAIN SELECT * FROM t1; +EXPLAIN SELECT * FROM v1; + +INSERT INTO t1 VALUES (1), (3), (2); + +EXPLAIN SELECT * FROM t1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); +EXPLAIN SELECT * FROM v1 t WHERE t.s1+1 < (SELECT MAX(t1.s1) FROM t1); + +DROP VIEW v1; +DROP TABLE t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47eb19364ee..1ff62a5f699 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -14221,9 +14221,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, item_list.push_back(new Item_string(table_name_buffer, len, cs)); } else - item_list.push_back(new Item_string(table->alias, - strlen(table->alias), + { + TABLE_LIST *tab=table->pos_in_table_list; + item_list.push_back(new Item_string(tab->alias, + strlen(tab->alias), cs)); + } /* type */ item_list.push_back(new Item_string(join_type_str[tab->type], strlen(join_type_str[tab->type]), @@ -14410,8 +14413,8 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) // drop UNCACHEABLE_EXPLAIN, because it is for internal usage only uint8 uncacheable= (sl->uncacheable & ~UNCACHEABLE_EXPLAIN); sl->type= (((&thd->lex->select_lex)==sl)? - ((thd->lex->all_selects_list != sl) ? - primary_key_name : "SIMPLE"): + (sl->first_inner_unit() || sl->next_select() ? + "PRIMARY" : "SIMPLE"): ((sl == first)? ((sl->linkage == DERIVED_TABLE_TYPE) ? "DERIVED": From 34206c6f80578375b8df6e64cc3185b7bec27dba Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Thu, 7 Sep 2006 11:06:37 -0700 Subject: [PATCH 09/11] Fixed bug #21698: erroneously a field could be replaced by an equal constant under any circumstances. In fact this substitution can be allowed if the field is not of a type string or if the field reference serves as an argument of a comparison predicate. --- mysql-test/r/func_str.result | 35 +++++++++++++++++++ mysql-test/r/heap_hash.result | 2 +- mysql-test/t/func_str.test | 27 +++++++++++++++ sql/item.cc | 41 ++++++++++++++++++++-- sql/item.h | 18 +++++++++- sql/item_cmpfunc.cc | 61 +++++++++++++++++++++++++++++---- sql/item_cmpfunc.h | 4 +++ sql/item_func.cc | 64 ++++++++++++++++++++++++++++++----- sql/item_func.h | 2 ++ sql/sql_select.cc | 17 +++++++--- 10 files changed, 247 insertions(+), 24 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 14da630f61e..94fb570381a 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1113,4 +1113,39 @@ conv("18383815659218730760",10,10) + 0 select "18383815659218730760" + 0; "18383815659218730760" + 0 1.8383815659219e+19 +CREATE TABLE t1 (code varchar(10)); +INSERT INTO t1 VALUES ('a12'), ('A12'), ('a13'); +SELECT ASCII(code), code FROM t1 WHERE code='A12'; +ASCII(code) code +97 a12 +65 A12 +SELECT ASCII(code), code FROM t1 WHERE code='A12' AND ASCII(code)=65; +ASCII(code) code +65 A12 +INSERT INTO t1 VALUES ('a12 '), ('A12 '); +SELECT LENGTH(code), code FROM t1 WHERE code='A12'; +LENGTH(code) code +3 a12 +3 A12 +4 a12 +5 A12 +SELECT LENGTH(code), code FROM t1 WHERE code='A12' AND LENGTH(code)=5; +LENGTH(code) code +5 A12 +ALTER TABLE t1 ADD INDEX (code); +CREATE TABLE t2 (id varchar(10) PRIMARY KEY); +INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14'); +SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id +WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5); +code id +A12 a12 +EXPLAIN EXTENDED +SELECT * FROM t1 INNER JOIN t2 ON code=id +WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref code code 13 const 3 Using where; Using index +1 SIMPLE t2 ref PRIMARY PRIMARY 12 const 1 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`code` AS `code`,`test`.`t2`.`id` AS `id` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`code` = _latin1'a12') and (`test`.`t2`.`id` = _latin1'a12') and (length(`test`.`t1`.`code`) = 5)) +DROP TABLE t1,t2; End of 5.0 tests diff --git a/mysql-test/r/heap_hash.result b/mysql-test/r/heap_hash.result index e0835bbf8d6..80abcebbfea 100644 --- a/mysql-test/r/heap_hash.result +++ b/mysql-test/r/heap_hash.result @@ -354,7 +354,7 @@ t3 1 a 2 b NULL 13 NULL NULL HASH explain select * from t1 ignore key(btree_idx), t3 where t1.name='matt' and t3.a = concat('',t1.name) and t3.b=t1.name; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref heap_idx heap_idx 22 const 7 Using where -1 SIMPLE t3 ref a a 44 const,const 7 Using where +1 SIMPLE t3 ref a a 44 func,const 7 Using where drop table t1, t2, t3; create temporary table t1 ( a int, index (a) ) engine=memory; insert into t1 values (1),(2),(3),(4),(5); diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 8753db0ebe1..415c6d2a44e 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -753,4 +753,31 @@ select cast(rtrim(ltrim(' 20.06 ')) as decimal(19,2)); select conv("18383815659218730760",10,10) + 0; select "18383815659218730760" + 0; +# +# Bug #21698: substitution of a string field for a constant under a function +# + +CREATE TABLE t1 (code varchar(10)); +INSERT INTO t1 VALUES ('a12'), ('A12'), ('a13'); + +SELECT ASCII(code), code FROM t1 WHERE code='A12'; +SELECT ASCII(code), code FROM t1 WHERE code='A12' AND ASCII(code)=65; + +INSERT INTO t1 VALUES ('a12 '), ('A12 '); + +SELECT LENGTH(code), code FROM t1 WHERE code='A12'; +SELECT LENGTH(code), code FROM t1 WHERE code='A12' AND LENGTH(code)=5; + +ALTER TABLE t1 ADD INDEX (code); +CREATE TABLE t2 (id varchar(10) PRIMARY KEY); +INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14'); + +SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id + WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5); +EXPLAIN EXTENDED +SELECT * FROM t1 INNER JOIN t2 ON code=id + WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5); + +DROP TABLE t1,t2; + --echo End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index 96b20d0f0bb..c3b950bc32b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3745,14 +3745,49 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) } +/* + Check whether a field can be substituted by an equal item + + SYNOPSIS + equal_fields_propagator() + arg - *arg != NULL <-> the field is in the context where + substitution for an equal item is valid + + DESCRIPTION + The function checks whether a substitution of the field + occurrence for an equal item is valid. + + NOTES + The following statement is not always true: + x=y => F(x)=F(x/y). + This means substitution of an item for an equal item not always + yields an equavalent condition. + Here's an example: + 'a'='a ' + (LENGTH('a')=1) != (LENGTH('a ')=2) + Such a substitution is surely valid if either the substituted + field is not of a STRING type or if it is an argument of + a comparison predicate. + + RETURN + TRUE substitution is valid + FALSE otherwise +*/ + +bool Item_field::subst_argument_checker(byte **arg) +{ + return (result_type() != STRING_RESULT) || (*arg); +} + + /* Set a pointer to the multiple equality the field reference belongs to (if any) SYNOPSIS equal_fields_propagator() - arg - reference to list of multiple equalities where - the field (this object) is to be looked for + arg - reference to list of multiple equalities where + the field (this object) is to be looked for DESCRIPTION The function looks for a multiple equality containing the field item @@ -3764,7 +3799,7 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) NOTES This function is supposed to be called as a callback parameter in calls - of the transform method. + of the compile method. RETURN VALUES pointer to the replacing constant item, if the field item was substituted diff --git a/sql/item.h b/sql/item.h index e3df0fdf389..f6145d6e20c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -410,7 +410,8 @@ public: }; -typedef bool (Item::*Item_processor)(byte *arg); +typedef bool (Item::*Item_processor) (byte *arg); +typedef bool (Item::*Item_analyzer) (byte **argp); typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); @@ -739,6 +740,14 @@ public: return (this->*transformer)(arg); } + virtual Item* compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) + { + if ((this->*analyzer) (arg_p)) + return ((this->*transformer) (arg_t)); + return 0; + } + virtual void traverse_cond(Cond_traverser traverser, void *arg, traverse_order order) { @@ -753,6 +762,12 @@ public: virtual bool change_context_processor(byte *context) { return 0; } virtual bool reset_query_id_processor(byte *query_id) { return 0; } virtual bool is_expensive_processor(byte *arg) { return 0; } + virtual bool subst_argument_checker(byte **arg) + { + if (*arg) + *arg= NULL; + return TRUE; + } virtual Item *equal_fields_propagator(byte * arg) { return this; } virtual Item *set_no_const_sub(byte *arg) { return this; } @@ -1254,6 +1269,7 @@ public: return field->can_be_compared_as_longlong(); } Item_equal *find_item_equal(COND_EQUAL *cond_equal); + bool subst_argument_checker(byte **arg); Item *equal_fields_propagator(byte *arg); Item *set_no_const_sub(byte *arg); Item *replace_equal_field(byte *arg); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index dbf380232c4..fa01ae65b0d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2747,16 +2747,16 @@ bool Item_cond::walk(Item_processor processor, byte *arg) SYNOPSIS transform() - transformer the transformer callback function to be applied to the nodes - of the tree of the object - arg parameter to be passed to the transformer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg parameter to be passed to the transformer DESCRIPTION - The function recursively applies the transform method with the - same transformer to each member item of the condition list. + The function recursively applies the transform method to each + member item of the condition list. If the call of the method for a member item returns a new item the old item is substituted for a new one. - After this the transform method is applied to the root node + After this the transformer is applied to the root node of the Item_cond object. RETURN VALUES @@ -2778,6 +2778,55 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg) return Item_func::transform(transformer, arg); } + +/* + Compile Item_cond object with a processor and a transformer callback functions + + SYNOPSIS + compile() + analyzer the analyzer callback function to be applied to the nodes + of the tree of the object + arg_p in/out parameter to be passed to the analyzer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg_t parameter to be passed to the transformer + + DESCRIPTION + First the function applies the analyzer to the root node of + the Item_func object. Then if the analyzer succeeeds (returns TRUE) + the function recursively applies the compile method to member + item of the condition list. + If the call of the method for a member item returns a new item + the old item is substituted for a new one. + After this the transformer is applied to the root node + of the Item_cond object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) +{ + if (!(this->*analyzer)(arg_p)) + return 0; + + byte *arg_v= *arg_p; + List_iterator li(list); + Item *item; + while ((item= li++)) + { + /* + The same parameter value of arg_p must be passed + to analyze any argument of the condition formula. + */ + Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t); + if (new_item && new_item != item) + li.replace(new_item); + } + return Item_func::transform(transformer, arg_t); +} + void Item_cond::traverse_cond(Cond_traverser traverser, void *arg, traverse_order order) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 47f9f2aa98f..f2c43833bd9 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -240,6 +240,7 @@ public: } Item *neg_transformer(THD *thd); virtual Item *negated_item(); + bool subst_argument_checker(byte **arg) { return TRUE; } }; class Item_func_not :public Item_bool_func @@ -1171,6 +1172,9 @@ public: Item *transform(Item_transformer transformer, byte *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); + bool subst_argument_checker(byte **arg) { return TRUE; } + Item *compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t); }; diff --git a/sql/item_func.cc b/sql/item_func.cc index e699669dcc5..acd6ec19470 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -234,22 +234,21 @@ void Item_func::traverse_cond(Cond_traverser traverser, } - /* Transform an Item_func object with a transformer callback function SYNOPSIS transform() - transformer the transformer callback function to be applied to the nodes - of the tree of the object - argument parameter to be passed to the transformer + transformer the transformer callback function to be applied to the nodes + of the tree of the object + argument parameter to be passed to the transformer DESCRIPTION - The function recursively applies the transform method with the - same transformer to each argument the function. - If the call of the method for a member item returns a new item + The function recursively applies the transform method to each + argument of the Item_func node. + If the call of the method for an argument item returns a new item the old item is substituted for a new one. - After this the transform method is applied to the root node + After this the transformer is applied to the root node of the Item_func object. RETURN VALUES @@ -274,6 +273,55 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument) } +/* + Compile Item_func object with a processor and a transformer callback functions + + SYNOPSIS + compile() + analyzer the analyzer callback function to be applied to the nodes + of the tree of the object + arg_p in/out parameter to be passed to the processor + transformer the transformer callback function to be applied to the nodes + of the tree of the object + arg_t parameter to be passed to the transformer + + DESCRIPTION + First the function applies the analyzer to the root node of + the Item_func object. Then if the analizer succeeeds (returns TRUE) + the function recursively applies the compile method to each argument + of the Item_func node. + If the call of the method for an argument item returns a new item + the old item is substituted for a new one. + After this the transformer is applied to the root node + of the Item_func object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t) +{ + if (!(this->*analyzer)(arg_p)) + return 0; + byte *arg_v= *arg_p; + if (arg_count) + { + Item **arg,**arg_end; + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + /* + The same parameter value of arg_p must be passed + to analyze any argument of the condition formula. + */ + Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t); + if (new_item && *arg != new_item) + current_thd->change_item_tree(arg, new_item); + } + } + return (this->*transformer)(arg_t); +} + /* See comments in Item_cmp_func::split_sum_func() */ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array, diff --git a/sql/item_func.h b/sql/item_func.h index c15b0b854b0..0d09bac4e86 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -187,6 +187,8 @@ public: } bool walk(Item_processor processor, byte *arg); Item *transform(Item_transformer transformer, byte *arg); + Item* compile(Item_analyzer analyzer, byte **arg_p, + Item_transformer transformer, byte *arg_t); void traverse_cond(Cond_traverser traverser, void * arg, traverse_order order); bool is_expensive_processor(byte *arg); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 47eb19364ee..3e5e1d94445 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6593,8 +6593,8 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) SYNOPSIS build_equal_items_for_cond() - cond condition(expression) where to make replacement - inherited path to all inherited multiple equality items + cond condition(expression) where to make replacement + inherited path to all inherited multiple equality items DESCRIPTION At each 'and' level the function detects items for equality predicates @@ -6608,7 +6608,9 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal) The function also traverses the cond tree and and for each field reference sets a pointer to the multiple equality item containing the field, if there is any. If this multiple equality equates fields to a constant the - function replace the field reference by the constant. + function replaces the field reference by the constant in the cases + when the field is not of a string type or when the field reference is + just an argument of a comparison predicate. The function also determines the maximum number of members in equality lists of each Item_cond_and object assigning it to cond_equal->max_members of this object and updating accordingly @@ -6756,9 +6758,14 @@ static COND *build_equal_items_for_cond(COND *cond, /* For each field reference in cond, not from equal item predicates, set a pointer to the multiple equality it belongs to (if there is any) + as soon the field is not of a string type or the field reference is + an argument of a comparison predicate. */ - cond= cond->transform(&Item::equal_fields_propagator, - (byte *) inherited); + byte *dummy; + cond= cond->compile(&Item::subst_argument_checker, + &dummy, + &Item::equal_fields_propagator, + (byte *) inherited); cond->update_used_tables(); } return cond; From 0f8069bfb0b449f6ceeafb0aa6a9fde1427e3099 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Fri, 8 Sep 2006 13:10:14 +0300 Subject: [PATCH 10/11] Bug #21772: can not name a column 'upgrade' when create a table in version 5.0.24 Upgrade was a reserved word. Unreserve UPGRADE so it can be used in unquoted identifiers. --- mysql-test/r/create.result | 2 ++ mysql-test/t/create.test | 6 ++++++ sql/sql_yacc.yy | 1 + 3 files changed, 9 insertions(+) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 3f8083a0e20..5f885ad199b 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -773,3 +773,5 @@ Warnings: Warning 1071 Specified key was too long; max key length is 765 bytes insert into t1 values('aaa'); drop table t1; +create table t1 (upgrade int); +drop table t1; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 07edbf206fe..db73782fdbf 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -674,4 +674,10 @@ create table t1(f1 varchar(800) binary not null, key(f1)) engine = innodb insert into t1 values('aaa'); drop table t1; +# +# Bug#21772: can not name a column 'upgrade' when create a table +# +create table t1 (upgrade int); +drop table t1; + # End of 5.0 tests diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aee810e75ad..97f6cdd27e4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7555,6 +7555,7 @@ keyword: | TRUNCATE_SYM {} | UNICODE_SYM {} | XA_SYM {} + | UPGRADE_SYM {} ; /* From 9c15b7e0e8648bb774589cfe27790cb3075ec5d8 Mon Sep 17 00:00:00 2001 From: "igor@rurik.mysql.com" <> Date: Sat, 9 Sep 2006 09:43:09 -0700 Subject: [PATCH 11/11] Post-pushbuild corrections for fix of bug #21698. --- mysql-test/r/func_str.result | 4 ++-- mysql-test/t/func_str.test | 4 ++-- sql/item.h | 27 +++++++++++++++++++++++++++ sql/item_cmpfunc.cc | 2 +- sql/item_func.cc | 2 +- sql/sql_select.cc | 4 ++-- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 94fb570381a..00642e1a570 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1136,12 +1136,12 @@ ALTER TABLE t1 ADD INDEX (code); CREATE TABLE t2 (id varchar(10) PRIMARY KEY); INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14'); SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id -WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5); +WHERE t2.id='a12' AND (LENGTH(code)=5 OR code < 'a00'); code id A12 a12 EXPLAIN EXTENDED SELECT * FROM t1 INNER JOIN t2 ON code=id -WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5); +WHERE id='a12' AND (LENGTH(code)=5 OR code < 'a00'); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref code code 13 const 3 Using where; Using index 1 SIMPLE t2 ref PRIMARY PRIMARY 12 const 1 Using where; Using index diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 415c6d2a44e..45415882ac7 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -773,10 +773,10 @@ CREATE TABLE t2 (id varchar(10) PRIMARY KEY); INSERT INTO t2 VALUES ('a11'), ('a12'), ('a13'), ('a14'); SELECT * FROM t1 INNER JOIN t2 ON t1.code=t2.id - WHERE t2.id='a12' AND (code < 'a00' OR LENGTH(code)=5); + WHERE t2.id='a12' AND (LENGTH(code)=5 OR code < 'a00'); EXPLAIN EXTENDED SELECT * FROM t1 INNER JOIN t2 ON code=id - WHERE id='a12' AND (code < 'a00' OR LENGTH(code)=5); + WHERE id='a12' AND (LENGTH(code)=5 OR code < 'a00'); DROP TABLE t1,t2; diff --git a/sql/item.h b/sql/item.h index f6145d6e20c..cdd0f863aea 100644 --- a/sql/item.h +++ b/sql/item.h @@ -411,6 +411,17 @@ public: typedef bool (Item::*Item_processor) (byte *arg); +/* + Analyzer function + SYNOPSIS + argp in/out IN: Analysis parameter + OUT: Parameter to be passed to the transformer + + RETURN + TRUE Invoke the transformer + FALSE Don't do it + +*/ typedef bool (Item::*Item_analyzer) (byte **argp); typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); @@ -740,6 +751,22 @@ public: return (this->*transformer)(arg); } + /* + This function performs a generic "compilation" of the Item tree. + The process of compilation is assumed to go as follows: + + compile() + { + if (this->*some_analyzer(...)) + { + compile children if any; + this->*some_transformer(...); + } + } + + i.e. analysis is performed top-down while transformation is done + bottom-up. + */ virtual Item* compile(Item_analyzer analyzer, byte **arg_p, Item_transformer transformer, byte *arg_t) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index fa01ae65b0d..3694c01d0aa 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2811,7 +2811,6 @@ Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, if (!(this->*analyzer)(arg_p)) return 0; - byte *arg_v= *arg_p; List_iterator li(list); Item *item; while ((item= li++)) @@ -2820,6 +2819,7 @@ Item *Item_cond::compile(Item_analyzer analyzer, byte **arg_p, The same parameter value of arg_p must be passed to analyze any argument of the condition formula. */ + byte *arg_v= *arg_p; Item *new_item= item->compile(analyzer, &arg_v, transformer, arg_t); if (new_item && new_item != item) li.replace(new_item); diff --git a/sql/item_func.cc b/sql/item_func.cc index acd6ec19470..c5588e43b49 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -304,7 +304,6 @@ Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p, { if (!(this->*analyzer)(arg_p)) return 0; - byte *arg_v= *arg_p; if (arg_count) { Item **arg,**arg_end; @@ -314,6 +313,7 @@ Item *Item_func::compile(Item_analyzer analyzer, byte **arg_p, The same parameter value of arg_p must be passed to analyze any argument of the condition formula. */ + byte *arg_v= *arg_p; Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t); if (new_item && *arg != new_item) current_thd->change_item_tree(arg, new_item); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4d2b3cac254..8ba11195c87 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6919,9 +6919,9 @@ static COND *build_equal_items_for_cond(COND *cond, as soon the field is not of a string type or the field reference is an argument of a comparison predicate. */ - byte *dummy; + byte *is_subst_valid= (byte *) 1; cond= cond->compile(&Item::subst_argument_checker, - &dummy, + &is_subst_valid, &Item::equal_fields_propagator, (byte *) inherited); cond->update_used_tables();