From 1b8ecd274e2a3f20b45c8b96831758757582f4fd Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 19 Apr 2007 03:04:23 +0400 Subject: [PATCH 01/17] BUG#27939: Early NULLs filtering doesn't work for eq_ref access - Turn it on for JT_EQ_REF access method mysql-test/r/join.result: BUG#27939: Testcase mysql-test/t/join.test: BUG#27939: Testcase --- mysql-test/r/bdb_notembedded.result | 35 ++++++++++++++++++++++++++ mysql-test/r/join.result | 28 +++++++++++++++++++++ mysql-test/t/bdb_notembedded.test | 38 +++++++++++++++++++++++++++++ mysql-test/t/join.test | 23 +++++++++++++++++ sql/sql_select.cc | 5 ++-- 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index af2d4bed592..c785c3be379 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -779,4 +779,32 @@ Field Type Null Key Default Extra Name varchar(50) YES NULL DROP VIEW v1; DROP TABLE t1,t2,tv1,tv2; +create table t1 (a int, b int); +insert into t1 values +(NULL, 1), +(NULL, 2), +(NULL, 3), +(NULL, 4); +create table t2 (a int not null, primary key(a)); +insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t3 (a int not null, primary key(a)); +insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +flush status; +select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b; +a b a a +explain select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using index +1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index +We expect rnd_next=5, and read_key must be 0 because of short-cutting: +show status like 'Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 5 +drop table t1, t2, t3; End of 5.0 tests. diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index 99dd21e8ee2..a0fc7059179 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -610,4 +610,27 @@ DESCRIBE tv2; DROP VIEW v1; DROP TABLE t1,t2,tv1,tv2; + +# BUG#27939: Early NULLs filtering doesn't work for eq_ref access +create table t1 (a int, b int); +insert into t1 values + (NULL, 1), + (NULL, 2), + (NULL, 3), + (NULL, 4); + +create table t2 (a int not null, primary key(a)); +insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t3 (a int not null, primary key(a)); +insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +flush status; +select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b; +explain select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b; +--echo We expect rnd_next=5, and read_key must be 0 because of short-cutting: +show status like 'Handler_read%'; +drop table t1, t2, t3; + + --echo End of 5.0 tests. diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dc018dd43bb..36be506dfa5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5444,8 +5444,9 @@ static void add_not_null_conds(JOIN *join) for (uint i=join->const_tables ; i < join->tables ; i++) { JOIN_TAB *tab=join->join_tab+i; - if ((tab->type == JT_REF || tab->type == JT_REF_OR_NULL) && - !tab->table->maybe_null) + if ((tab->type == JT_REF || tab->type == JT_EQ_REF || + tab->type == JT_REF_OR_NULL) && + !tab->table->maybe_null) { for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++) { From 583d8a0a944b6377d79b1b7afe997b058cfcb78e Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 22 Apr 2007 12:57:32 +0500 Subject: [PATCH 02/17] rpl_udf test enabled --- mysql-test/t/disabled.def | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index ad2c7a6c08c..d1f8db97131 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -28,7 +28,6 @@ rpl_ddl : BUG#26418 2007-03-01 mleich Slave out of sync after C rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly -rpl_udf : BUG#27564 2007-03-31 lars New test case for rpl of UDF shows valgrind failure synchronization : Bug#24529 Test 'synchronization' fails on Mac pushbuild; Also on Linux 64 bit. # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open From ae5865ff430cc6939f5e94abc1645e27899ab823 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 23 Apr 2007 14:16:49 +0300 Subject: [PATCH 03/17] - addendum of the fix for bug 27786: applied the new function is_union() to places in the code that check the same condition. - 5.0->5.1 merge fixes mysql-test/r/subselect3.result: merge 5.0->5.1 : updated explain. sql/item_subselect.cc: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. sql/sql_derived.cc: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. sql/sql_parse.cc: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. sql/sql_select.cc: addendum of the fix for bug 27786: applied the new function is_union() to places in the code that check the same condition. sql/sql_union.cc: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. sql/sql_view.cc: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. sql/sql_yacc.yy: addendum of the fix for bug 27786: applied the new function is_union() to the applicable places. --- mysql-test/r/subselect3.result | 8 ++++---- sql/item_subselect.cc | 6 +++--- sql/sql_derived.cc | 4 +--- sql/sql_parse.cc | 2 +- sql/sql_select.cc | 11 ++++++----- sql/sql_union.cc | 19 +++++++++---------- sql/sql_view.cc | 2 +- sql/sql_yacc.yy | 2 +- 8 files changed, 26 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 981f59e9787..657d95b7ee3 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -698,10 +698,10 @@ INSERT INTO t1 VALUES (1), (NULL), (4); INSERT INTO t2 VALUES (3), (1),(2), (5), (4), (7), (6); EXPLAIN EXTENDED SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a 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 -1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index -2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where +1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 100.00 Using index +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 Using where Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`b` = `test`.`t1`.`a`) and (not((`test`.`t1`.`a`,(select 1 AS `Not_used` from `test`.`t1` where (((`test`.`t2`.`b`) = `test`.`t1`.`a`) or isnull(`test`.`t1`.`a`)) having (`test`.`t1`.`a`)))))) SELECT a FROM t1, t2 WHERE a=b AND (b NOT IN (SELECT a FROM t1)); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 79b09967a9d..812d3c222c0 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -82,7 +82,7 @@ void Item_subselect::init(st_select_lex *select_lex, parsing_place= (outer_select->in_sum_expr ? NO_MATTER : outer_select->parsing_place); - if (select_lex->next_select()) + if (unit->is_union()) engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); @@ -412,7 +412,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) SELECT_LEX *select_lex= join->select_lex; Query_arena *arena= thd->stmt_arena; - if (!select_lex->master_unit()->first_select()->next_select() && + if (!select_lex->master_unit()->is_union() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && !select_lex->item_list.head()->with_sum_func && @@ -1147,7 +1147,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, else { bool tmp; - if (select_lex->master_unit()->first_select()->next_select()) + if (select_lex->master_unit()->is_union()) { /* comparison functions can't be changed during fix_fields() diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 89bd7958c86..ea7545fe5cb 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -236,9 +236,7 @@ bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list) SELECT_LEX *first_select= unit->first_select(); select_union *derived_result= orig_table_list->derived_result; SELECT_LEX *save_current_select= lex->current_select; - bool is_union= first_select->next_select() && - first_select->next_select()->linkage == UNION_TYPE; - if (is_union) + if (unit->is_union()) { // execute union without clean up res= unit->exec(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9d02053ec00..31cf9e18959 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5893,7 +5893,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) fake_select_lex->context.resolve_in_select_list= TRUE; fake_select_lex->context.select_lex= fake_select_lex; - if (!first_sl->next_select()) + if (!is_union()) { /* This works only for diff --git a/sql/sql_select.cc b/sql/sql_select.cc index da2c8e96b0f..14423c949e5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -231,7 +231,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, register SELECT_LEX *select_lex = &lex->select_lex; DBUG_ENTER("handle_select"); - if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex) + if (select_lex->master_unit()->is_union() || + select_lex->master_unit()->fake_select_lex) res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option); else { @@ -442,7 +443,7 @@ JOIN::prepare(Item ***rref_pointer_array, select_lex= select_lex_arg; select_lex->join= this; join_list= &select_lex->top_join_list; - union_part= (unit_arg->first_select()->next_select() != 0); + union_part= unit_arg->is_union(); thd->lex->current_select->is_item_list_lookup= 1; /* @@ -1191,7 +1192,7 @@ JOIN::optimize() if (!group_list && !order && unit->item && unit->item->substype() == Item_subselect::IN_SUBS && tables == 1 && conds && - !unit->first_select()->next_select()) + !unit->is_union()) { if (!having) { @@ -3165,7 +3166,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, if (!join->group_list && !join->order && join->unit->item && join->unit->item->substype() == Item_subselect::IN_SUBS && - !join->unit->first_select()->next_select()) + !join->unit->is_union()) { KEY_FIELD *save= *key_fields; add_key_fields(join, key_fields, and_level, cond_arg, usable_tables, @@ -15523,7 +15524,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) "UNION"))); sl->options|= SELECT_DESCRIBE; } - if (first->next_select()) + if (unit->is_union()) { unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization unit->fake_select_lex->type= "UNION RESULT"; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index bc81679a7fd..2a1db422058 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -165,7 +165,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT_LEX *lex_select_save= thd_arg->lex->current_select; SELECT_LEX *sl, *first_sl= first_select(); select_result *tmp_result; - bool is_union; + bool is_union_select; TABLE *empty_table= 0; DBUG_ENTER("st_select_lex_unit::prepare"); @@ -203,11 +203,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; - is_union= first_sl->next_select() || fake_select_lex; + is_union_select= is_union() || fake_select_lex; /* Global option */ - if (is_union) + if (is_union_select) { if (!(tmp_result= union_result= new select_union)) goto err; @@ -238,7 +238,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl; - can_skip_order_by= is_union && !(sl->braces && sl->explicit_limit); + can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit); saved_error= join->prepare(&sl->ref_pointer_array, (TABLE_LIST*) sl->table_list.first, @@ -251,7 +251,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, (ORDER*) 0 : (ORDER *)sl->order_list.first, (ORDER*) sl->group_list.first, sl->having, - (is_union ? (ORDER*) 0 : + (is_union_select ? (ORDER*) 0 : (ORDER*) thd_arg->lex->proc_list.first), sl, this); /* There are no * in the statement anymore (for PS) */ @@ -264,7 +264,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, Use items list of underlaid select for derived tables to preserve information about fields lengths and exact types */ - if (!is_union) + if (!is_union_select) types= first_sl->item_list; else if (sl == first_sl) { @@ -307,7 +307,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } - if (is_union) + if (is_union_select) { /* Check that it was possible to aggregate @@ -639,7 +639,7 @@ void st_select_lex_unit::reinit_exec_mechanism() { prepared= optimized= executed= 0; #ifndef DBUG_OFF - if (first_select()->next_select()) + if (is_union()) { List_iterator_fast it(item_list); Item *field; @@ -706,7 +706,6 @@ bool st_select_lex_unit::change_result(select_subselect *new_result, List *st_select_lex_unit::get_unit_column_types() { SELECT_LEX *sl= first_select(); - bool is_union= test(sl->next_select()); bool is_procedure= test(sl->join->procedure); if (is_procedure) @@ -717,7 +716,7 @@ List *st_select_lex_unit::get_unit_column_types() } - if (is_union) + if (is_union()) { DBUG_ASSERT(prepared); /* Types are generated during prepare */ diff --git a/sql/sql_view.cc b/sql/sql_view.cc index ee99a2974f4..f796a05924a 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -818,7 +818,7 @@ loop_out: UNION */ if (view->updatable_view && - !lex->select_lex.next_select() && + !lex->select_lex.master_unit()->is_union() && !((TABLE_LIST*)lex->select_lex.table_list.first)->next_local && find_table_in_global_list(lex->query_tables->next_global, lex->query_tables->db, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0c7d2fc2187..87cd33117dc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7817,7 +7817,7 @@ order_clause: yet. */ SELECT_LEX *first_sl= unit->first_select(); - if (!first_sl->next_select() && + if (!unit->is_union() && (first_sl->order_list.elements || first_sl->select_limit) && unit->add_fake_select_lex(lex->thd)) From b570cee1152b61422b0e0bd3035194cdbb21ce5d Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 23 Apr 2007 17:15:51 +0300 Subject: [PATCH 04/17] Bug #27811: FORCE_INIT_OF_VARS was not defined for the debug builds on Windows. This caused LINT_INIT macro to be defined as NOP and this triggers false alarms about use of uninitialized with the runtime libs of some Visual Studio versions. Fixed by defining FORCE_INIT_OF_VARS to match the state of the Windows CMakeLists.txt: Bug #27811: added the missing build option mysql-test/r/windows.result: Bug #27811: test case mysql-test/t/windows.test: Bug #27811: test case --- CMakeLists.txt | 4 ++++ mysql-test/r/windows.result | 7 +++++++ mysql-test/t/windows.test | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a286071bb0..b93b3e0db27 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ ADD_DEFINITIONS(-D WITH_MYISAM_STORAGE_ENGINE) ADD_DEFINITIONS(-D CMAKE_BUILD) ADD_DEFINITIONS(-D HAVE_YASSL) +# Set debug options +SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DFORCE_INIT_OF_VARS") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DFORCE_INIT_OF_VARS") + SET (mysql_plugin_defs "${mysql_plugin_defs},builtin_myisam_plugin") diff --git a/mysql-test/r/windows.result b/mysql-test/r/windows.result index 1702fd28c18..b5f9a48d805 100644 --- a/mysql-test/r/windows.result +++ b/mysql-test/r/windows.result @@ -12,3 +12,10 @@ Warnings: Warning 0 DATA DIRECTORY option ignored Warning 0 INDEX DIRECTORY option ignored drop table t1; +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (1,1); +EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used +End of 5.0 tests. diff --git a/mysql-test/t/windows.test b/mysql-test/t/windows.test index b5377a9b9b0..6976ee98750 100644 --- a/mysql-test/t/windows.test +++ b/mysql-test/t/windows.test @@ -27,3 +27,11 @@ CREATE TABLE t1 ( `ID` int(6) ) data directory 'c:/tmp/' index directory 'c:/tmp drop table t1; # End of 4.1 tests + +# +# Bug #27811: The variable 'join_tab' is being used without being defined +# +CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (1,1); +EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2)); + +--echo End of 5.0 tests. From 3ffdeef04da6268a1adb82018dcd5ea402fbefaa Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 24 Apr 2007 22:35:57 +0400 Subject: [PATCH 05/17] Bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. When fields are inserted instead of * in the select list they were not marked for check for the ONLY_FULL_GROUP_BY mode. The Field_iterator_table::create_item() function now marks newly created items for check when in the ONLY_FULL_GROUP_BY mode. The setup_wild() and the insert_fields() functions now maintain the cur_pos_in_select_list counter for the ONLY_FULL_GROUP_BY mode. sql/sql_base.cc: Bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. The setup_wild() and the insert_fields() functions now maintain the cur_pos_in_select_list counter for the ONLY_FULL_GROUP_BY mode. sql/table.cc: Bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. The Field_iterator_table::create_item() function now marks newly created items for check when in the ONLY_FULL_GROUP_BY mode. mysql-test/r/group_by.result: Added a test case for the bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. mysql-test/t/group_by.test: Added a test case for the bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. --- mysql-test/r/group_by.result | 12 ++++++++++++ mysql-test/t/group_by.test | 14 ++++++++++++++ sql/sql_base.cc | 5 +++++ sql/table.cc | 11 ++++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 97375898f41..88f635595d6 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -1035,3 +1035,15 @@ HAVING SUM(t1_inner.b)+t1_outer.b > 5); ERROR 42000: 'test.t1_outer.b' isn't in GROUP BY DROP TABLE t1; SET SQL_MODE = ''; +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; +create table t1(f1 int, f2 int); +select * from t1 group by f1; +ERROR 42000: 'test.t1.f2' isn't in GROUP BY +select * from t1 group by f2; +ERROR 42000: 'test.t1.f1' isn't in GROUP BY +select * from t1 group by f1, f2; +f1 f2 +select t1.f1,t.* from t1, t1 t group by 1; +ERROR 42000: 'test.t.f1' isn't in GROUP BY +drop table t1; +SET SQL_MODE = ''; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 92c92bf3957..538c5cd897d 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -752,3 +752,17 @@ SELECT b FROM t1 AS t1_outer GROUP BY a HAVING t1_outer.a IN HAVING SUM(t1_inner.b)+t1_outer.b > 5); DROP TABLE t1; SET SQL_MODE = ''; +# +# Bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode. +# +SET SQL_MODE = 'ONLY_FULL_GROUP_BY'; +create table t1(f1 int, f2 int); +--error 1055 +select * from t1 group by f1; +--error 1055 +select * from t1 group by f2; +select * from t1 group by f1, f2; +--error 1055 +select t1.f1,t.* from t1, t1 t group by 1; +drop table t1; +SET SQL_MODE = ''; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7ebdc90f8b3..6e6611d54d2 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4588,6 +4588,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, */ arena= thd->activate_stmt_arena_if_needed(&backup); + thd->lex->current_select->cur_pos_in_select_list= 0; while (wild_num && (item= it++)) { if (item->type() == Item::FIELD_ITEM && @@ -4629,7 +4630,10 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, } wild_num--; } + else + thd->lex->current_select->cur_pos_in_select_list++; } + thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; if (arena) { /* make * substituting permanent */ @@ -5134,6 +5138,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, item->walk(&Item::reset_query_id_processor, (byte *)(&thd->query_id)); } + thd->lex->current_select->cur_pos_in_select_list++; } /* In case of stored tables, all fields are considered as used, diff --git a/sql/table.cc b/sql/table.cc index 6d13514e782..fb2012a79d1 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2670,7 +2670,16 @@ const char *Field_iterator_table::name() Item *Field_iterator_table::create_item(THD *thd) { - return new Item_field(thd, &thd->lex->current_select->context, *ptr); + SELECT_LEX *select= thd->lex->current_select; + + Item_field *item= new Item_field(thd, &select->context, *ptr); + if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && + !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS) + { + select->non_agg_fields.push_back(item); + item->marker= select->cur_pos_in_select_list; + } + return item; } From 8f374c8f34221467061feca44fd67898dbb21701 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 25 Apr 2007 17:15:05 +0500 Subject: [PATCH 06/17] Bug#27047 INFORMATION_SCHEMA table cannot have BIGINT fields added support for DATE, TIME, BLOB, FLOAT and all *INT variants in I_S tables sql/item.h: added support for DATE, TIME, BLOB, FLOAT and all *INT variants in I_S tables sql/sp.cc: added support for DATE, TIME, BLOB, FLOAT and all *INT variants in I_S tables sql/sql_show.cc: added support for DATE, TIME, BLOB, FLOAT and all *INT variants in I_S tables sql/table.h: added MY_I_S_MAYBE_NULL & MY_I_S_UNSIGNED flags which are used as field atributes for I_S schema table fields --- sql/item.h | 44 +++++++--- sql/sp.cc | 4 +- sql/sql_show.cc | 229 +++++++++++++++++++++++++++++------------------- sql/table.h | 6 +- 4 files changed, 179 insertions(+), 104 deletions(-) diff --git a/sql/item.h b/sql/item.h index 3d20aaf66cd..747d79afda9 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1840,33 +1840,57 @@ public: /* for show tables */ - -class Item_datetime :public Item_string +class Item_partition_func_safe_string: public Item_string { public: - Item_datetime(const char *item_name): Item_string(item_name,"",0, - &my_charset_bin) - { max_length=19;} - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + Item_partition_func_safe_string(const char *name, uint length, + CHARSET_INFO *cs= NULL): + Item_string(name, length, cs) + {} bool check_partition_func_processor(byte *int_arg) {return TRUE;} }; -class Item_empty_string :public Item_string + +class Item_return_date_time :public Item_partition_func_safe_string +{ + enum_field_types date_time_field_type; +public: + Item_return_date_time(const char *name_arg, enum_field_types field_type_arg) + :Item_partition_func_safe_string(name_arg, 0, &my_charset_bin), + date_time_field_type(field_type_arg) + { } + enum_field_types field_type() const { return date_time_field_type; } +}; + + +class Item_blob :public Item_partition_func_safe_string +{ +public: + Item_blob(const char *name, uint length) : + Item_partition_func_safe_string(name, length, &my_charset_bin) + { max_length= length; } + enum Type type() const { return TYPE_HOLDER; } + enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } +}; + + +class Item_empty_string :public Item_partition_func_safe_string { public: Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) : - Item_string("",0, cs ? cs : &my_charset_bin) + Item_partition_func_safe_string("",0, cs ? cs : &my_charset_bin) { name=(char*) header; max_length= cs ? length * cs->mbmaxlen : length; } void make_field(Send_field *field); }; + class Item_return_int :public Item_int { enum_field_types int_field_type; public: Item_return_int(const char *name_arg, uint length, - enum_field_types field_type_arg) - :Item_int(name_arg, 0, length), int_field_type(field_type_arg) + enum_field_types field_type_arg, longlong value= 0) + :Item_int(name_arg, value, length), int_field_type(field_type_arg) { unsigned_flag=1; } diff --git a/sql/sp.cc b/sql/sp.cc index 49b8b304e76..d96e4295a83 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -784,7 +784,9 @@ db_show_routine_status(THD *thd, int type, const char *wild) { switch (used_field->field_type) { case MYSQL_TYPE_TIMESTAMP: - field_list.push_back(item=new Item_datetime(used_field->field_name)); + field_list.push_back(item= + new Item_return_date_time(used_field->field_name, + MYSQL_TYPE_DATETIME)); break; default: field_list.push_back(item=new Item_empty_string(used_field->field_name, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b28077300f3..e4e0fb22a5e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4704,16 +4704,26 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) for (; fields_info->field_name; fields_info++) { switch (fields_info->field_type) { + case MYSQL_TYPE_TINY: case MYSQL_TYPE_LONG: - if (!(item= new Item_int(fields_info->field_name, - fields_info->value, - fields_info->field_length))) + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + if (!(item= new Item_return_int(fields_info->field_name, + fields_info->field_length, + fields_info->field_type, + fields_info->value))) { DBUG_RETURN(0); } + item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED); break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIMESTAMP: - if (!(item=new Item_datetime(fields_info->field_name))) + case MYSQL_TYPE_DATETIME: + if (!(item=new Item_return_date_time(fields_info->field_name, + fields_info->field_type))) { DBUG_RETURN(0); } @@ -4729,7 +4739,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) { DBUG_RETURN(0); } - item->unsigned_flag= (fields_info->field_length/10000)%10; + item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED); item->decimals= fields_info->field_length%10; item->max_length= (fields_info->field_length/100)%100; if (item->unsigned_flag == 0) @@ -4739,6 +4749,16 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) item->set_name(fields_info->field_name, strlen(fields_info->field_name), cs); break; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + if (!(item= new Item_blob(fields_info->field_name, + fields_info->field_length))) + { + DBUG_RETURN(0); + } + break; default: /* Don't let unimplemented types pass through. Could be a grave error. */ DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING); @@ -4754,7 +4774,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) break; } field_list.push_back(item); - item->maybe_null= fields_info->maybe_null; + item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL); field_count++; } TMP_TABLE_PARAM *tmp_table_param = @@ -5374,25 +5394,29 @@ ST_FIELD_INFO tables_fields_info[]= {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"}, {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine"}, - {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Version"}, + {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"}, {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"}, - {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Rows"}, - {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - "Avg_row_length"}, - {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - "Data_length"}, - {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - "Max_data_length"}, - {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - "Index_length"}, - {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Data_free"}, - {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - "Auto_increment"}, - {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"}, - {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"}, - {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"}, + {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"}, + {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"}, + {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"}, + {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"}, + {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"}, + {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"}, + {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment"}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"}, {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, - {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Checksum"}, + {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"}, {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"}, {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} @@ -5405,15 +5429,20 @@ ST_FIELD_INFO columns_fields_info[]= {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"}, - {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 0, 0}, - {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, "Default"}, + {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + MY_I_S_UNSIGNED, 0}, + {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, + 1, "Default"}, {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, - 0}, - {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0}, - {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0}, - {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0}, + {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, + 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0}, {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"}, @@ -5430,7 +5459,7 @@ ST_FIELD_INFO charsets_fields_info[]= {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"}, - {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"}, + {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5439,10 +5468,10 @@ ST_FIELD_INFO collation_fields_info[]= { {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"}, {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, - {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Id"}, + {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id"}, {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"}, {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"}, - {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"}, + {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5469,19 +5498,19 @@ ST_FIELD_INFO events_fields_info[]= {"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0}, {"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"}, - {"EXECUTE_AT", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Execute at"}, + {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at"}, {"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value"}, {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field"}, {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, - {"STARTS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Starts"}, - {"ENDS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Ends"}, + {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts"}, + {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends"}, {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status"}, {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, - {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, - {"LAST_EXECUTED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, + {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0}, + {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ORIGINATOR", 10, MYSQL_TYPE_LONG, 0, 0, "Originator"}, + {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5512,8 +5541,8 @@ ST_FIELD_INFO proc_fields_info[]= {"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"}, - {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Created"}, - {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Modified"}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created"}, + {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified"}, {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"}, {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, @@ -5526,14 +5555,15 @@ ST_FIELD_INFO stat_fields_info[]= {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"NON_UNIQUE", 1, MYSQL_TYPE_LONG, 0, 0, "Non_unique"}, + {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique"}, {"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name"}, - {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONG, 0, 0, "Seq_in_index"}, + {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index"}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name"}, {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation"}, - {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 1, "Cardinality"}, - {"SUB_PART", 3, MYSQL_TYPE_LONG, 0, 1, "Sub_part"}, + {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1, + "Cardinality"}, + {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part"}, {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed"}, {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type"}, @@ -5623,8 +5653,8 @@ ST_FIELD_INFO key_column_usage_fields_info[]= {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, - {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONG, 0, 0, 0}, - {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONG, 0, 1, 0}, + {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0}, {"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, @@ -5646,8 +5676,8 @@ ST_FIELD_INFO open_tables_fields_info[]= { {"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"}, {"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"In_use", 1, MYSQL_TYPE_LONG, 0, 0, "In_use"}, - {"Name_locked", 4, MYSQL_TYPE_LONG, 0, 0, "Name_locked"}, + {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use"}, + {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -5661,7 +5691,7 @@ ST_FIELD_INFO triggers_fields_info[]= {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"}, - {"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0}, + {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"}, {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0}, @@ -5670,7 +5700,7 @@ ST_FIELD_INFO triggers_fields_info[]= {"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"}, + {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created"}, {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"}, {"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} @@ -5684,23 +5714,27 @@ ST_FIELD_INFO partitions_fields_info[]= {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, - {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, + {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, - {"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, - {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, - {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, + {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0}, {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, @@ -5734,12 +5768,12 @@ ST_FIELD_INFO system_variables_fields_info[]= ST_FIELD_INFO processlist_fields_info[]= { - {"ID", 4, MYSQL_TYPE_LONG, 0, 0, "Id"}, + {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id"}, {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User"}, {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host"}, {"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db"}, {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command"}, - {"TIME", 7, MYSQL_TYPE_LONG, 0, 0, "Time"}, + {"TIME", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Time"}, {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State"}, {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} @@ -5763,7 +5797,7 @@ ST_FIELD_INFO plugin_fields_info[]= ST_FIELD_INFO files_fields_info[]= { - {"FILE_ID", 4, MYSQL_TYPE_LONG, 0, 0, 0}, + {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, {"FILE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0}, {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, @@ -5771,34 +5805,45 @@ ST_FIELD_INFO files_fields_info[]= {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONG, 0, 1, 0}, + {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, {"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0}, - {"DELETED_ROWS", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"UPDATE_COUNT", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"FREE_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"EXTENT_SIZE", 4, MYSQL_TYPE_LONG, 0, 0, 0}, - {"INITIAL_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0}, - {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0}, - {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0}, - {"CREATION_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0}, - {"RECOVER_TIME", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONG, 0, 1, 0}, - {"VERSION", 21 , MYSQL_TYPE_LONG, 0, 1, "Version"}, + {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {"INITIAL_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0}, + {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0}, + {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0}, + {"VERSION", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"}, {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"}, - {"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 1, "Rows"}, - {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Avg_row_length"}, - {"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_length"}, - {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Max_data_length"}, - {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Index_length"}, - {"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_free"}, - {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"}, - {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"}, - {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"}, - {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"}, + {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"}, + {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"}, + {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"}, + {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"}, + {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"}, + {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"}, + {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"}, + {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"}, + {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"}, + {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"}, {"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0}, {"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} diff --git a/sql/table.h b/sql/table.h index bb9ced2e450..5b6aa2a8a7c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -554,13 +554,17 @@ enum enum_schema_tables }; +#define MY_I_S_MAYBE_NULL 1 +#define MY_I_S_UNSIGNED 2 + + typedef struct st_field_info { const char* field_name; uint field_length; enum enum_field_types field_type; int value; - bool maybe_null; + uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.) const char* old_name; } ST_FIELD_INFO; From 209bf25a7f5105fc1329a0b31f96be0298057155 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 26 Apr 2007 02:01:23 +0500 Subject: [PATCH 07/17] Fixed bug #27650: INSERT into InnoDB table may cause "ERROR 1062 (23000): Duplicate entry..." errors or lost records after multi-row INSERT of the form: "INSERT INTO t (id...) VALUES (NULL...) ON DUPLICATE KEY UPDATE id=VALUES(id)", where "id" is an AUTO_INCREMENT column. It happens because InnoDB handler forgets to save next insert id after updating of auto_increment column with new values. As result of that last insert id stored inside InnoDB dictionary tables differs from it's cached thd->next_insert_id value. sql/ha_innodb.cc: Fixed bug #27650. INSERT into InnoDB table may cause "ERROR 1062 (23000): Duplicate entry..." errors or lost records after multi-row INSERT of the form: "INSERT INTO t (id...) VALUES (NULL...) ON DUPLICATE KEY UPDATE id=VALUES(id)", where "id" is an AUTO_INCREMENT column. It happens because InnoDB handler forgets to save next insert id after updating of auto_increment column with new values. As result of that last insert id stored inside InnoDB dictionary tables differs from it's cached thd->next_insert_id value. ha_innobase::write_row() function has been corrected. mysql-test/t/innodb_mysql.test: Added a test case for bug #27650. mysql-test/r/innodb_mysql.result: Added a test case for bug #27650. --- mysql-test/r/innodb_mysql.result | 39 ++++++++++++++++++++++++++ mysql-test/t/innodb_mysql.test | 47 ++++++++++++++++++++++++++++++++ sql/ha_innodb.cc | 2 ++ 3 files changed, 88 insertions(+) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 298fc58ffcb..88428840a82 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -446,4 +446,43 @@ a 2 5 drop table t1; +create table t1( +id int auto_increment, +c char(1) not null, +counter int not null default 1, +primary key (id), +unique key (c) +) engine=innodb; +insert into t1 (id, c) values +(NULL, 'a'), +(NULL, 'a') +on duplicate key update id = values(id), counter = counter + 1; +select * from t1; +id c counter +2 a 2 +insert into t1 (id, c) values +(NULL, 'b') +on duplicate key update id = values(id), counter = counter + 1; +select * from t1; +id c counter +2 a 2 +3 b 1 +truncate table t1; +insert into t1 (id, c) values (NULL, 'a'); +select * from t1; +id c counter +1 a 1 +insert into t1 (id, c) values (NULL, 'b'), (NULL, 'b') +on duplicate key update id = values(id), c = values(c), counter = counter + 1; +select * from t1; +id c counter +1 a 1 +3 b 2 +insert into t1 (id, c) values (NULL, 'a') +on duplicate key update id = values(id), c = values(c), counter = counter + 1; +select * from t1; +id c counter +3 b 2 +4 a 2 +drop table t1; End of 5.0 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index c351891d205..c2521656a9c 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -412,4 +412,51 @@ DROP TABLE t1; --source include/innodb_rollback_on_timeout.inc + +-- source include/have_innodb.inc + +# +# Bug #27650: INSERT fails after multi-row INSERT of the form: +# INSERT INTO t (id...) VALUES (NULL...) ON DUPLICATE KEY UPDATE id=VALUES(id) +# + +create table t1( +id int auto_increment, +c char(1) not null, +counter int not null default 1, +primary key (id), +unique key (c) +) engine=innodb; + +insert into t1 (id, c) values +(NULL, 'a'), +(NULL, 'a') +on duplicate key update id = values(id), counter = counter + 1; + +select * from t1; + +insert into t1 (id, c) values +(NULL, 'b') +on duplicate key update id = values(id), counter = counter + 1; + +select * from t1; + +truncate table t1; + +insert into t1 (id, c) values (NULL, 'a'); + +select * from t1; + +insert into t1 (id, c) values (NULL, 'b'), (NULL, 'b') +on duplicate key update id = values(id), c = values(c), counter = counter + 1; + +select * from t1; + +insert into t1 (id, c) values (NULL, 'a') +on duplicate key update id = values(id), c = values(c), counter = counter + 1; + +select * from t1; + +drop table t1; + --echo End of 5.0 tests diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 217f59d4b7e..808c3bcd41d 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -3299,6 +3299,8 @@ no_commit: if (error == DB_DUPLICATE_KEY && auto_inc_used && (user_thd->lex->sql_command == SQLCOM_REPLACE || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT + || (user_thd->lex->sql_command == SQLCOM_INSERT + && user_thd->lex->duplicates == DUP_UPDATE) || (user_thd->lex->sql_command == SQLCOM_LOAD && user_thd->lex->duplicates == DUP_REPLACE))) { From e14fd2b6516ae54a16ebd4cb0a92702fd8d4a315 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 26 Apr 2007 11:12:17 +0300 Subject: [PATCH 08/17] Bug #27363: Validity checks for nested set functions were not taking into account that the enclosed set function may be on a nest level that is lower than the nest level of the enclosing set function. Fixed by : - propagating max_sum_func_level up the enclosing set functions chain. - updating the max_sum_func_level of the enclosing set function when the enclosed set function is aggregated above or on the same nest level of as the level of the enclosing set function. - updating the max_arg_level of the enclosing set function on a reference that refers to an item above or on the same nest level as the level of the enclosing set function. - Treating both Item_field and Item_ref as possibly referencing items from outer nest levels. mysql-test/r/subselect.result: Bug #27363: test cases mysql-test/t/subselect.test: Bug #27363: test cases sql/item.cc: Bug #27363: Treat the reference as an outer reference for the enclosing set function even if it's referencing an item that is above the nest level of the enclosing set function. Consider both Item_field and Item_ref. sql/item_sum.cc: Bug #27363: Use the enclosed set function aggregation level to mark the enclosing set function even if it's aggregated on a level that is above the nest level of the enclosing set function. Pass max_sum_func_level up the accending branch of the recursion because it must take into account each directly or indirectly nested set function. --- mysql-test/r/bdb_notembedded.result | 35 ++++++++++++++++++++++++++ mysql-test/r/subselect.result | 23 +++++++++++++++++ mysql-test/t/bdb_notembedded.test | 38 +++++++++++++++++++++++++++++ mysql-test/t/subselect.test | 30 +++++++++++++++++++++++ sql/item.cc | 28 +++++++++++++++++++-- sql/item_sum.cc | 16 ++++++++++-- 6 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index d5a1c0b2451..dbeb9486c0f 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3947,3 +3947,26 @@ WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; ERROR HY000: Invalid use of group function SET @@sql_mode=default; DROP TABLE t1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 values (1),(1),(1),(1); +CREATE TABLE t2 (x INT); +INSERT INTO t1 values (1000),(1001),(1002); +SELECT SUM( (SELECT COUNT(a) FROM t2) ) FROM t1; +ERROR HY000: Invalid use of group function +SELECT SUM( (SELECT SUM(COUNT(a)) FROM t2) ) FROM t1; +ERROR HY000: Invalid use of group function +SELECT COUNT(1) FROM DUAL; +COUNT(1) +1 +SELECT SUM( (SELECT AVG( (SELECT t1.a FROM t2) ) FROM DUAL) ) FROM t1; +ERROR HY000: Invalid use of group function +SELECT +SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING t1.a < 12) ) FROM t2) ) +FROM t1; +ERROR HY000: Invalid use of group function +SELECT t1.a as XXA, +SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING XXA < 12) ) FROM t2) ) +FROM t1; +ERROR HY000: Invalid use of group function +DROP TABLE t1,t2; +End of 5.0 tests. diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 182b9b27ef7..a9f130f1870 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2809,3 +2809,33 @@ SELECT a FROM t1 t0 SET @@sql_mode=default; DROP TABLE t1; + +# +# Bug #27363: nested aggregates in outer, subquery / sum(select +# count(outer)) +# +CREATE TABLE t1 (a INT); INSERT INTO t1 values (1),(1),(1),(1); +CREATE TABLE t2 (x INT); INSERT INTO t1 values (1000),(1001),(1002); + +--error ER_INVALID_GROUP_FUNC_USE +SELECT SUM( (SELECT COUNT(a) FROM t2) ) FROM t1; +--error ER_INVALID_GROUP_FUNC_USE +SELECT SUM( (SELECT SUM(COUNT(a)) FROM t2) ) FROM t1; +SELECT COUNT(1) FROM DUAL; + +--error ER_INVALID_GROUP_FUNC_USE +SELECT SUM( (SELECT AVG( (SELECT t1.a FROM t2) ) FROM DUAL) ) FROM t1; + +--error ER_INVALID_GROUP_FUNC_USE +SELECT + SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING t1.a < 12) ) FROM t2) ) +FROM t1; + +--error ER_INVALID_GROUP_FUNC_USE +SELECT t1.a as XXA, + SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING XXA < 12) ) FROM t2) ) +FROM t1; + +DROP TABLE t1,t2; + +--echo End of 5.0 tests. diff --git a/sql/item.cc b/sql/item.cc index 8568a44c547..05067cf16dc 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3538,9 +3538,13 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) thd->restore_active_arena(arena, &backup); fixed_as_field= 1; } + /* + A reference is resolved to a nest level that's outer or the same as + the nest level of the enclosing set function : adjust the value of + max_arg_level for the function if it's needed. + */ if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level == - thd->lex->current_select->nest_level) + thd->lex->in_sum_func->nest_level >= select->nest_level) { Item::Type ref_type= (*reference)->type(); set_if_bigger(thd->lex->in_sum_func->max_arg_level, @@ -5182,6 +5186,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex, thd->lex->current_select, this, fld); + /* + A reference is resolved to a nest level that's outer or the same as + the nest level of the enclosing set function : adjust the value of + max_arg_level for the function if it's needed. + */ + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level >= + last_checked_context->select_lex->nest_level) + set_if_bigger(thd->lex->in_sum_func->max_arg_level, + last_checked_context->select_lex->nest_level); return FALSE; } if (ref == 0) @@ -5195,6 +5209,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) DBUG_ASSERT(*ref && (*ref)->fixed); mark_as_dependent(thd, last_checked_context->select_lex, context->select_lex, this, this); + /* + A reference is resolved to a nest level that's outer or the same as + the nest level of the enclosing set function : adjust the value of + max_arg_level for the function if it's needed. + */ + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level >= + last_checked_context->select_lex->nest_level) + set_if_bigger(thd->lex->in_sum_func->max_arg_level, + last_checked_context->select_lex->nest_level); } } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 359b4516c3c..ca8aa2f06e7 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -175,13 +175,25 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) MYF(0)); return TRUE; } - if (in_sum_func && in_sum_func->nest_level == nest_level) + if (in_sum_func) { /* If the set function is nested adjust the value of max_sum_func_level for the nesting set function. + We take into account only enclosed set functions that are to be + aggregated on the same level or above of the nest level of + the enclosing set function. + But we must always pass up the max_sum_func_level because it is + the maximum nested level of all directly and indirectly enclosed + set functions. We must do that even for set functions that are + aggregated inside of their enclosing set function's nest level + because the enclosing function may contain another enclosing + function that is to be aggregated outside or on the same level + as its parent's nest level. */ - set_if_bigger(in_sum_func->max_sum_func_level, aggr_level); + if (in_sum_func->nest_level >= aggr_level) + set_if_bigger(in_sum_func->max_sum_func_level, aggr_level); + set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level); } update_used_tables(); thd->lex->in_sum_func= in_sum_func; From 7bb6a7259377239c3befdc27d39cfacc2b132c30 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Apr 2007 00:12:09 +0400 Subject: [PATCH 09/17] Bug#27590: Wrong DATE/DATETIME comparison. DATE and DATETIME can be compared either as strings or as int. Both methods have their disadvantages. Strings can contain valid DATETIME value but have insignificant zeros omitted thus became non-comparable with other DATETIME strings. The comparison as int usually will require conversion from the string representation and the automatic conversion in most cases is carried out in a wrong way thus producing wrong comparison result. Another problem occurs when one tries to compare DATE field with a DATETIME constant. The constant is converted to DATE losing its precision i.e. losing time part. This fix addresses the problems described above by adding a special DATE/DATETIME comparator. The comparator correctly converts DATE/DATETIME string values to int when it's necessary, adds zero time part (00:00:00) to DATE values to compare them correctly to DATETIME values. Due to correct conversion malformed DATETIME string values are correctly compared to other DATE/DATETIME values. As of this patch a DATE value equals to DATETIME value with zero time part. For example '2001-01-01' equals to '2001-01-01 00:00:00'. The compare_datetime() function is added to the Arg_comparator class. It implements the correct comparator for DATE/DATETIME values. Two supplementary functions called get_date_from_str() and get_datetime_value() are added. The first one extracts DATE/DATETIME value from a string and the second one retrieves the correct DATE/DATETIME value from an item. The new Arg_comparator::can_compare_as_dates() function is added and used to check whether two given items can be compared by the compare_datetime() comparator. Two caching variables were added to the Arg_comparator class to speedup the DATE/DATETIME comparison. One more store() method was added to the Item_cache_int class to cache int values. The new is_datetime() function was added to the Item class. It indicates whether the item returns a DATE/DATETIME value. sql/item.cc: Bug#27590: Wrong DATE/DATETIME comparison. One more store() method was added to the Item_cache_int class to cache int values. The new is_datetime() function was added to the Item class. It indicates whether the item returns a DATE/DATETIME value. sql/item.h: Bug#27590: Wrong DATE/DATETIME comparison. One more store() method was added to the Item_cache_int class to cache int values. The new is_datetime() function was added to the Item class. It indicates whether the item returns a DATE/DATETIME value. sql/item_cmpfunc.cc: Bug#27590: Wrong DATE/DATETIME comparison. The compare_datetime() function is added to the Arg_comparator class. It implements the correct comparator for DATE/DATETIME values. Two supplementary functions called get_date_from_str() and get_datetime_value() are added. The first one extracts DATE/DATETIME value from a string and the second one retrieves the correct DATE/DATETIME value from an item. The new Arg_comparator::can_compare_as_dates() function is added and used to check whether two given items can be compared by the compare_datetime() comparator. sql/item_cmpfunc.h: Bug#27590: Wrong DATE/DATETIME comparison. The compare_datetime() function is added to the Arg_comparator class. It implements the correct comparator for DATE/DATETIME values. Two supplementary functions called get_date_from_str() and get_datetime_value() are added. The first one extracts DATE/DATETIME value from a string and the second one retrieves the correct DATE/DATETIME value from an item. The new Arg_comparator::can_compare_as_dates() function is added and used to check whether two given items can be compared by the compare_datetime() comparator. Two caching variables were added to the Arg_comparator class to speedup the DATE/DATETIME comparison. mysql-test/include/ps_conv.inc: Test case adjusted after fix for bug#27590. mysql-test/r/distinct.result: Test cases results are corrected after fix for bug#27590. sql/sql_select.cc: Bug#27590: Wrong DATE/DATETIME comparison. The test_if_equality_guarantees_uniqueness() function now uses Arg_comparator::can_compare_as_dates() to detect comparable DATE/DATETIME items. mysql-test/r/ps_2myisam.result: The result of the adjusted test case after fix for bug#27590. mysql-test/r/ps_3innodb.result: The result of the adjusted test case after fix for bug#27590. mysql-test/r/ps_4heap.result: The result of the adjusted test case after fix for bug#27590. mysql-test/r/ps_5merge.result: The result of the adjusted test case after fix for bug#27590. mysql-test/r/subselect.result: Test cases results are corrected after fix for bug#27590. mysql-test/r/type_datetime.result: Added a test case for the bug#27590: Wrong DATE/DATETIME comparison. mysql-test/t/type_datetime.test: Added a test case for the bug#27590: Wrong DATE/DATETIME comparison. tests/mysql_client_test.c: Test case adjusted after fix for bug#27590. --- mysql-test/include/ps_conv.inc | 4 +- mysql-test/r/bdb_notembedded.result | 35 +++ mysql-test/r/distinct.result | 2 +- mysql-test/r/ps_2myisam.result | 4 +- mysql-test/r/ps_3innodb.result | 4 +- mysql-test/r/ps_4heap.result | 4 +- mysql-test/r/ps_5merge.result | 8 +- mysql-test/r/subselect.result | 4 +- mysql-test/r/type_datetime.result | 39 ++++ mysql-test/t/bdb_notembedded.test | 38 ++++ mysql-test/t/type_datetime.test | 20 ++ sql/item.cc | 23 ++ sql/item.h | 3 + sql/item_cmpfunc.cc | 318 +++++++++++++++++++++++++++- sql/item_cmpfunc.h | 27 ++- sql/sql_select.cc | 18 +- tests/mysql_client_test.c | 6 +- 17 files changed, 517 insertions(+), 40 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/include/ps_conv.inc b/mysql-test/include/ps_conv.inc index 09290d760ce..195d1061664 100644 --- a/mysql-test/include/ps_conv.inc +++ b/mysql-test/include/ps_conv.inc @@ -1171,7 +1171,7 @@ execute stmt1 using @arg00, @arg00, @arg00, @arg00, @arg00 ; ######## SELECT .. WHERE column(date/time/..)=value(DATETIME/LONGBLOB) ######## set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -1180,7 +1180,7 @@ select 'true' as found from t9 where c1= 20 and c13= CAST(@arg00 AS DATE) and c14= @arg00 and c15= @arg00 and c16= @arg00 and c17= @arg00 ; prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/r/distinct.result b/mysql-test/r/distinct.result index 190e8595126..8525e0f19e4 100644 --- a/mysql-test/r/distinct.result +++ b/mysql-test/r/distinct.result @@ -633,7 +633,7 @@ EXPLAIN SELECT (SELECT DISTINCT ADDDATE(a,1) FROM t1 WHERE ADDDATE(a,1) = '2002-08-03'); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where; Using temporary +2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where CREATE TABLE t2 (a CHAR(5) CHARACTER SET latin1 COLLATE latin1_general_ci); INSERT INTO t2 VALUES (0xf6); INSERT INTO t2 VALUES ('oe'); diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result index de6e2d62763..2b2a29b2122 100644 --- a/mysql-test/r/ps_2myisam.result +++ b/mysql-test/r/ps_2myisam.result @@ -3070,7 +3070,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3083,7 +3083,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result index 1ebaafdd488..f30262ef219 100644 --- a/mysql-test/r/ps_3innodb.result +++ b/mysql-test/r/ps_3innodb.result @@ -3053,7 +3053,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3066,7 +3066,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result index 74b9326dbc1..a012e516404 100644 --- a/mysql-test/r/ps_4heap.result +++ b/mysql-test/r/ps_4heap.result @@ -3054,7 +3054,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3067,7 +3067,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result index bf80514906b..ac9c8ae55ff 100644 --- a/mysql-test/r/ps_5merge.result +++ b/mysql-test/r/ps_5merge.result @@ -2990,7 +2990,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3003,7 +3003,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -6004,7 +6004,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -6017,7 +6017,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index d5a1c0b2451..09c7b92f51b 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -394,13 +394,13 @@ EXPLAIN EXTENDED SELECT DISTINCT date FROM t1 WHERE date='2002-08-03'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL PRIMARY 43 NULL 2 Using where; Using index Warnings: -Note 1003 select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = 20020803) +Note 1003 select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = _latin1'2002-08-03') EXPLAIN EXTENDED SELECT (SELECT DISTINCT date FROM t1 WHERE date='2002-08-03'); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used 2 SUBQUERY t1 index NULL PRIMARY 43 NULL 2 Using where; Using index Warnings: -Note 1003 select (select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = 20020803)) AS `(SELECT DISTINCT date FROM t1 WHERE date='2002-08-03')` +Note 1003 select (select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = _latin1'2002-08-03')) AS `(SELECT DISTINCT date FROM t1 WHERE date='2002-08-03')` SELECT DISTINCT date FROM t1 WHERE date='2002-08-03'; date 2002-08-03 diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result index 7caa23d330d..42f97a6d53b 100644 --- a/mysql-test/r/type_datetime.result +++ b/mysql-test/r/type_datetime.result @@ -192,3 +192,42 @@ CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMA SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)); CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)) 101112.098700 +create table t1 (f1 date, f2 datetime, f3 timestamp); +insert into t1(f1) values(curdate()); +select curdate() < now(), f1 < now(), cast(f1 as date) < now() from t1; +curdate() < now() f1 < now() cast(f1 as date) < now() +1 1 1 +delete from t1; +insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01'); +insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01'); +insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01'); +insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00'); +insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01'); +select f1, f3 from t1 where f1 >= '2001-02-05 00:00:00' and f3 <= '2001-04-15'; +f1 f3 +2001-02-05 2001-02-05 01:01:01 +2001-03-10 2001-03-10 01:01:01 +2001-04-15 2001-04-15 00:00:00 +select f1, f3 from t1 where f1 >= '2001-2-5 0:0:0' and f2 <= '2001-4-15'; +f1 f3 +2001-02-05 2001-02-05 01:01:01 +2001-03-10 2001-03-10 01:01:01 +2001-04-15 2001-04-15 00:00:00 +select f1, f2 from t1 where if(1, f1, 0) >= f2; +f1 f2 +2001-02-05 2001-02-05 00:00:00 +2001-03-10 2001-03-09 01:01:01 +2001-04-15 2001-04-15 00:00:00 +select 1 from dual where cast('2001-1-1 2:3:4' as date) = cast('2001-01-01' as datetime); +1 +1 +select f1, f2, UNIX_TIMESTAMP(f2), UNIX_TIMESTAMP(f1), +f1 > f2, f1 = f2, f1 < f2 +from t1; +f1 f2 UNIX_TIMESTAMP(f2) UNIX_TIMESTAMP(f1) f1 > f2 f1 = f2 f1 < f2 +2001-01-01 2001-01-01 01:01:01 978300061 978296400 0 0 1 +2001-02-05 2001-02-05 00:00:00 981320400 981320400 0 1 0 +2001-03-10 2001-03-09 01:01:01 984088861 984171600 1 0 0 +2001-04-15 2001-04-15 00:00:00 987282000 987282000 0 1 0 +2001-05-20 2001-05-20 01:01:01 990309661 990306000 0 0 1 +drop table t1; diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/mysql-test/t/type_datetime.test b/mysql-test/t/type_datetime.test index 3d04eb85cf3..69a19f45411 100644 --- a/mysql-test/t/type_datetime.test +++ b/mysql-test/t/type_datetime.test @@ -141,3 +141,23 @@ SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) AS DECIMAL(20,6)); SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMAL(20,6)); SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6)); +# +# Bug#27590: Wrong DATE/DATETIME comparison. +# +create table t1 (f1 date, f2 datetime, f3 timestamp); +insert into t1(f1) values(curdate()); +select curdate() < now(), f1 < now(), cast(f1 as date) < now() from t1; +delete from t1; +insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01'); +insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01'); +insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01'); +insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00'); +insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01'); +select f1, f3 from t1 where f1 >= '2001-02-05 00:00:00' and f3 <= '2001-04-15'; +select f1, f3 from t1 where f1 >= '2001-2-5 0:0:0' and f2 <= '2001-4-15'; +select f1, f2 from t1 where if(1, f1, 0) >= f2; +select 1 from dual where cast('2001-1-1 2:3:4' as date) = cast('2001-01-01' as datetime); +select f1, f2, UNIX_TIMESTAMP(f2), UNIX_TIMESTAMP(f1), + f1 > f2, f1 = f2, f1 < f2 + from t1; +drop table t1; diff --git a/sql/item.cc b/sql/item.cc index 8568a44c547..c28771ef382 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4166,6 +4166,21 @@ enum_field_types Item::field_type() const } +bool Item::is_datetime() +{ + switch (field_type()) + { + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + return TRUE; + default: + break; + } + return FALSE; +} + + /* Create a field to hold a string value from an item @@ -6123,6 +6138,14 @@ void Item_cache_int::store(Item *item) } +void Item_cache_int::store(Item *item, longlong val_arg) +{ + value= val_arg; + null_value= item->null_value; + unsigned_flag= item->unsigned_flag; +} + + String *Item_cache_int::val_str(String *str) { DBUG_ASSERT(fixed == 1); diff --git a/sql/item.h b/sql/item.h index 2c4943bea6e..06f20dde8f8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -847,6 +847,7 @@ public: representation is more precise than the string one). */ virtual bool result_as_longlong() { return FALSE; } + bool is_datetime(); }; @@ -2406,11 +2407,13 @@ public: Item_cache_int(): Item_cache(), value(0) {} void store(Item *item); + void store(Item *item, longlong val_arg); double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } String* val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type() const { return INT_RESULT; } + bool result_as_longlong() { return TRUE; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index ac1adc235c3..4d258ca1e6e 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -326,7 +326,9 @@ void Item_bool_func2::fix_length_and_dec() if (arg_real_item->type() == FIELD_ITEM) { Field *field=((Item_field*) arg_real_item)->field; - if (field->can_be_compared_as_longlong()) + if (field->can_be_compared_as_longlong() && + !(arg_real_item->is_datetime() && + args[1]->result_type() == STRING_RESULT)) { if (convert_constant_item(thd, field,&args[1])) { @@ -341,7 +343,9 @@ void Item_bool_func2::fix_length_and_dec() if (arg_real_item->type() == FIELD_ITEM) { Field *field=((Item_field*) arg_real_item)->field; - if (field->can_be_compared_as_longlong()) + if (field->can_be_compared_as_longlong() && + !(arg_real_item->is_datetime() && + args[0]->result_type() == STRING_RESULT)) { if (convert_constant_item(thd, field,&args[0])) { @@ -460,6 +464,316 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) } +/* + Convert date provided in a string to the int representation. + + SYNOPSIS + get_date_from_str() + thd Thread handle + str a string to convert + warn_type type of the timestamp for issuing the warning + warn_name field name for issuing the warning + error_arg [out] TRUE if string isn't a DATETIME or clipping occur + + DESCRIPTION + Convert date provided in the string str to the int representation. + if the string contains wrong date or doesn't contain it at all + then the warning is issued and TRUE returned in the error_arg argument. + The warn_type and the warn_name arguments are used as the name and the + type of the field when issuing the warning. + + RETURN + converted value. +*/ + +static ulonglong +get_date_from_str(THD *thd, String *str, timestamp_type warn_type, + char *warn_name, bool *error_arg) +{ + ulonglong value; + int error; + MYSQL_TIME l_time; + enum_mysql_timestamp_type ret; + *error_arg= TRUE; + + ret= str_to_datetime(str->ptr(), str->length(), &l_time, + (TIME_FUZZY_DATE | MODE_INVALID_DATES | + (thd->variables.sql_mode & + (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))), + &error); + if ((ret == MYSQL_TIMESTAMP_DATETIME || ret == MYSQL_TIMESTAMP_DATE)) + { + value= TIME_to_ulonglong_datetime(&l_time); + *error_arg= FALSE; + } + + if (error || *error_arg) + { + make_truncated_value_warning(thd, str->ptr(), str->length(), warn_type, + warn_name); + *error_arg= TRUE; + } + return value; +} + + +/* + Check whether compare_datetime() can be used to compare items. + + SYNOPSIS + Arg_comparator::can_compare_as_dates() + a, b [in] items to be compared + const_value [out] converted value of the string constant, if any + + DESCRIPTION + Check several cases when the DATE/DATETIME comparator should be used. + The following cases are checked: + 1. Both a and b is a DATE/DATETIME field/function returning string or + int result. + 2. Only a or b is a DATE/DATETIME field/function returning string or + int result and the other item (b or a) is an item with string result. + If the second item is a constant one then it's checked to be + convertible to the DATE/DATETIME type. If the constant can't be + converted to a DATE/DATETIME then the compare_datetime() comparator + isn't used and the warning about wrong DATE/DATETIME value is issued. + In all other cases (date-[int|real|decimal]/[int|real|decimal]-date) + the comparison is handled by other comparators. + If the datetime comparator can be used and one the operands of the + comparison is a string constant that was successfully converted to a + DATE/DATETIME type then the result of the conversion is returned in the + const_value if it is provided. If there is no constant or + compare_datetime() isn't applicable then the *const_value remains + unchanged. + + RETURN + the found type of date comparison +*/ + +enum Arg_comparator::enum_date_cmp_type +Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) +{ + enum enum_date_cmp_type cmp_type= CMP_DATE_DFLT; + Item *str_arg= 0, *date_arg= 0; + + if (a->type() == Item::ROW_ITEM || b->type() == Item::ROW_ITEM) + return CMP_DATE_DFLT; + + if (a->is_datetime()) + { + if (b->is_datetime()) + cmp_type= CMP_DATE_WITH_DATE; + else if (b->result_type() == STRING_RESULT) + { + cmp_type= CMP_DATE_WITH_STR; + date_arg= a; + str_arg= b; + } + } + else if (b->is_datetime() && a->result_type() == STRING_RESULT) + { + cmp_type= CMP_STR_WITH_DATE; + date_arg= b; + str_arg= a; + } + + if (cmp_type != CMP_DATE_DFLT) + { + if (cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item()) + { + THD *thd= current_thd; + ulonglong value; + bool error; + String tmp, *str_val= 0; + timestamp_type t_type= (date_arg->field_type() == MYSQL_TYPE_DATE ? + MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME); + + str_val= str_arg->val_str(&tmp); + if (str_arg->null_value) + return CMP_DATE_DFLT; + value= get_date_from_str(thd, str_val, t_type, date_arg->name, &error); + if (error) + return CMP_DATE_DFLT; + if (const_value) + *const_value= value; + } + } + return cmp_type; +} + + +int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, + Item **a1, Item **a2, + Item_result type) +{ + enum enum_date_cmp_type cmp_type; + ulonglong const_value; + a= a1; + b= a2; + + if ((cmp_type= can_compare_as_dates(*a, *b, &const_value))) + { + thd= current_thd; + owner= owner_arg; + a_type= (*a)->field_type(); + b_type= (*b)->field_type(); + a_cache= 0; + b_cache= 0; + + if (cmp_type != CMP_DATE_WITH_DATE && + ((*b)->const_item() || (*a)->const_item())) + { + Item_cache_int *cache= new Item_cache_int(); + /* Mark the cache as non-const to prevent re-caching. */ + cache->set_used_tables(1); + if (!(*a)->is_datetime()) + { + cache->store((*a), const_value); + a_cache= cache; + a= (Item **)&a_cache; + } + else + { + cache->store((*b), const_value); + b_cache= cache; + b= (Item **)&b_cache; + } + } + is_nulls_eq= owner->functype() == Item_func::EQUAL_FUNC; + func= &Arg_comparator::compare_datetime; + return 0; + } + return set_compare_func(owner_arg, type); +} + + +/* + Retrieves correct DATETIME value from given item. + + SYNOPSIS + get_datetime_value() + thd thread handle + item_arg [in/out] item to retrieve DATETIME value from + cache_arg [in/out] pointer to place to store the caching item to + warn_item [in] item for issuing the conversion warning + is_null [out] TRUE <=> the item_arg is null + + DESCRIPTION + Retrieves the correct DATETIME value from given item for comparison by the + compare_datetime() function. + If item's result can be compared as longlong then its int value is used + and its string value is used otherwise. Strings are always parsed and + converted to int values by the get_date_from_str() function. + This allows us to compare correctly string dates with missed insignificant + zeros. If an item is a constant one then its value is cached and it isn't + get parsed again. An Item_cache_int object is used for caching values. It + seamlessly substitutes the original item. The cache item is marked as + non-constant to prevent re-caching it again. In order to compare + correctly DATE and DATETIME items the result of the former are treated as + a DATETIME with zero time (00:00:00). + + RETURN + obtained value +*/ + +static ulonglong +get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, + Item *warn_item, bool *is_null) +{ + ulonglong value; + String buf, *str= 0; + Item *item= **item_arg; + + if (item->result_as_longlong()) + { + value= item->val_int(); + *is_null= item->null_value; + if (item->field_type() == MYSQL_TYPE_DATE) + value*= 1000000L; + } + else + { + str= item->val_str(&buf); + *is_null= item->null_value; + } + if (*is_null) + return -1; + /* + Convert strings to the integer DATE/DATETIME representation. + Even if both dates provided in strings we can't compare them directly as + strings as there is no warranty that they are correct and do not miss + some insignificant zeros. + */ + if (str) + { + bool error; + enum_field_types f_type= warn_item->field_type(); + timestamp_type t_type= f_type == + MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME; + value= get_date_from_str(thd, str, t_type, warn_item->name, &error); + } + if (item->const_item()) + { + Item_cache_int *cache= new Item_cache_int(); + /* Mark the cache as non-const to prevent re-caching. */ + cache->set_used_tables(1); + cache->store(item, value); + *cache_arg= cache; + *item_arg= cache_arg; + } + return value; +} + +/* + Compare items values as dates. + + SYNOPSIS + Arg_comparator::compare_datetime() + + DESCRIPTION + Compare items values as DATE/DATETIME for both EQUAL_FUNC and from other + comparison functions. The correct DATETIME values are obtained + with help of the get_datetime_value() function. + + RETURN + If is_nulls_eq is TRUE: + 1 if items are equal or both are null + 0 otherwise + If is_nulls_eq is FALSE: + -1 a < b or one of items is null + 0 a == b + 1 a > b +*/ + +int Arg_comparator::compare_datetime() +{ + bool is_null= FALSE; + ulonglong a_value, b_value; + + /* Get DATE/DATETIME value of the 'a' item. */ + a_value= get_datetime_value(thd, &a, &a_cache, *b, &is_null); + if (!is_nulls_eq && is_null) + { + owner->null_value= 1; + return -1; + } + + /* Get DATE/DATETIME value of the 'b' item. */ + b_value= get_datetime_value(thd, &b, &b_cache, *a, &is_null); + if (is_null) + { + owner->null_value= is_nulls_eq ? 0 : 1; + return is_nulls_eq ? 1 : -1; + } + + owner->null_value= 0; + + /* Compare values. */ + if (is_nulls_eq) + return (a_value == b_value); + return a_value < b_value ? -1 : (a_value > b_value ? 1 : 0); +} + + int Arg_comparator::compare_string() { String *res1,*res2; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 0c4a62aaa24..ce4d470a34a 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -35,12 +35,19 @@ class Arg_comparator: public Sql_alloc Item_bool_func2 *owner; Arg_comparator *comparators; // used only for compare_row() double precision; - + /* Fields used in DATE/DATETIME comparison. */ + THD *thd; + enum_field_types a_type, b_type; // Types of a and b items + Item *a_cache, *b_cache; // Cached values of a and b items + bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC + enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, + CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; public: DTCollation cmp_collation; - Arg_comparator() {}; - Arg_comparator(Item **a1, Item **a2): a(a1), b(a2) {}; + Arg_comparator(): thd(0), a_cache(0), b_cache(0) {}; + Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0), + a_cache(0), b_cache(0) {}; int set_compare_func(Item_bool_func2 *owner, Item_result type); inline int set_compare_func(Item_bool_func2 *owner_arg) @@ -48,14 +55,10 @@ public: return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), (*b)->result_type())); } - inline int set_cmp_func(Item_bool_func2 *owner_arg, + int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, - Item_result type) - { - a= a1; - b= a2; - return set_compare_func(owner_arg, type); - } + Item_result type); + inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { @@ -83,6 +86,10 @@ public: int compare_e_row(); // compare args[0] & args[1] int compare_real_fixed(); int compare_e_real_fixed(); + int compare_datetime(); // compare args[0] & args[1] as DATETIMEs + + static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b, + ulonglong *const_val_arg); static arg_cmp_func comparator_matrix [5][2]; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 79ae4ade8ab..86dec87b9fb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8621,17 +8621,13 @@ static bool test_if_equality_guarantees_uniqueness(Item *l, Item *r) { return r->const_item() && - /* elements must be of the same result type */ - (r->result_type() == l->result_type() || - /* or dates compared to longs */ - (((l->type() == Item::FIELD_ITEM && - ((Item_field *)l)->field->can_be_compared_as_longlong()) || - (l->type() == Item::FUNC_ITEM && - ((Item_func *)l)->result_as_longlong())) && - r->result_type() == INT_RESULT)) - /* and must have the same collation if compared as strings */ - && (l->result_type() != STRING_RESULT || - l->collation.collation == r->collation.collation); + /* elements must be compared as dates */ + (Arg_comparator::can_compare_as_dates(l, r, 0) || + /* or of the same result type */ + (r->result_type() == l->result_type() && + /* and must have the same collation if compared as strings */ + (l->result_type() != STRING_RESULT || + l->collation.collation == r->collation.collation))); } /* diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index d64ec08a71d..dffd7517e78 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -8852,12 +8852,14 @@ static void test_ts() mysql_free_result(prep_res); mysql_stmt_close(stmt); - + char queries [3][60]= {"SELECT a, b, c FROM test_ts WHERE %c=?", + "SELECT a, b, c FROM test_ts WHERE %c=?", + "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS DATE)"}; for (name= 'a'; field_count--; name++) { int row_count= 0; - sprintf(query, "SELECT a, b, c FROM test_ts WHERE %c=?", name); + sprintf(query, queries[field_count], name); if (!opt_silent) fprintf(stdout, "\n %s", query); From 7216281eabfbdc4141222fbd7a70660435852366 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Apr 2007 00:40:35 +0400 Subject: [PATCH 10/17] Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. The BETWEEN function was comparing DATE/DATETIME values either as ints or as strings. Both methods have their disadvantages and may lead to a wrong result. Now BETWEEN function checks whether all of its arguments has the STRING result types and at least one of them is a DATE/DATETIME item. If so it sets up two Arg_comparator obects to compare with the compare_datetime() comparator and uses them to compare such items. Added two Arg_comparator object members and one flag to the Item_func_between class for the correct DATE/DATETIME comparison. The Item_func_between::fix_length_and_dec() function now detects whether it's used for DATE/DATETIME comparison and sets up newly added Arg_comparator objects to do this. The Item_func_between::val_int() now uses Arg_comparator objects to perform correct DATE/DATETIME comparison. The owner variable of the Arg_comparator class now can be set to NULL if the caller wants to handle NULL values by itself. Now the Item_date_add_interval::get_date() function ajusts cached_field type according to the detected type. mysql-test/t/type_datetime.test: Added a test case for the bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. mysql-test/r/type_datetime.result: Added a test case for the bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. mysql-test/r/query_cache.result: A test case result corrected after the fix for bug#16377. sql/item_timefunc.cc: Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. Now the Item_date_add_interval::get_date() function ajusts cached_field type according to the detected type. sql/item_cmpfunc.cc: Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. The Item_func_between::fix_length_and_dec() function now detects whether it's used for DATE/DATETIME comparison and sets up newly added Arg_comparator objects to do this. The Item_func_between::val_int() now uses Arg_comparators to perform correct DATE/DATETIME comparison. The owner variable of the Arg_comparator class now can be set to NULL if the caller wants to handle NULL values by itself. sql/item_cmpfunc.h: Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. Added two Arg_comparator object members and one flag to the Item_func_between class for the correct DATE/DATETIME comparison. --- mysql-test/r/query_cache.result | 12 ++--- mysql-test/r/type_datetime.result | 35 ++++++++++++ mysql-test/t/type_datetime.test | 19 +++++++ sql/item_cmpfunc.cc | 90 +++++++++++++++++++++++-------- sql/item_cmpfunc.h | 7 ++- sql/item_timefunc.cc | 21 ++++++++ 6 files changed, 151 insertions(+), 33 deletions(-) diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index d77745176f7..151ddd95f84 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -945,25 +945,19 @@ 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 INTEGER value: '20050327 invalid' -Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid' +Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 0 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 INTEGER value: '20050328 invalid' -Warning 1292 Truncated incorrect INTEGER value: '20050328 invalid' +Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 0 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 INTEGER value: '20050327 invalid' -Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid' +Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 0 show status like "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 0 diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result index 42f97a6d53b..f5ff3369c8b 100644 --- a/mysql-test/r/type_datetime.result +++ b/mysql-test/r/type_datetime.result @@ -231,3 +231,38 @@ f1 f2 UNIX_TIMESTAMP(f2) UNIX_TIMESTAMP(f1) f1 > f2 f1 = f2 f1 < f2 2001-04-15 2001-04-15 00:00:00 987282000 987282000 0 1 0 2001-05-20 2001-05-20 01:01:01 990309661 990306000 0 0 1 drop table t1; +create table t1 (f1 date, f2 datetime, f3 timestamp); +insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01'); +insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01'); +insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01'); +insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00'); +insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01'); +select f2 from t1 where f2 between '2001-2-5' and '01-04-14'; +f2 +2001-02-05 00:00:00 +2001-03-09 01:01:01 +select f1, f2, f3 from t1 where f1 between f2 and f3; +f1 f2 f3 +2001-02-05 2001-02-05 00:00:00 2001-02-05 01:01:01 +2001-03-10 2001-03-09 01:01:01 2001-03-10 01:01:01 +2001-04-15 2001-04-15 00:00:00 2001-04-15 00:00:00 +select f1, f2, f3 from t1 where cast(f1 as datetime) between f2 and +cast(f3 as date); +f1 f2 f3 +2001-02-05 2001-02-05 00:00:00 2001-02-05 01:01:01 +2001-03-10 2001-03-09 01:01:01 2001-03-10 01:01:01 +2001-04-15 2001-04-15 00:00:00 2001-04-15 00:00:00 +select f2 from t1 where '2001-04-10 12:34:56' between f2 and '01-05-01'; +f2 +2001-01-01 01:01:01 +2001-02-05 00:00:00 +2001-03-09 01:01:01 +select f2, f3 from t1 where '01-03-10' between f2 and f3; +f2 f3 +2001-03-09 01:01:01 2001-03-10 01:01:01 +select f2 from t1 where DATE(f2) between "2001-4-15" AND "01-4-15"; +f2 +2001-04-15 00:00:00 +SELECT 1 from dual where NOW() BETWEEN CURRENT_DATE() - INTERVAL 1 DAY AND CURRENT_DATE(); +1 +drop table t1; diff --git a/mysql-test/t/type_datetime.test b/mysql-test/t/type_datetime.test index 69a19f45411..2c38b3ea9e3 100644 --- a/mysql-test/t/type_datetime.test +++ b/mysql-test/t/type_datetime.test @@ -161,3 +161,22 @@ select f1, f2, UNIX_TIMESTAMP(f2), UNIX_TIMESTAMP(f1), f1 > f2, f1 = f2, f1 < f2 from t1; drop table t1; + +# +# Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function. +# +create table t1 (f1 date, f2 datetime, f3 timestamp); +insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01'); +insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01'); +insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01'); +insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00'); +insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01'); +select f2 from t1 where f2 between '2001-2-5' and '01-04-14'; +select f1, f2, f3 from t1 where f1 between f2 and f3; +select f1, f2, f3 from t1 where cast(f1 as datetime) between f2 and + cast(f3 as date); +select f2 from t1 where '2001-04-10 12:34:56' between f2 and '01-05-01'; +select f2, f3 from t1 where '01-03-10' between f2 and f3; +select f2 from t1 where DATE(f2) between "2001-4-15" AND "01-4-15"; +SELECT 1 from dual where NOW() BETWEEN CURRENT_DATE() - INTERVAL 1 DAY AND CURRENT_DATE(); +drop table t1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index b52a320abbf..8d38b71ca4d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -692,7 +692,7 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, b= (Item **)&b_cache; } } - is_nulls_eq= owner->functype() == Item_func::EQUAL_FUNC; + is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC); func= &Arg_comparator::compare_datetime; return 0; } @@ -700,6 +700,21 @@ int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg, } +void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) +{ + thd= current_thd; + /* A caller will handle null values by itself. */ + owner= NULL; + a= a1; + b= b1; + a_type= (*a)->field_type(); + b_type= (*b)->field_type(); + a_cache= 0; + b_cache= 0; + is_nulls_eq= FALSE; + func= &Arg_comparator::compare_datetime; +} + /* Retrieves correct DATETIME value from given item. @@ -807,7 +822,8 @@ int Arg_comparator::compare_datetime() a_value= get_datetime_value(thd, &a, &a_cache, *b, &is_null); if (!is_nulls_eq && is_null) { - owner->null_value= 1; + if (owner) + owner->null_value= 1; return -1; } @@ -815,11 +831,13 @@ int Arg_comparator::compare_datetime() b_value= get_datetime_value(thd, &b, &b_cache, *a, &is_null); if (is_null) { - owner->null_value= is_nulls_eq ? 0 : 1; + if (owner) + owner->null_value= is_nulls_eq ? 0 : 1; return is_nulls_eq ? 1 : -1; } - owner->null_value= 0; + if (owner) + owner->null_value= 0; /* Compare values. */ if (is_nulls_eq) @@ -1674,8 +1692,11 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref) void Item_func_between::fix_length_and_dec() { - max_length= 1; - THD *thd= current_thd; + max_length= 1; + THD *thd= current_thd; + int i; + bool datetime_found= FALSE; + compare_as_dates= TRUE; /* As some compare functions are generated after sql_yacc, @@ -1690,26 +1711,29 @@ void Item_func_between::fix_length_and_dec() return; /* - Make a special case of compare with date/time and longlong fields. - They are compared as integers, so for const item this time-consuming - conversion can be done only once, not for every single comparison + Detect the comparison of DATE/DATETIME items. + At least one of items should be a DATE/DATETIME item and other items + should return the STRING result. */ - if (args[0]->real_item()->type() == FIELD_ITEM && - thd->lex->sql_command != SQLCOM_CREATE_VIEW && - thd->lex->sql_command != SQLCOM_SHOW_CREATE) + for (i= 0; i < 3; i++) { - Field *field=((Item_field*) (args[0]->real_item()))->field; - if (field->can_be_compared_as_longlong()) + if (args[i]->is_datetime()) { - /* - The following can't be recoded with || as convert_constant_item - changes the argument - */ - if (convert_constant_item(thd, field,&args[1])) - cmp_type=INT_RESULT; // Works for all types. - if (convert_constant_item(thd, field,&args[2])) - cmp_type=INT_RESULT; // Works for all types. + datetime_found= TRUE; + continue; } + if (args[i]->result_type() == STRING_RESULT) + continue; + compare_as_dates= FALSE; + break; + } + if (!datetime_found) + compare_as_dates= FALSE; + + if (compare_as_dates) + { + ge_cmp.set_datetime_cmp_func(args, args + 1); + le_cmp.set_datetime_cmp_func(args, args + 2); } } @@ -1717,7 +1741,27 @@ void Item_func_between::fix_length_and_dec() longlong Item_func_between::val_int() { // ANSI BETWEEN DBUG_ASSERT(fixed == 1); - if (cmp_type == STRING_RESULT) + if (compare_as_dates) + { + int ge_res, le_res; + + ge_res= ge_cmp.compare(); + if ((null_value= args[0]->null_value)) + return 0; + le_res= le_cmp.compare(); + + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((ge_res >= 0 && le_res <=0) != negated); + else if (args[1]->null_value) + { + null_value= le_res > 0; // not null if false range. + } + else + { + null_value= ge_res < 0; + } + } + else if (cmp_type == STRING_RESULT) { String *value,*a,*b; value=args[0]->val_str(&value0); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 6318c7444de..761ca90d0a7 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -91,6 +91,7 @@ public: static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b, ulonglong *const_val_arg); + void set_datetime_cmp_func(Item **a1, Item **b1); static arg_cmp_func comparator_matrix [5][2]; friend class Item_func; @@ -579,8 +580,12 @@ class Item_func_between :public Item_func_opt_neg public: Item_result cmp_type; String value0,value1,value2; + /* TRUE <=> arguments will be compared as dates. */ + bool compare_as_dates; + /* Comparators used for DATE/DATETIME comparison. */ + Arg_comparator ge_cmp, le_cmp; Item_func_between(Item *a, Item *b, Item *c) - :Item_func_opt_neg(a, b, c) {} + :Item_func_opt_neg(a, b, c), compare_as_dates(FALSE) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_KEY; } enum Functype functype() const { return BETWEEN; } diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 683cd8803d6..194fb6445fa 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2173,6 +2173,27 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) default: goto null_date; } + + /* Adjust cached_field_type according to the detected type. */ + if (cached_field_type == MYSQL_TYPE_STRING) + { + switch (ltime->time_type) + { + case MYSQL_TIMESTAMP_DATE: + cached_field_type= MYSQL_TYPE_DATE; + break; + case MYSQL_TIMESTAMP_DATETIME: + cached_field_type= MYSQL_TYPE_DATETIME; + break; + case MYSQL_TIMESTAMP_TIME: + cached_field_type= MYSQL_TYPE_TIME; + break; + default: + /* Shouldn't get here. */ + DBUG_ASSERT(0); + break; + } + } return 0; // Ok invalid_date: From 51d19e32429255e95d6800fef904abfa1e5fc173 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 28 Apr 2007 00:04:50 +0400 Subject: [PATCH 11/17] Additional fix for the bug#27590. tests/mysql_client_test.c: Fixed failing build on the windows platform. mysql-test/r/ps_7ndb.result: The result of the adjusted test case after fix for bug#27590. mysql-test/r/ps_6bdb.result: The result of the adjusted test case after fix for bug#27590. sql/item_cmpfunc.cc: A warning is fixed. --- mysql-test/r/ps_6bdb.result | 4 ++-- mysql-test/r/ps_7ndb.result | 4 ++-- sql/item_cmpfunc.cc | 4 ++-- tests/mysql_client_test.c | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result index e83a3944d4e..407dddb6f53 100644 --- a/mysql-test/r/ps_6bdb.result +++ b/mysql-test/r/ps_6bdb.result @@ -3053,7 +3053,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3066,7 +3066,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/mysql-test/r/ps_7ndb.result b/mysql-test/r/ps_7ndb.result index e8358098ee7..2673448489a 100644 --- a/mysql-test/r/ps_7ndb.result +++ b/mysql-test/r/ps_7ndb.result @@ -3053,7 +3053,7 @@ found true set @arg00= CAST('1991-01-01 01:01:01' as datetime) ; select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and @@ -3066,7 +3066,7 @@ and c17= @arg00 ; found true prepare stmt1 from "select 'true' as found from t9 -where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and +where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and c14= CAST('1991-01-01 01:01:01' as datetime) and c15= CAST('1991-01-01 01:01:01' as datetime) and c16= CAST('1991-01-01 01:01:01' as datetime) and diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 8d38b71ca4d..169c017c914 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -544,7 +544,7 @@ static ulonglong get_date_from_str(THD *thd, String *str, timestamp_type warn_type, char *warn_name, bool *error_arg) { - ulonglong value; + ulonglong value= 0; int error; MYSQL_TIME l_time; enum_mysql_timestamp_type ret; @@ -748,7 +748,7 @@ static ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, Item *warn_item, bool *is_null) { - ulonglong value; + ulonglong value= 0; String buf, *str= 0; Item *item= **item_arg; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index dffd7517e78..3f0c24aa28a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -8785,7 +8785,9 @@ static void test_ts() int rc, field_count; char name; char query[MAX_TEST_QUERY_LENGTH]; - + const char *queries [3]= {"SELECT a, b, c FROM test_ts WHERE %c=?", + "SELECT a, b, c FROM test_ts WHERE %c=?", + "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS DATE)"}; myheader("test_ts"); rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ts"); @@ -8852,9 +8854,7 @@ static void test_ts() mysql_free_result(prep_res); mysql_stmt_close(stmt); - char queries [3][60]= {"SELECT a, b, c FROM test_ts WHERE %c=?", - "SELECT a, b, c FROM test_ts WHERE %c=?", - "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS DATE)"}; + for (name= 'a'; field_count--; name++) { int row_count= 0; From f90870e98fe0e5626ccef14eb5b324d06195243a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 00:50:33 +0500 Subject: [PATCH 12/17] Fixed bug #20710. This bug occurs when error message length exceeds allowed limit: my_error() function outputs "%s" sequences instead of long string arguments. Formats like %-.64s are very common in errmsg.txt files, however my_error() function simply ignores precision of those formats. mysys/my_error.c: Fixed bug #20710. This bug occurs when error message length exceeds allowed limit: my_error() function output "%s" sequences instead of long string arguments. my_error() function has been fixed to accept formats like %-.64s. mysql-test/t/alter_table.test: Added test case for bug #20710. mysql-test/r/alter_table.result: Added test case for bug #20710. --- mysql-test/r/alter_table.result | 5 +++++ mysql-test/t/alter_table.test | 11 +++++++++++ mysys/my_error.c | 13 +++++++++++++ 3 files changed, 29 insertions(+) diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index da5cf688325..8957e1ee7a1 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -803,3 +803,8 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp alter table table_24562 order by no_such_col; ERROR 42S22: Unknown column 'no_such_col' in 'order clause' drop table table_24562; +CREATE TABLE t1 (c1 CHAR(255)); +INSERT INTO t1 VALUES (REPEAT("x", 255)), (REPEAT("x", 255)); +ALTER TABLE t1 ADD UNIQUE (c1); +ERROR 23000: Duplicate entry 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' for key 1 +DROP TABLE t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 52a569dfb57..874c42ac0b6 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -583,5 +583,16 @@ alter table table_24562 order by no_such_col; drop table table_24562; +# +# Bug #20710: adding unique index of column with duplicated +# long values to reproduce error message with truncated key value. +# + +CREATE TABLE t1 (c1 CHAR(255)); +INSERT INTO t1 VALUES (REPEAT("x", 255)), (REPEAT("x", 255)); +--error 1062 +ALTER TABLE t1 ADD UNIQUE (c1); +DROP TABLE t1; + # End of 4.1 tests diff --git a/mysys/my_error.c b/mysys/my_error.c index 8a377f63c7e..0f8ffb7c05f 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -82,6 +82,11 @@ int my_error(int nr,myf MyFlags, ...) If "%.*u" or "%.*d" are encountered, the precision number is read from the variable argument list but its value is ignored. */ + if (*tpos == '-') + { + tpos++; + olen--; + } prec_supplied= 0; if (*tpos== '.') { @@ -94,6 +99,14 @@ int my_error(int nr,myf MyFlags, ...) prec_chars= va_arg(ap, int); /* get length parameter */ prec_supplied= 1; } + else + { + for (prec_chars= 0; my_isdigit(&my_charset_latin1, *tpos); tpos++, olen--) + { + prec_supplied= 1; + prec_chars= prec_chars * 10 + *tpos - '0'; + } + } } if (!prec_supplied) From 98c0da4ed5dd8cbfc8450675e2c008b829367318 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 04:16:17 +0500 Subject: [PATCH 13/17] Fixed bug #13191. INSERT...ON DUPLICATE KEY UPDATE may cause error 1032: "Can't find record in ..." if we are inserting into InnoDB table unique index of partial key with underlying UTF-8 string field. This error occurs because INSERT...ON DUPLICATE uses a wrong procedure to copy string fields of multi-byte character sets for index search. mysql-test/t/innodb_mysql.test: Added test case for bug #13191. mysql-test/r/innodb_mysql.result: Added test case for bug #13191. sql/field.h: Fixed bug #13191. Field_string::get_key_image() virtual function was overloaded to implement copying of variable length character (UTF-8) fields. Field::get_key_image() function prototype has been changed to return byte size of copied data. sql/field.cc: Fixed bug #13191. Field_string::get_key_image() virtual function was overloaded to implement copying of variable length character (UTF-8) fields. Field::get_key_image() function prototype has been changed to return byte size of copied data. sql/key.cc: Fixed bug #13191. INSERT...ON DUPLICATE KEY UPDATE may cause error 1032: "Can't find record in ...". This error occurs because INSERT...ON DUPLICATE uses a wrong procedure to copy field parts for index search. key_copy() function has been fixed. --- mysql-test/r/innodb_mysql.result | 33 ++++++++++++++++++++++ mysql-test/t/innodb_mysql.test | 30 ++++++++++++++++++++ sql/field.cc | 41 ++++++++++++++++++--------- sql/field.h | 48 ++++++++++++++++++++++++++++---- sql/key.cc | 23 +++++++-------- 5 files changed, 145 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 2cd6f7826ca..12c8b414d2d 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -128,4 +128,37 @@ show /*!50002 GLOBAL */ status like 'Handler_rollback'; Variable_name Value Handler_rollback 0 drop table t1; +CREATE TABLE t1(c1 TEXT, UNIQUE (c1(1)), cnt INT DEFAULT 1) +ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +c1 cnt +1a 1 +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +c1 cnt +1a 2 +DROP TABLE t1; +CREATE TABLE t1(c1 VARCHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1) +ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +c1 cnt +1a 1 +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +c1 cnt +1a 2 +DROP TABLE t1; +CREATE TABLE t1(c1 CHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1) +ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +c1 cnt +1a 1 +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +c1 cnt +1a 2 +DROP TABLE t1; End of 4.1 tests diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index c5a5e997775..0973385dc5b 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -161,4 +161,34 @@ show /*!50002 GLOBAL */ status like 'Handler_rollback'; connection default; drop table t1; disconnect con1; + +# +# Bug #13191: INSERT...ON DUPLICATE KEY UPDATE of UTF-8 string fields +# used in partial unique indices. +# + +CREATE TABLE t1(c1 TEXT, UNIQUE (c1(1)), cnt INT DEFAULT 1) + ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1(c1 VARCHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1) + ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1(c1 CHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1) + ENGINE=INNODB CHARACTER SET UTF8; +INSERT INTO t1 (c1) VALUES ('1a'); +SELECT * FROM t1; +INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +DROP TABLE t1; + --echo End of 4.1 tests diff --git a/sql/field.cc b/sql/field.cc index acc837c1d37..1b27e12e078 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5204,6 +5204,16 @@ uint Field_string::max_packed_col_length(uint max_length) return (max_length > 255 ? 2 : 1)+max_length; } +uint Field_string::get_key_image(char *buff, uint length, CHARSET_INFO *cs, + imagetype type_arg) +{ + uint bytes = my_charpos(cs, ptr, ptr + field_length, + length / field_charset->mbmaxlen); + memcpy(buff, ptr, bytes); + if (bytes < length) + bzero(buff + bytes, length - bytes); + return bytes; +} /**************************************************************************** ** VARCHAR type (Not available for the end user yet) @@ -5414,8 +5424,8 @@ uint Field_varstring::max_packed_col_length(uint max_length) return (max_length > 255 ? 2 : 1)+max_length; } -void Field_varstring::get_key_image(char *buff, uint length, CHARSET_INFO *cs, - imagetype type) +uint Field_varstring::get_key_image(char *buff, uint length, CHARSET_INFO *cs, + imagetype type) { uint f_length=uint2korr(ptr); if (f_length > length) @@ -5426,6 +5436,7 @@ void Field_varstring::get_key_image(char *buff, uint length, CHARSET_INFO *cs, if (f_length < length) bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length)); #endif + return HA_KEY_BLOB_LENGTH+f_length; } void Field_varstring::set_key_image(char *buff,uint length, CHARSET_INFO *cs) @@ -5724,8 +5735,8 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, /* The following is used only when comparing a key */ -void Field_blob::get_key_image(char *buff,uint length, - CHARSET_INFO *cs, imagetype type) +uint Field_blob::get_key_image(char *buff,uint length, + CHARSET_INFO *cs, imagetype type) { uint32 blob_length= get_length(ptr); char *blob; @@ -5737,16 +5748,17 @@ void Field_blob::get_key_image(char *buff,uint length, MBR mbr; Geometry_buffer buffer; Geometry *gobj; + const uint image_length= SIZEOF_STORED_DOUBLE*4; if (blob_length < SRID_SIZE) { - bzero(buff, SIZEOF_STORED_DOUBLE*4); - return; + bzero(buff, image_length); + return image_length; } get_ptr(&blob); gobj= Geometry::construct(&buffer, blob, blob_length); if (gobj->get_mbr(&mbr, &dummy)) - bzero(buff, SIZEOF_STORED_DOUBLE*4); + bzero(buff, image_length); else { float8store(buff, mbr.xmin); @@ -5754,7 +5766,7 @@ void Field_blob::get_key_image(char *buff,uint length, float8store(buff+16, mbr.ymin); float8store(buff+24, mbr.ymax); } - return; + return image_length; } #endif /*HAVE_SPATIAL*/ @@ -5774,6 +5786,7 @@ void Field_blob::get_key_image(char *buff,uint length, } int2store(buff,length); memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length); + return HA_KEY_BLOB_LENGTH+length; } void Field_blob::set_key_image(char *buff,uint length, CHARSET_INFO *cs) @@ -6021,8 +6034,8 @@ uint Field_blob::max_packed_col_length(uint max_length) #ifdef HAVE_SPATIAL -void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, - imagetype type) +uint Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, + imagetype type) { char *blob; const char *dummy; @@ -6030,16 +6043,17 @@ void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, ulong blob_length= get_length(ptr); Geometry_buffer buffer; Geometry *gobj; + const uint image_length= SIZEOF_STORED_DOUBLE*4; if (blob_length < SRID_SIZE) { - bzero(buff, SIZEOF_STORED_DOUBLE*4); - return; + bzero(buff, image_length); + return image_length; } get_ptr(&blob); gobj= Geometry::construct(&buffer, blob, blob_length); if (gobj->get_mbr(&mbr, &dummy)) - bzero(buff, SIZEOF_STORED_DOUBLE*4); + bzero(buff, image_length); else { float8store(buff, mbr.xmin); @@ -6047,6 +6061,7 @@ void Field_geom::get_key_image(char *buff, uint length, CHARSET_INFO *cs, float8store(buff + 16, mbr.ymin); float8store(buff + 24, mbr.ymax); } + return image_length; } diff --git a/sql/field.h b/sql/field.h index 58177747120..20f1209a439 100644 --- a/sql/field.h +++ b/sql/field.h @@ -228,9 +228,43 @@ public: { memcpy(buff,ptr,length); } inline void set_image(char *buff,uint length, CHARSET_INFO *cs) { memcpy(ptr,buff,length); } - virtual void get_key_image(char *buff,uint length, CHARSET_INFO *cs, - imagetype type) - { get_image(buff,length,cs); } + + + /* + Copy a field part into an output buffer. + + SYNOPSIS + Field::get_key_image() + buff [out] output buffer + length output buffer size + cs charset, always same as this->charset(), + (to be removed in 5.x) + type itMBR for geometry blobs, otherwise itRAW + + DESCRIPTION + This function makes a copy of field part of size equal to or + less than "length" parameter value. + For fields of string types (CHAR, VARCHAR, TEXT) the rest of buffer + is padded by zero byte. + + NOTES + For variable length character fields (i.e. UTF-8) the "length" + parameter means a number of output buffer bytes as if all field + characters have maximal possible size (mbmaxlen). In the other words, + "length" parameter is a number of characters multiplied by + field_charset->mbmaxlen. + + RETURN + Number of copied bytes (excluding padded zero bytes -- see above). + */ + + virtual uint get_key_image(char *buff, uint length, + CHARSET_INFO *cs, + imagetype type) + { + get_image(buff,length,cs); + return length; + } virtual void set_key_image(char *buff,uint length, CHARSET_INFO *cs) { set_image(buff,length,cs); } inline longlong val_int_offset(uint row_offset) @@ -947,6 +981,8 @@ public: enum_field_types real_type() const { return FIELD_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } + virtual uint get_key_image(char *buff,uint length, CHARSET_INFO *cs, + imagetype type); }; @@ -981,7 +1017,7 @@ public: String *val_str(String*,String *); int cmp(const char *,const char*); void sort_string(char *buff,uint length); - void get_key_image(char *buff,uint length, CHARSET_INFO *cs, imagetype type); + uint get_key_image(char *buff,uint length, CHARSET_INFO *cs, imagetype type); void set_key_image(char *buff,uint length, CHARSET_INFO *cs); void sql_type(String &str) const; char *pack(char *to, const char *from, uint max_length=~(uint) 0); @@ -1060,7 +1096,7 @@ public: store_length(length); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); } - void get_key_image(char *buff,uint length, CHARSET_INFO *cs, imagetype type); + uint get_key_image(char *buff,uint length, CHARSET_INFO *cs, imagetype type); void set_key_image(char *buff,uint length, CHARSET_INFO *cs); void sql_type(String &str) const; inline bool copy() @@ -1117,7 +1153,7 @@ public: int store(longlong nr) { return 1; } int reset(void) { return !maybe_null() || Field_blob::reset(); } - void get_key_image(char *buff,uint length, CHARSET_INFO *cs,imagetype type); + uint get_key_image(char *buff,uint length, CHARSET_INFO *cs,imagetype type); void set_key_image(char *buff,uint length, CHARSET_INFO *cs); }; #endif /*HAVE_SPATIAL*/ diff --git a/sql/key.cc b/sql/key.cc index 7ddd40de2c9..d57d1570da7 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -89,20 +89,21 @@ void key_copy(byte *key,TABLE *table,uint idx,uint key_length) } if (key_part->key_part_flag & HA_BLOB_PART) { - char *pos; - ulong blob_length=((Field_blob*) key_part->field)->get_length(); - key_length-=2; - ((Field_blob*) key_part->field)->get_ptr(&pos); - length=min(key_length,key_part->length); - set_if_smaller(blob_length,length); - int2store(key,(uint) blob_length); - key+=2; // Skip length info - memcpy(key,pos,blob_length); + key_length-= HA_KEY_BLOB_LENGTH; + length= min(key_length, key_part->length); + key_part->field->get_key_image((char *) key, length, + key_part->field->charset(), + Field::itRAW); + key+= HA_KEY_BLOB_LENGTH; } else { - length=min(key_length,key_part->length); - memcpy(key,table->record[0]+key_part->offset,(size_t) length); + length= min(key_length, key_part->length); + Field *field= key_part->field; + CHARSET_INFO *cs= field->charset(); + uint bytes= field->get_key_image(key, length, cs, Field::itRAW); + if (bytes < length) + cs->cset->fill(cs, key + bytes, length - bytes, ' '); } key+=length; key_length-=length; From 3f169678479be890f6813f16615d413247b3e779 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 05:06:14 +0500 Subject: [PATCH 14/17] Adjusted results after the fix for bug #20710. --- mysql-test/r/heap.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result index 6bb9d0c87ee..b19ccca2f44 100644 --- a/mysql-test/r/heap.result +++ b/mysql-test/r/heap.result @@ -294,7 +294,7 @@ drop table t1; create table t1 (c char(255), primary key(c(90))); insert into t1 values ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); insert into t1 values ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); -ERROR 23000: Duplicate entry 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl' for key 1 +ERROR 23000: Duplicate entry 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl' for key 1 drop table t1; CREATE TABLE t1 (a int, key(a)) engine=heap; insert into t1 values (0); From e8225073649844a43bc4ea6362a5df5e6210933b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 11:56:23 +0500 Subject: [PATCH 15/17] Patch to eliminate compilation errors under VC after bug #13191 fix. --- sql/key.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/key.cc b/sql/key.cc index d57d1570da7..5bb389fcb45 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -101,9 +101,9 @@ void key_copy(byte *key,TABLE *table,uint idx,uint key_length) length= min(key_length, key_part->length); Field *field= key_part->field; CHARSET_INFO *cs= field->charset(); - uint bytes= field->get_key_image(key, length, cs, Field::itRAW); + uint bytes= field->get_key_image((char *) key, length, cs, Field::itRAW); if (bytes < length) - cs->cset->fill(cs, key + bytes, length - bytes, ' '); + cs->cset->fill(cs, (char *) key + bytes, length - bytes, ' '); } key+=length; key_length-=length; From 7eb77da33a7da7c56053164793012f1091048350 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 12:56:46 +0500 Subject: [PATCH 16/17] Patch to eliminate compilation errors under VC after bug #13191 fix. --- sql/key.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/key.cc b/sql/key.cc index 1816e924c2b..2bdde46b6b3 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -124,7 +124,7 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length) { key_length-= HA_KEY_BLOB_LENGTH; length= min(key_length, key_part->length); - key_part->field->get_key_image(to_key, length, Field::itRAW); + key_part->field->get_key_image((char*) to_key, length, Field::itRAW); to_key+= HA_KEY_BLOB_LENGTH; } else From 95f51da26013aa962e5e8648e5bcad8bebc06b4e Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Apr 2007 18:46:06 +0500 Subject: [PATCH 17/17] merging fix --- mysql-test/r/innodb_mysql.result | 1 + mysql-test/r/join.result | 15 --------------- mysql-test/r/subselect.result | 23 +++++++++++++++++++++++ sql/item_cmpfunc.cc | 5 +++-- sql/item_timefunc.cc | 25 ++++++++++++++++++++++++- sql/time.cc | 4 +++- 6 files changed, 54 insertions(+), 19 deletions(-) diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index 895cb88a018..d17820e37bc 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -459,6 +459,7 @@ id c counter 3 b 2 4 a 2 drop table t1; +End of 5.0 tests CREATE TABLE t1 (a int, b int); insert into t1 values (1,1),(1,2); CREATE TABLE t2 (primary key (a)) select * from t1; diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index 840a92dcce2..b132c8b2e4c 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -847,19 +847,4 @@ select '^^: The above should be ~= 20 + cost(select * from t1). Value less than Z ^^: The above should be ~= 20 + cost(select * from t1). Value less than 20 is an error drop table t1, t2; -CREATE TABLE t1 (ID INTEGER, Name VARCHAR(50)); -CREATE TABLE t2 (Test_ID INTEGER); -CREATE VIEW v1 (Test_ID, Description) AS SELECT ID, Name FROM t1; -CREATE TABLE tv1 SELECT Description AS Name FROM v1 JOIN t2 -USING (Test_ID); -DESCRIBE tv1; -Field Type Null Key Default Extra -Name varchar(50) YES NULL -CREATE TABLE tv2 SELECT Description AS Name FROM v1 JOIN t2 -ON v1.Test_ID = t2.Test_ID; -DESCRIBE tv2; -Field Type Null Key Default Extra -Name varchar(50) YES NULL -DROP VIEW v1; -DROP TABLE t1,t2,tv1,tv2; End of 5.0 tests. diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 1fff29cd72c..0143b0c7fd3 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -4035,6 +4035,29 @@ FROM t1; ERROR HY000: Invalid use of group function DROP TABLE t1,t2; End of 5.0 tests. +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (2,22),(1,11),(2,22); +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +a +1 +2 +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; +a +SELECT a FROM t1 t0 +WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; +a +1 +2 +SET @@sql_mode='ansi'; +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +ERROR HY000: Invalid use of group function +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; +ERROR HY000: Invalid use of group function +SELECT a FROM t1 t0 +WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; +ERROR HY000: Invalid use of group function +SET @@sql_mode=default; +DROP TABLE t1; CREATE TABLE t1 (s1 char(1)); INSERT INTO t1 VALUES ('a'); SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 982179a3bb2..ad7b877cd0b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -619,8 +619,9 @@ get_date_from_str(THD *thd, String *str, timestamp_type warn_type, if (error || *error_arg) { - make_truncated_value_warning(thd, str->ptr(), str->length(), warn_type, - warn_name); + make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + str->ptr(), str->length(), + warn_type, warn_name); *error_arg= TRUE; } return value; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 4d6ca2a9b3e..0fd57264784 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2056,7 +2056,30 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date) if (date_sub_interval) interval.neg = !interval.neg; - return (null_value= date_add_interval(ltime, int_type, interval)); + if ((null_value= date_add_interval(ltime, int_type, interval))) + return 1; + + /* Adjust cached_field_type according to the detected type. */ + if (cached_field_type == MYSQL_TYPE_STRING) + { + switch (ltime->time_type) + { + case MYSQL_TIMESTAMP_DATE: + cached_field_type= MYSQL_TYPE_DATE; + break; + case MYSQL_TIMESTAMP_DATETIME: + cached_field_type= MYSQL_TYPE_DATETIME; + break; + case MYSQL_TIMESTAMP_TIME: + cached_field_type= MYSQL_TYPE_TIME; + break; + default: + /* Shouldn't get here. */ + DBUG_ASSERT(0); + break; + } + } + return 0; } diff --git a/sql/time.cc b/sql/time.cc index 249de3b879b..c552d085f5e 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -860,8 +860,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter } break; default: - return 1; + goto null_date; } + return 0; // Ok invalid_date: @@ -869,6 +870,7 @@ invalid_date: ER_DATETIME_FUNCTION_OVERFLOW, ER(ER_DATETIME_FUNCTION_OVERFLOW), "datetime"); +null_date: return 1; }