From 33fc4ad4e124413ef617a1a073bb50135f6a12af Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Jul 2007 04:35:16 +0500 Subject: [PATCH 1/5] Fixed bug #30120. SP with local variables with non-ASCII names crashed the server. The server replaces SP local variable names with NAME_CONST calls when putting statements into the binary log. It used UTF8-encoded item names as variable names for the replacement inside NAME_CONST calls. However, statement string may be encoded by any known character set by the SET NAMES statement. The server used byte length of UTF8-encoded names to increment the position in the query string that led to array index overrun. sql/item.cc: Fixed bug #30120. The Item_splocal class constructor has been modified to accept new parameter `len_in_q': the byte length of variable name in the query string. sql/item.h: Fixed bug #30120. The Item_splocal class has been modified to keep new field `len_in_query': the byte length of variable name in the query string. sql/sp_head.cc: Fixed bug #30120. The subst_spvars function has been modified to increment position in the query string by the lengths of not encoded variable names instead of byte length of names encoded to UTF-8. sql/sql_yacc.yy: Fixed bug #30120. The simple_ident rule action has been modified to pass the byte length of the local variable name token to the Item_splocal object constructor. mysql-test/t/sp.test: Updated test case for bug #30120. mysql-test/r/sp.result: Updated test case for bug #30120. --- mysql-test/r/sp.result | 11 +++++++++++ mysql-test/t/sp.test | 21 +++++++++++++++++++++ sql/item.cc | 4 ++-- sql/item.h | 11 ++++++++++- sql/sp_head.cc | 2 +- sql/sql_yacc.yy | 3 ++- 6 files changed, 47 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a7b6d8aa3fd..4a278cd4aec 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6303,4 +6303,15 @@ DROP VIEW v1; DROP FUNCTION f1; DROP FUNCTION f2; DROP TABLE t1; +SET NAMES latin1; +CREATE PROCEDURE p1() +BEGIN +DECLARE бвд INT; +SELECT бвд; +END| +CALL p1(); +бвд +NULL +SET NAMES default; +DROP PROCEDURE p1; End of 5.0 tests diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index ddf33b285f5..46a1b1dc740 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -7278,4 +7278,25 @@ DROP FUNCTION f1; DROP FUNCTION f2; DROP TABLE t1; +# +# Bug #30120 SP with local variables with non-ASCII names crashes server. +# + +SET NAMES latin1; + +DELIMITER |; + +CREATE PROCEDURE p1() +BEGIN + DECLARE бвд INT; + SELECT бвд; +END| + +DELIMITER ;| + +CALL p1(); + +SET NAMES default; +DROP PROCEDURE p1; + --echo End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index bd47fb706a6..9612fbc5243 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1053,9 +1053,9 @@ bool Item_sp_variable::is_null() Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx, enum_field_types sp_var_type, - uint pos_in_q) + uint pos_in_q, uint len_in_q) :Item_sp_variable(sp_var_name.str, sp_var_name.length), - m_var_idx(sp_var_idx), pos_in_query(pos_in_q) + m_var_idx(sp_var_idx), pos_in_query(pos_in_q), len_in_query(len_in_q) { maybe_null= TRUE; diff --git a/sql/item.h b/sql/item.h index 72236cb5e63..23f6977a0f8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -960,9 +960,18 @@ public: SP variable in query text. */ uint pos_in_query; + /* + Byte length of SP variable name in the statement (see pos_in_query). + The value of this field may differ from the name_length value because + name_length contains byte length of UTF8-encoded item name, but + the query string (see sp_instr_stmt::m_query) is currently stored with + a charset from the SET NAMES statement. + */ + uint len_in_query; Item_splocal(const LEX_STRING &sp_var_name, uint sp_var_idx, - enum_field_types sp_var_type, uint pos_in_q= 0); + enum_field_types sp_var_type, + uint pos_in_q= 0, uint len_in_q= 0); bool is_splocal() { return 1; } /* Needed for error checking */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index fd8724b2171..8f4d407a5b0 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -864,7 +864,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) /* append the text between sp ref occurences */ res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); - prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length; + prev_pos= (*splocal)->pos_in_query + (*splocal)->len_in_query; /* append the spvar substitute */ res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('")); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6fbd521e302..b0e59d0f8dc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7708,7 +7708,8 @@ simple_ident: Item_splocal *splocal; splocal= new Item_splocal($1, spv->offset, spv->type, lip->tok_start_prev - - lex->sphead->m_tmp_query); + lex->sphead->m_tmp_query, + lip->tok_end - lip->tok_start_prev); #ifndef DBUG_OFF if (splocal) splocal->m_sp= lex->sphead; From 77edfb16e8a699d87694a7b3aff95118c5104d83 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Jul 2007 21:05:56 +0500 Subject: [PATCH 2/5] bigint.test: Fixing a typo in the test case. mysql-test/t/bigint.test: Fixing a typo in the test case. --- mysql-test/t/bigint.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/t/bigint.test b/mysql-test/t/bigint.test index 4aef7395184..002dfad9981 100644 --- a/mysql-test/t/bigint.test +++ b/mysql-test/t/bigint.test @@ -307,6 +307,6 @@ select -(9223372036854775808); select -((9223372036854775808)); select -(-(9223372036854775808)); --disable_metadata ---endble_ps_protocol +--enable_ps_protocol select --9223372036854775808, ---9223372036854775808, ----9223372036854775808; select -(-9223372036854775808), -(-(-9223372036854775808)); From 791584ae0d57ce1f31b4652c1dd9cb4edee8526e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 31 Jul 2007 10:46:04 +0500 Subject: [PATCH 3/5] Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. As a result of this bug 'SELECT AGGREGATE_FUNCTION(fld) ... GROUP BY' can return one row instead of an empty result set. When GROUP BY only has fields of constant tables (with a single row), the optimizer deletes the group_list. After that we lose the information about whether we had an GROUP BY statement. Though it's important as SELECT min(x) from empty_table; and SELECT min(x) from empty_table GROUP BY y; have to return different results - the first query should return one row, second - an empty result set. So here we add the 'group_optimized_away' flag to remember this case when GROUP BY exists in the query and is removed by the optimizer, and check this flag in end_send_group() mysql-test/r/group_by.result: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. test result mysql-test/r/insert_select.result: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. test result mysql-test/t/group_by.test: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. This is additional testcase that is more basic than the original bug's testcase and has the same reason. mysql-test/t/insert_select.test: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. test case sql/sql_select.cc: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. Remember the 'GROUP BY was optimized away' case in the JOIN::group_optimized and check this in the end_send_group() sql/sql_select.h: Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty. JOIN::group_optimized member added to remember the 'GROUP BY optimied away' case --- mysql-test/r/group_by.result | 17 +++++++++++++++++ mysql-test/r/insert_select.result | 26 ++++++++++++++++++++++++++ mysql-test/t/group_by.test | 23 +++++++++++++++++++++++ mysql-test/t/insert_select.test | 28 ++++++++++++++++++++++++++++ sql/sql_select.cc | 4 +++- sql/sql_select.h | 9 +++++++++ 6 files changed, 106 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 61b73dc7005..f717c16742f 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -818,3 +818,20 @@ a 2 1 DROP TABLE t1; +CREATE TABLE t1 ( +f1 int(10) unsigned NOT NULL auto_increment primary key, +f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( +f1 varchar(10) NOT NULL default '', +f2 char(3) NOT NULL default '', +PRIMARY KEY (`f1`), +KEY `k1` (`f2`,`f1`) +); +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT SQL_BUFFER_RESULT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +avg(t2.f1) +SELECT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +avg(t2.f1) +DROP TABLE t1, t2; diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index 1d7aef256e1..601eb2aa0bc 100644 --- a/mysql-test/r/insert_select.result +++ b/mysql-test/r/insert_select.result @@ -690,3 +690,29 @@ CREATE TABLE t1 (a int PRIMARY KEY); INSERT INTO t1 values (1), (2); INSERT INTO t1 SELECT a + 2 FROM t1 LIMIT 1; DROP TABLE t1; +CREATE TABLE t1 ( +f1 int(10) unsigned NOT NULL auto_increment PRIMARY KEY, +f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( +f1 varchar(10) NOT NULL default '', +f2 char(3) NOT NULL default '', +PRIMARY KEY (`f1`), +KEY `k1` (`f2`, `f1`) +); +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +min(t2.f1) +INSERT INTO t1 (f2) +SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT * FROM t1; +f1 f2 +1 +DROP TABLE t1, t2; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index 064d46aa0c0..180299f7f4a 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -633,4 +633,27 @@ SELECT a FROM t1 ORDER BY 'a' DESC; SELECT a FROM t1 ORDER BY "a" DESC; SELECT a FROM t1 ORDER BY `a` DESC; DROP TABLE t1; + + +# +# Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself +# returns empty +# +CREATE TABLE t1 ( + f1 int(10) unsigned NOT NULL auto_increment primary key, + f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( + f1 varchar(10) NOT NULL default '', + f2 char(3) NOT NULL default '', + PRIMARY KEY (`f1`), + KEY `k1` (`f2`,`f1`) +); + +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT SQL_BUFFER_RESULT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +SELECT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +DROP TABLE t1, t2; + # End of 4.1 tests diff --git a/mysql-test/t/insert_select.test b/mysql-test/t/insert_select.test index fcea489fcff..dfb313706dd 100644 --- a/mysql-test/t/insert_select.test +++ b/mysql-test/t/insert_select.test @@ -239,4 +239,32 @@ INSERT INTO t1 SELECT a + 2 FROM t1 LIMIT 1; DROP TABLE t1; +# +# Bug #29717 INSERT INTO SELECT inserts values even if SELECT statement itself returns empty +# + +CREATE TABLE t1 ( + f1 int(10) unsigned NOT NULL auto_increment PRIMARY KEY, + f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( + f1 varchar(10) NOT NULL default '', + f2 char(3) NOT NULL default '', + PRIMARY KEY (`f1`), + KEY `k1` (`f2`, `f1`) +); + +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT COUNT(*) FROM t1; + +SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; + +INSERT INTO t1 (f2) + SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; + +SELECT COUNT(*) FROM t1; +SELECT * FROM t1; +DROP TABLE t1, t2; + # End of 4.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8b5664a7f96..8f0ad359f43 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -777,6 +777,7 @@ JOIN::optimize() order=0; // The output has only one row simple_order=1; select_distinct= 0; // No need in distinct for 1 row + group_optimized_away= 1; } calc_group_buffer(this, group_list); @@ -6896,7 +6897,8 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!join->first_record || end_of_records || (idx=test_if_group_changed(join->group_fields)) >= 0) { - if (join->first_record || (end_of_records && !join->group)) + if (join->first_record || + (end_of_records && !join->group && !join->group_optimized_away)) { if (join->procedure) join->procedure->end_group(); diff --git a/sql/sql_select.h b/sql/sql_select.h index c61ef4fb92b..6227d6d2cc5 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -180,6 +180,14 @@ class JOIN :public Sql_alloc ROLLUP rollup; // Used with rollup bool select_distinct; // Set if SELECT DISTINCT + /* + If we have the GROUP BY statement in the query, + but the group_list was emptied by optimizer, this + flag is TRUE. + It happens when fields in the GROUP BY are from + constant table + */ + bool group_optimized_away; /* simple_xxxxx is set if ORDER/GROUP BY doesn't include any references @@ -276,6 +284,7 @@ class JOIN :public Sql_alloc ref_pointer_array_size= 0; zero_result_cause= 0; optimized= 0; + group_optimized_away= 0; fields_list= fields_arg; bzero((char*) &keyuse,sizeof(keyuse)); From 210243480cf0506809d28dd7502f73628483cd8b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 31 Jul 2007 11:10:03 +0500 Subject: [PATCH 4/5] merging --- mysql-test/r/group_by.result | 17 +++++++++++++++++ mysql-test/r/insert_select.result | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 88f635595d6..d4ffbe43a91 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -824,6 +824,23 @@ a 2 1 DROP TABLE t1; +CREATE TABLE t1 ( +f1 int(10) unsigned NOT NULL auto_increment primary key, +f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( +f1 varchar(10) NOT NULL default '', +f2 char(3) NOT NULL default '', +PRIMARY KEY (`f1`), +KEY `k1` (`f2`,`f1`) +); +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT SQL_BUFFER_RESULT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +avg(t2.f1) +SELECT avg(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +avg(t2.f1) +DROP TABLE t1, t2; create table t1 (c1 char(3), c2 char(3)); create table t2 (c3 char(3), c4 char(3)); insert into t1 values ('aaa', 'bb1'), ('aaa', 'bb2'); diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index 8cb94072818..d16562d97e2 100644 --- a/mysql-test/r/insert_select.result +++ b/mysql-test/r/insert_select.result @@ -699,6 +699,32 @@ Handler_read_prev 0 Handler_read_rnd 0 Handler_read_rnd_next 1 DROP TABLE t1; +CREATE TABLE t1 ( +f1 int(10) unsigned NOT NULL auto_increment PRIMARY KEY, +f2 varchar(100) NOT NULL default '' +); +CREATE TABLE t2 ( +f1 varchar(10) NOT NULL default '', +f2 char(3) NOT NULL default '', +PRIMARY KEY (`f1`), +KEY `k1` (`f2`, `f1`) +); +INSERT INTO t1 values(NULL, ''); +INSERT INTO `t2` VALUES ('486878','WDT'),('486910','WDT'); +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +min(t2.f1) +INSERT INTO t1 (f2) +SELECT min(t2.f1) FROM t1, t2 where t2.f2 = 'SIR' GROUP BY t1.f1; +SELECT COUNT(*) FROM t1; +COUNT(*) +1 +SELECT * FROM t1; +f1 f2 +1 +DROP TABLE t1, t2; CREATE TABLE t1 (x int, y int); CREATE TABLE t2 (z int, y int); CREATE TABLE t3 (a int, b int); From 124ad9c6403eaf43086ac28426a89ee6df9729fb Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 31 Jul 2007 13:22:01 +0500 Subject: [PATCH 5/5] sql_yacc.yy, sp.result, disabled.def: Post-merge fix. mysql-test/t/disabled.def: Post-merge fix. mysql-test/r/sp.result: Post-merge fix. sql/sql_yacc.yy: Post-merge fix. --- mysql-test/r/sp.result | 8 ++++---- mysql-test/t/disabled.def | 1 - sql/sql_yacc.yy | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 4ac88b995dd..ebdab4a3f89 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6431,7 +6431,7 @@ select 1; select 3; select 4; -end utf8 utf8_general_ci latin1_swedish_ci +end latin1 latin1_swedish_ci latin1_swedish_ci call proc_25411_a(); 1 1 @@ -6451,7 +6451,7 @@ proc_25411_b CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_25411_b`( ) begin select p1, p2; -end utf8 utf8_general_ci latin1_swedish_ci +end latin1 latin1_swedish_ci latin1_swedish_ci select name, param_list, body from mysql.proc where name like "%25411%"; name param_list body proc_25411_a begin @@ -6489,7 +6489,7 @@ select 1 ,2 ,3; select 1,2 ,3 ; select 1 ,2 ,3 ; select 1 ,2 ,3 ; -end utf8 utf8_general_ci latin1_swedish_ci +end latin1 latin1_swedish_ci latin1_swedish_ci call proc_25411_c(); 1 2 3 1 2 3 @@ -6510,7 +6510,7 @@ select 1 /* testing */; show create procedure proc_26302; Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation proc_26302 CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_26302`() -select 1 /* testing */ utf8 utf8_general_ci latin1_swedish_ci +select 1 /* testing */ latin1 latin1_swedish_ci latin1_swedish_ci select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES where ROUTINE_NAME = "proc_26302"; ROUTINE_NAME ROUTINE_DEFINITION diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index fff651e54c9..368a659d939 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -25,4 +25,3 @@ ctype_big5 : BUG#26711 2007-06-21 Lars Test has never worked on Do mysql_upgrade : Bug#28560 test links to /usr/local/mysql/lib libraries, causes non-determinism and failures on ABI breakage federated_transactions : Bug#29523 Transactions do not work -ddl_i18n_koi8r : Bug #30120 SP with local variables with non-ASCII names crashes server diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5d665d5a6a2..855c8dede5f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9506,7 +9506,7 @@ simple_ident: splocal= new Item_splocal($1, spv->offset, spv->type, lip->get_tok_start_prev() - lex->sphead->m_tmp_query, - lip->tok_end - lip->tok_start_prev); + lip->get_tok_end() - lip->get_tok_start_prev()); #ifndef DBUG_OFF if (splocal) splocal->m_sp= lex->sphead;