From 0657ca208031b10b6bb02e53daa5de97d29e9b51 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Wed, 26 Oct 2005 15:34:57 +0200 Subject: [PATCH 1/9] Fixed BUG#14233: Crash after tampering with the mysql.proc table Added error checking for errors when attempting to use stored procedures after the mysql.proc table has been dropped, corrupted, or tampered with. Test cases were put in a separate file (sp-destruct.test). --- mysql-test/r/sp-destruct.result | 87 +++++++++++++++++++++ mysql-test/t/sp-destruct.test | 130 ++++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 2 + sql/share/errmsg.txt | 2 + sql/sp.cc | 85 +++++++++++++++------ sql/sp.h | 10 +-- sql/sql_base.cc | 32 ++++++-- sql/sql_trigger.h | 2 +- 8 files changed, 314 insertions(+), 36 deletions(-) create mode 100644 mysql-test/r/sp-destruct.result create mode 100644 mysql-test/t/sp-destruct.test diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result new file mode 100644 index 00000000000..4f3af87c012 --- /dev/null +++ b/mysql-test/r/sp-destruct.result @@ -0,0 +1,87 @@ +use mysql; +drop table if exists proc_backup; +create table proc_backup as select * from proc; +use test; +drop procedure if exists bug14233; +drop function if exists bug14233; +drop table if exists t1; +drop view if exists v1; +create procedure bug14233() +set @x = 42; +create function bug14233_f() returns int +return 42; +create table t1 (id int); +create trigger t1_ai after insert on t1 for each row call bug14233(); +alter table mysql.proc drop type; +call bug14233(); +ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +create view v1 as select bug14233_f(); +ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +insert into t1 values (0); +ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +flush table mysql.proc; +call bug14233(); +ERROR HY000: Incorrect information in file: './mysql/proc.frm' +create view v1 as select bug14233_f(); +ERROR HY000: Incorrect information in file: './mysql/proc.frm' +insert into t1 values (0); +ERROR HY000: Incorrect information in file: './mysql/proc.frm' +flush table mysql.proc; +call bug14233(); +ERROR 42S02: Table 'mysql.proc' doesn't exist +create view v1 as select bug14233_f(); +ERROR 42S02: Table 'mysql.proc' doesn't exist +insert into t1 values (0); +ERROR 42S02: Table 'mysql.proc' doesn't exist +use mysql; +create table proc as select * from proc_backup; +alter table proc add primary key (db,name,type); +use test; +flush table mysql.proc; +flush privileges; +delete from mysql.proc where name like 'bug14233%'; +insert into mysql.proc +( +db, name, type, specific_name, language, sql_data_access, is_deterministic, +security_type, param_list, returns, body, definer, created, modified, +sql_mode, comment +) +values +( +'test', 'bug14233_1', 'FUNCTION', 'bug14233_1', 'SQL', 'READS_SQL_DATA', 'NO', +'DEFINER', '', 'int(10)', +'select count(*) from mysql.user', +'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +), +( +'test', 'bug14233_2', 'FUNCTION', 'bug14233_2', 'SQL', 'READS_SQL_DATA', 'NO', +'DEFINER', '', 'int(10)', +'begin declare x int; select count(*) into x from mysql.user; end', +'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +), +( +'test', 'bug14233_3', 'PROCEDURE', 'bug14233_3', 'SQL', 'READS_SQL_DATA','NO', +'DEFINER', '', '', +'alksj wpsj sa ^#!@ ', +'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +); +select bug14233_1(); +ERROR 0A000: Not allowed to return a result set from a function +create view v1 as select bug14233_1(); +ERROR 0A000: Not allowed to return a result set from a function +select bug14233_2(); +ERROR 2F005: FUNCTION bug14233_2 ended without RETURN +create view v1 as select bug14233_2(); +select * from v1; +ERROR 2F005: FUNCTION bug14233_2 ended without RETURN +call bug14233_3(); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wpsj sa ^#!@ ' at line 3 +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row call bug14233_3(); +insert into t1 values (0); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wpsj sa ^#!@ ' at line 3 +delete from mysql.proc where name like 'bug14233%'; +drop table mysql.proc_backup; +drop trigger t1_ai; +drop table t1; +drop view v1; diff --git a/mysql-test/t/sp-destruct.test b/mysql-test/t/sp-destruct.test new file mode 100644 index 00000000000..00eeb8ddc9d --- /dev/null +++ b/mysql-test/t/sp-destruct.test @@ -0,0 +1,130 @@ +# +# Destructive stored procedure tests +# +# We do horrible things to the mysql.proc table here, so any unexpected +# failures here might leave it in an undetermined state. +# +# In the case of trouble you might want to skip this. +# + +# We're using --system things that probably doesn't work on Windows. +--source include/not_windows.inc + +# Backup proc table +use mysql; +--disable_warnings +drop table if exists proc_backup; +--enable_warnings +create table proc_backup as select * from proc; +use test; + +--disable_warnings +drop procedure if exists bug14233; +drop function if exists bug14233; +drop table if exists t1; +drop view if exists v1; +--enable_warnings + +create procedure bug14233() + set @x = 42; + +create function bug14233_f() returns int + return 42; + +create table t1 (id int); +create trigger t1_ai after insert on t1 for each row call bug14233(); + +# Unsupported tampering with the mysql.proc definition +alter table mysql.proc drop type; +--error ER_SP_PROC_TABLE_CORRUPT +call bug14233(); +--error ER_SP_PROC_TABLE_CORRUPT +create view v1 as select bug14233_f(); +--error ER_SP_PROC_TABLE_CORRUPT +insert into t1 values (0); + +flush table mysql.proc; + +# Thrashing the .frm file +--system echo 'saljdlfa' > var/master-data/mysql/proc.frm +--error ER_NOT_FORM_FILE +call bug14233(); +--error ER_NOT_FORM_FILE +create view v1 as select bug14233_f(); +--error ER_NOT_FORM_FILE +insert into t1 values (0); + + +flush table mysql.proc; + +# Drop the mysql.proc table +--system rm var/master-data/mysql/proc.* +--error ER_NO_SUCH_TABLE +call bug14233(); +--error ER_NO_SUCH_TABLE +create view v1 as select bug14233_f(); +--error ER_NO_SUCH_TABLE +insert into t1 values (0); + +# Restore mysql.proc +use mysql; +create table proc as select * from proc_backup; +alter table proc add primary key (db,name,type); +use test; + +flush table mysql.proc; +flush privileges; + +delete from mysql.proc where name like 'bug14233%'; + +# Unsupported editing of mysql.proc, circumventing checks in "create ..." +insert into mysql.proc +( + db, name, type, specific_name, language, sql_data_access, is_deterministic, + security_type, param_list, returns, body, definer, created, modified, + sql_mode, comment +) +values +( + 'test', 'bug14233_1', 'FUNCTION', 'bug14233_1', 'SQL', 'READS_SQL_DATA', 'NO', + 'DEFINER', '', 'int(10)', + 'select count(*) from mysql.user', + 'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +), +( + 'test', 'bug14233_2', 'FUNCTION', 'bug14233_2', 'SQL', 'READS_SQL_DATA', 'NO', + 'DEFINER', '', 'int(10)', + 'begin declare x int; select count(*) into x from mysql.user; end', + 'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +), +( + 'test', 'bug14233_3', 'PROCEDURE', 'bug14233_3', 'SQL', 'READS_SQL_DATA','NO', + 'DEFINER', '', '', + 'alksj wpsj sa ^#!@ ', + 'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' +); + +--error ER_SP_NO_RETSET +select bug14233_1(); +--error ER_SP_NO_RETSET +create view v1 as select bug14233_1(); + +--error ER_SP_NORETURNEND +select bug14233_2(); +create view v1 as select bug14233_2(); +--error ER_SP_NORETURNEND +select * from v1; + +--error ER_PARSE_ERROR +call bug14233_3(); +drop trigger t1_ai; +create trigger t1_ai after insert on t1 for each row call bug14233_3(); +--error ER_PARSE_ERROR +insert into t1 values (0); + +# Clean-up +delete from mysql.proc where name like 'bug14233%'; +drop table mysql.proc_backup; +drop trigger t1_ai; +drop table t1; +drop view v1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index ab57139bb77..8a4318c61ec 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -13,6 +13,8 @@ # Tests that require multiple connections, except security/privilege tests, # go to sp-thread. # Tests that uses 'goto' to into sp-goto.test (currently disabled) +# Tests that destroys system tables (e.g. mysql.proc) for error testing +# go to sp-destruct. use test; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index ccf11248a1f..141fd46d464 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5422,3 +5422,5 @@ ER_NO_REFERENCED_ROW_2 23000 eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)" ER_SP_BAD_VAR_SHADOW 42000 eng "Variable '%-.64s' must be quoted with `...`, or renamed" +ER_SP_PROC_TABLE_CORRUPT + eng "The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" diff --git a/sql/sp.cc b/sql/sp.cc index 8386c5d58a2..6f08897b1af 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1414,20 +1414,22 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src) first_no_prelock - If true, don't add tables or cache routines used by the body of the first routine (i.e. *start) will be executed in non-prelocked mode. + tabs_changed - Set to TRUE some tables were added, FALSE otherwise NOTE If some function is missing this won't be reported here. Instead this fact will be discovered during query execution. RETURN VALUE - TRUE - some tables were added - FALSE - no tables were added. + 0 - success + -x - failure (error code, like SP_PARSE_ERROR et al) */ -static bool +static int sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, Sroutine_hash_entry *start, - bool first_no_prelock) + bool first_no_prelock, bool *tabs_changed) { + int ret= 0; bool result= FALSE; bool first= TRUE; DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); @@ -1453,12 +1455,35 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, name.m_name.str+= 1; name.m_name.length= name.m_qname.length - name.m_db.length - 1; - if (db_find_routine(thd, type, &name, &sp) == SP_OK) + switch ((ret= db_find_routine(thd, type, &name, &sp))) { - if (type == TYPE_ENUM_FUNCTION) - sp_cache_insert(&thd->sp_func_cache, sp); - else - sp_cache_insert(&thd->sp_proc_cache, sp); + case SP_OK: + { + if (type == TYPE_ENUM_FUNCTION) + sp_cache_insert(&thd->sp_func_cache, sp); + else + sp_cache_insert(&thd->sp_proc_cache, sp); + } + break; + case SP_KEY_NOT_FOUND: + ret= SP_OK; + break; + case SP_OPEN_TABLE_FAILED: + /* + Force it to attempt opening it again on subsequent calls; + otherwise we will get one error message the first time, and + then ER_SP_PROC_TABLE_CORRUPT (below) on subsequent tries. + */ + mysql_proc_table_exists= 1; + /* Fall through */ + default: + /* + In some cases no error has been set (e.g. get field failed, + when the proc table has been tampered with). + */ + if (! thd->net.report_error) + my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), ret); + break; } delete newlex; thd->lex= oldlex; @@ -1473,7 +1498,9 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, } first= FALSE; } - DBUG_RETURN(result); + if (tabs_changed) + *tabs_changed= result; + DBUG_RETURN(ret); } @@ -1488,18 +1515,20 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, lex - LEX representing statement first_no_prelock - If true, don't add tables or cache routines used by the body of the first routine (i.e. *start) + tabs_changed - Set to TRUE some tables were added, FALSE otherwise RETURN VALUE - TRUE - some tables were added - FALSE - no tables were added. + 0 - success + -x - failure (error code, like SP_PARSE_ERROR et al) */ -bool -sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) +int +sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock, + bool *tabs_changed) { return sp_cache_routines_and_add_tables_aux(thd, lex, (Sroutine_hash_entry *)lex->sroutines_list.first, - first_no_prelock); + first_no_prelock, tabs_changed); } @@ -1513,16 +1542,21 @@ sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock) thd - thread context lex - LEX representing statement aux_lex - LEX representing view + + RETURN VALUE + 0 - success + -x - failure (error code, like SP_PARSE_ERROR et al) */ -void +int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex) { Sroutine_hash_entry **last_cached_routine_ptr= (Sroutine_hash_entry **)lex->sroutines_list.next; sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines_list); - (void)sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, FALSE); + return sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, FALSE, + NULL); } @@ -1536,12 +1570,18 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex) thd - thread context lex - LEX respresenting statement triggers - triggers of the table + + RETURN VALUE + 0 - success + -x - failure (error code, like SP_PARSE_ERROR et al) */ -void +int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, Table_triggers_list *triggers) { + int ret= 0; + if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key)) { Sroutine_hash_entry **last_cached_routine_ptr= @@ -1559,10 +1599,11 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, } } } - (void)sp_cache_routines_and_add_tables_aux(thd, lex, - *last_cached_routine_ptr, - FALSE); + ret= sp_cache_routines_and_add_tables_aux(thd, lex, + *last_cached_routine_ptr, + FALSE, NULL); } + return ret; } diff --git a/sql/sp.h b/sql/sp.h index 933e5793e4c..2332e8a0cbb 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -86,11 +86,11 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena, sp_name *rt, char rt_type); void sp_remove_not_own_routines(LEX *lex); void sp_update_sp_used_routines(HASH *dst, HASH *src); -bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex, - bool first_no_prelock); -void sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, - LEX *aux_lex); -void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, +int sp_cache_routines_and_add_tables(THD *thd, LEX *lex, + bool first_no_prelock, bool *tabs_changed); +int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, + LEX *aux_lex); +int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, Table_triggers_list *triggers); extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 973fbca12f5..ed05dc6720a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1978,15 +1978,20 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && thd->lex->sroutines_list.elements) { - bool first_no_prelocking, need_prelocking; + bool first_no_prelocking, need_prelocking, tabs_changed; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last; DBUG_ASSERT(thd->lex->query_tables == *start); sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking); - if ((sp_cache_routines_and_add_tables(thd, thd->lex, - first_no_prelocking) || - *start) && need_prelocking) + if (sp_cache_routines_and_add_tables(thd, thd->lex, + first_no_prelocking, + &tabs_changed) < 0) + { + result= -1; // Fatal error + goto err; + } + else if ((tabs_changed || *start) && need_prelocking) { query_tables_last_own= save_query_tables_last; *start= thd->lex->query_tables; @@ -2110,9 +2115,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) tables->lock_type >= TL_WRITE_ALLOW_WRITE) { if (!query_tables_last_own) - query_tables_last_own= thd->lex->query_tables_last; - sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, - tables->table->triggers); + query_tables_last_own= thd->lex->query_tables_last; + if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, + tables->table->triggers) < 0) + { + result= -1; // Fatal error + goto err; + } } free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); } @@ -2133,9 +2142,16 @@ process_view_routines: /* We have at least one table in TL here. */ if (!query_tables_last_own) query_tables_last_own= thd->lex->query_tables_last; - sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables->view); + if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, + tables->view) < 0) + { + result= -1; // Fatal error + goto err; + } } } + + err: thd->proc_info=0; free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index c1d1f8d0e9e..f50624767be 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -101,7 +101,7 @@ public: void set_table(TABLE *new_table); friend class Item_trigger_field; - friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, + friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, Table_triggers_list *triggers); private: From da7c4b76793bc509e3b31fe23e17266689a903ee Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Wed, 26 Oct 2005 16:35:59 +0200 Subject: [PATCH 2/9] Follow-up for BUG#14233 fix. Changed backup method for the mysql.proc table in sp-destruct.test since using "create ... as ..." didn't preserve everything, which made the system_mysql_db test fail. --- mysql-test/r/sp-destruct.result | 8 -------- mysql-test/t/sp-destruct.test | 16 ++++++---------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result index 4f3af87c012..65d72d2098b 100644 --- a/mysql-test/r/sp-destruct.result +++ b/mysql-test/r/sp-destruct.result @@ -1,6 +1,3 @@ -use mysql; -drop table if exists proc_backup; -create table proc_backup as select * from proc; use test; drop procedure if exists bug14233; drop function if exists bug14233; @@ -33,10 +30,6 @@ create view v1 as select bug14233_f(); ERROR 42S02: Table 'mysql.proc' doesn't exist insert into t1 values (0); ERROR 42S02: Table 'mysql.proc' doesn't exist -use mysql; -create table proc as select * from proc_backup; -alter table proc add primary key (db,name,type); -use test; flush table mysql.proc; flush privileges; delete from mysql.proc where name like 'bug14233%'; @@ -81,7 +74,6 @@ create trigger t1_ai after insert on t1 for each row call bug14233_3(); insert into t1 values (0); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wpsj sa ^#!@ ' at line 3 delete from mysql.proc where name like 'bug14233%'; -drop table mysql.proc_backup; drop trigger t1_ai; drop table t1; drop view v1; diff --git a/mysql-test/t/sp-destruct.test b/mysql-test/t/sp-destruct.test index 00eeb8ddc9d..7b13d273bd1 100644 --- a/mysql-test/t/sp-destruct.test +++ b/mysql-test/t/sp-destruct.test @@ -11,11 +11,10 @@ --source include/not_windows.inc # Backup proc table -use mysql; ---disable_warnings -drop table if exists proc_backup; ---enable_warnings -create table proc_backup as select * from proc; +--system rm -rf var/master-data/mysql/backup +--system mkdir var/master-data/mysql/backup +--system cp var/master-data/mysql/proc.* var/master-data/mysql/backup/ + use test; --disable_warnings @@ -67,10 +66,8 @@ create view v1 as select bug14233_f(); insert into t1 values (0); # Restore mysql.proc -use mysql; -create table proc as select * from proc_backup; -alter table proc add primary key (db,name,type); -use test; +--system mv var/master-data/mysql/backup/* var/master-data/mysql/ +--system rmdir var/master-data/mysql/backup flush table mysql.proc; flush privileges; @@ -124,7 +121,6 @@ insert into t1 values (0); # Clean-up delete from mysql.proc where name like 'bug14233%'; -drop table mysql.proc_backup; drop trigger t1_ai; drop table t1; drop view v1; From 8ecedd193b2eb8d3f696c4e10fc37eade057edcf Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Tue, 15 Nov 2005 19:21:05 -0800 Subject: [PATCH 3/9] Report truncation of spaces when inserting into a BINARY or VARBINARY field. (Bug #14299) --- mysql-test/r/type_binary.result | 22 +++++++++++++ mysql-test/t/type_binary.test | 24 ++++++++++++++ sql/field.cc | 57 ++++++++++++++++++++------------- 3 files changed, 80 insertions(+), 23 deletions(-) diff --git a/mysql-test/r/type_binary.result b/mysql-test/r/type_binary.result index 49fd7ba5633..ca8713e90ea 100644 --- a/mysql-test/r/type_binary.result +++ b/mysql-test/r/type_binary.result @@ -111,3 +111,25 @@ select count(distinct s1) from t1; count(distinct s1) 3 drop table t1; +create table t1 (b binary(2), vb varbinary(2)); +insert into t1 values(0x4120, 0x4120); +insert into t1 values(0x412020, 0x412020); +Warnings: +Warning 1265 Data truncated for column 'b' at row 1 +Warning 1265 Data truncated for column 'vb' at row 1 +drop table t1; +create table t1 (c char(2), vc varchar(2)); +insert into t1 values(0x4120, 0x4120); +insert into t1 values(0x412020, 0x412020); +Warnings: +Note 1265 Data truncated for column 'vc' at row 1 +drop table t1; +set @old_sql_mode= @@sql_mode, sql_mode= 'traditional'; +create table t1 (b binary(2), vb varbinary(2)); +insert into t1 values(0x4120, 0x4120); +insert into t1 values(0x412020, NULL); +ERROR 22001: Data too long for column 'b' at row 1 +insert into t1 values(NULL, 0x412020); +ERROR 22001: Data too long for column 'vb' at row 1 +drop table t1; +set @@sql_mode= @old_sql_mode; diff --git a/mysql-test/t/type_binary.test b/mysql-test/t/type_binary.test index b5928cb14c4..14a51e1d98e 100644 --- a/mysql-test/t/type_binary.test +++ b/mysql-test/t/type_binary.test @@ -65,3 +65,27 @@ select hex(s1) from t1 where s1=0x0120; select hex(s1) from t1 where s1=0x0100; select count(distinct s1) from t1; drop table t1; + +# +# Bug #14299: BINARY space truncation should cause warning or error +# +create table t1 (b binary(2), vb varbinary(2)); +insert into t1 values(0x4120, 0x4120); +insert into t1 values(0x412020, 0x412020); +drop table t1; +create table t1 (c char(2), vc varchar(2)); +insert into t1 values(0x4120, 0x4120); +insert into t1 values(0x412020, 0x412020); +drop table t1; + +set @old_sql_mode= @@sql_mode, sql_mode= 'traditional'; +create table t1 (b binary(2), vb varbinary(2)); +insert into t1 values(0x4120, 0x4120); +--error ER_DATA_TOO_LONG +insert into t1 values(0x412020, NULL); +--error ER_DATA_TOO_LONG +insert into t1 values(NULL, 0x412020); +drop table t1; +set @@sql_mode= @old_sql_mode; + +# End of 5.0 tests diff --git a/sql/field.cc b/sql/field.cc index 381a13c3263..0d628bada80 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5855,44 +5855,52 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); uint copy_length; - + /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); - + /* Convert character set if necessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) - { + { uint conv_errors; tmpstr.copy(from, length, cs, field_charset, &conv_errors); from= tmpstr.ptr(); - length= tmpstr.length(); + length= tmpstr.length(); if (conv_errors) error= 2; } - /* - Make sure we don't break a multibyte sequence - as well as don't copy a malformed data. - */ + /* Make sure we don't break a multibyte sequence or copy malformed data. */ copy_length= field_charset->cset->well_formed_len(field_charset, from,from+length, field_length/ field_charset->mbmaxlen, &well_formed_error); memcpy(ptr,from,copy_length); - if (copy_length < field_length) // Append spaces if shorter + + /* Append spaces if the string was shorter than the field. */ + if (copy_length < field_length) field_charset->cset->fill(field_charset,ptr+copy_length, - field_length-copy_length, + field_length-copy_length, field_charset->pad_char); - + + /* + Check if we lost any important data (anything in a binary string, + or any non-space in others). + */ if ((copy_length < length) && table->in_use->count_cuted_fields) - { // Check if we loosed some info - const char *end=from+length; - from+= copy_length; - from+= field_charset->cset->scan(field_charset, from, end, - MY_SEQ_SPACES); - if (from != end) + { + if (binary()) error= 2; + else + { + const char *end=from+length; + from+= copy_length; + from+= field_charset->cset->scan(field_charset, from, end, + MY_SEQ_SPACES); + if (from != end) + error= 2; + } } if (error) { @@ -6268,12 +6276,15 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) if ((copy_length < length) && table->in_use->count_cuted_fields && !error_code) { - const char *end= from + length; - from+= copy_length; - from+= field_charset->cset->scan(field_charset, from, end, MY_SEQ_SPACES); - /* If we lost only spaces then produce a NOTE, not a WARNING */ - if (from == end) - level= MYSQL_ERROR::WARN_LEVEL_NOTE; + if (!binary()) + { + const char *end= from + length; + from+= copy_length; + from+= field_charset->cset->scan(field_charset, from, end, MY_SEQ_SPACES); + /* If we lost only spaces then produce a NOTE, not a WARNING */ + if (from == end) + level= MYSQL_ERROR::WARN_LEVEL_NOTE; + } error_code= WARN_DATA_TRUNCATED; } if (error_code) From eb5bf2ec338379645cd8dd260de51f8ccbd46aa1 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 25 Nov 2005 17:09:26 +0100 Subject: [PATCH 4/9] Fixed BUG#14233: Crash after tampering with the mysql.proc table Post-review version. Some minor review fixes, but also changed the way some errors are handled: Don't return specific parse errors; instead always use the more general "table corrupt" error (amended accordingly). --- mysql-test/r/sp-destruct.result | 20 +++++++-------- mysql-test/r/sp-error.result | 2 +- mysql-test/t/sp-destruct.test | 14 +++++----- sql/share/errmsg.txt | 2 +- sql/sp.cc | 45 ++++++++++++++++++++------------- sql/sql_base.cc | 27 +++++++++++++++----- sql/sql_parse.cc | 8 ------ sql/sql_yacc.yy | 5 ++++ 8 files changed, 71 insertions(+), 52 deletions(-) diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result index 65d72d2098b..1b720be9403 100644 --- a/mysql-test/r/sp-destruct.result +++ b/mysql-test/r/sp-destruct.result @@ -11,11 +11,11 @@ create table t1 (id int); create trigger t1_ai after insert on t1 for each row call bug14233(); alter table mysql.proc drop type; call bug14233(); -ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +ERROR HY000: Failed to load routine test.bug14233. The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) create view v1 as select bug14233_f(); -ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +ERROR HY000: Failed to load routine test.bug14233_f. The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) insert into t1 values (0); -ERROR HY000: The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) +ERROR HY000: Failed to load routine test.bug14233. The table mysql.proc is missing, corrupt, or contains bad data (internal code -5) flush table mysql.proc; call bug14233(); ERROR HY000: Incorrect information in file: './mysql/proc.frm' @@ -59,21 +59,19 @@ values 'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' ); select bug14233_1(); -ERROR 0A000: Not allowed to return a result set from a function +ERROR HY000: Failed to load routine test.bug14233_1. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) create view v1 as select bug14233_1(); -ERROR 0A000: Not allowed to return a result set from a function +ERROR HY000: Failed to load routine test.bug14233_1. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) select bug14233_2(); -ERROR 2F005: FUNCTION bug14233_2 ended without RETURN +ERROR HY000: Failed to load routine test.bug14233_2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) create view v1 as select bug14233_2(); -select * from v1; -ERROR 2F005: FUNCTION bug14233_2 ended without RETURN +ERROR HY000: Failed to load routine test.bug14233_2. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) call bug14233_3(); -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wpsj sa ^#!@ ' at line 3 +ERROR HY000: Failed to load routine test.bug14233_3. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) drop trigger t1_ai; create trigger t1_ai after insert on t1 for each row call bug14233_3(); insert into t1 values (0); -ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wpsj sa ^#!@ ' at line 3 +ERROR HY000: Failed to load routine test.bug14233_3. The table mysql.proc is missing, corrupt, or contains bad data (internal code -6) delete from mysql.proc where name like 'bug14233%'; drop trigger t1_ai; drop table t1; -drop view v1; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 50ff7ea264a..cea0b66418e 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -124,7 +124,7 @@ begin declare x int; set x = val+3; end| -ERROR 42000: No RETURN found in FUNCTION f +ERROR 42000: No RETURN found in FUNCTION test.f create function f(val int) returns int begin declare x int; diff --git a/mysql-test/t/sp-destruct.test b/mysql-test/t/sp-destruct.test index 7b13d273bd1..a2a66090866 100644 --- a/mysql-test/t/sp-destruct.test +++ b/mysql-test/t/sp-destruct.test @@ -101,26 +101,24 @@ values 'root@localhost', NOW() , '0000-00-00 00:00:00', '', '' ); ---error ER_SP_NO_RETSET +--error ER_SP_PROC_TABLE_CORRUPT select bug14233_1(); ---error ER_SP_NO_RETSET +--error ER_SP_PROC_TABLE_CORRUPT create view v1 as select bug14233_1(); ---error ER_SP_NORETURNEND +--error ER_SP_PROC_TABLE_CORRUPT select bug14233_2(); +--error ER_SP_PROC_TABLE_CORRUPT create view v1 as select bug14233_2(); ---error ER_SP_NORETURNEND -select * from v1; ---error ER_PARSE_ERROR +--error ER_SP_PROC_TABLE_CORRUPT call bug14233_3(); drop trigger t1_ai; create trigger t1_ai after insert on t1 for each row call bug14233_3(); ---error ER_PARSE_ERROR +--error ER_SP_PROC_TABLE_CORRUPT insert into t1 values (0); # Clean-up delete from mysql.proc where name like 'bug14233%'; drop trigger t1_ai; drop table t1; -drop view v1; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 2e2403a21a1..eab46e308b8 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5422,4 +5422,4 @@ ER_NO_REFERENCED_ROW_2 23000 ER_SP_BAD_VAR_SHADOW 42000 eng "Variable '%-.64s' must be quoted with `...`, or renamed" ER_SP_PROC_TABLE_CORRUPT - eng "The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" + eng "Failed to load routine %s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)" diff --git a/sql/sp.cc b/sql/sp.cc index 6f08897b1af..49671a47bad 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1420,8 +1420,8 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src) Instead this fact will be discovered during query execution. RETURN VALUE - 0 - success - -x - failure (error code, like SP_PARSE_ERROR et al) + 0 - success + non-0 - failure */ static int @@ -1430,7 +1430,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, bool first_no_prelock, bool *tabs_changed) { int ret= 0; - bool result= FALSE; + int tabschnd= 0; /* Set if tables changed */ bool first= TRUE; DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); @@ -1478,11 +1478,21 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, /* Fall through */ default: /* - In some cases no error has been set (e.g. get field failed, - when the proc table has been tampered with). - */ - if (! thd->net.report_error) - my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), ret); + Any error when loading an existing routine is either some problem + with the mysql.proc table, or a parse error because the contents + has been tampered with (in which case we clear that error). + */ + if (ret == SP_PARSE_ERROR) + thd->clear_error(); + if (!thd->net.report_error) + { + char n[NAME_LEN*2+2]; + + /* m_qname.str is not always \0 terminated */ + memcpy(n, name.m_qname.str, name.m_qname.length); + n[name.m_qname.length]= '\0'; + my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); + } break; } delete newlex; @@ -1493,13 +1503,14 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, if (!(first && first_no_prelock)) { sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines); - result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last); + tabschnd|= + sp->add_used_tables_to_table_list(thd, &lex->query_tables_last); } } first= FALSE; } - if (tabs_changed) - *tabs_changed= result; + if (tabs_changed) /* it can be NULL */ + *tabs_changed= tabschnd; DBUG_RETURN(ret); } @@ -1518,8 +1529,8 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, tabs_changed - Set to TRUE some tables were added, FALSE otherwise RETURN VALUE - 0 - success - -x - failure (error code, like SP_PARSE_ERROR et al) + 0 - success + non-0 - failure */ int @@ -1544,8 +1555,8 @@ sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock, aux_lex - LEX representing view RETURN VALUE - 0 - success - -x - failure (error code, like SP_PARSE_ERROR et al) + 0 - success + non-0 - failure */ int @@ -1572,8 +1583,8 @@ sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex) triggers - triggers of the table RETURN VALUE - 0 - success - -x - failure (error code, like SP_PARSE_ERROR et al) + 0 - success + non-0 - failure */ int diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 273d9f7e3b4..fa3321bcb0e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1986,9 +1986,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking, - &tabs_changed) < 0) + &tabs_changed)) { - result= -1; // Fatal error + /* + Serious error during reading stored routines from mysql.proc table. + Something's wrong with the table or its contents, and an error has + been emitted; we must abort. + */ + result= -1; goto err; } else if ((tabs_changed || *start) && need_prelocking) @@ -2117,9 +2122,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) if (!query_tables_last_own) query_tables_last_own= thd->lex->query_tables_last; if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex, - tables->table->triggers) < 0) + tables->table->triggers)) { - result= -1; // Fatal error + /* + Serious error during reading stored routines from mysql.proc table. + Something's wrong with the table or its contents, and an error has + been emitted; we must abort. + */ + result= -1; goto err; } } @@ -2143,9 +2153,14 @@ process_view_routines: if (!query_tables_last_own) query_tables_last_own= thd->lex->query_tables_last; if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, - tables->view) < 0) + tables->view)) { - result= -1; // Fatal error + /* + Serious error during reading stored routines from mysql.proc table. + Something's wrong with the table or its contents, and an error has + been emitted; we must abort. + */ + result= -1; goto err; } } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1882965fd1e..ce157eeb46f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4111,14 +4111,6 @@ end_with_restore_list: } } #endif - if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && - !(lex->sphead->m_flags & sp_head::HAS_RETURN)) - { - my_error(ER_SP_NORETURN, MYF(0), name); - delete lex->sphead; - lex->sphead= 0; - goto error; - } /* We need to copy name and db in order to use them for diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 109dcd7e86a..841d4c90a70 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1475,6 +1475,11 @@ create_function_tail: YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; sp->init_strings(YYTHD, lex, lex->spname); + if (!(sp->m_flags & sp_head::HAS_RETURN)) + { + my_error(ER_SP_NORETURN, MYF(0), sp->m_qname.str); + YYABORT; + } /* Restore flag if it was cleared above */ if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; From d54c878f7b321c436cb92e227d29c9aa46d19569 Mon Sep 17 00:00:00 2001 From: "stewart@mysql.com" <> Date: Tue, 6 Dec 2005 11:25:53 +0100 Subject: [PATCH 5/9] BUG#15512 crash during online add of API/SQL node --- ndb/src/kernel/blocks/qmgr/QmgrMain.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index 7003d70e8b1..ed18a4ddb8b 100644 --- a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -2030,29 +2030,32 @@ void Qmgr::execAPI_REGREQ(Signal* signal) }//Qmgr::execAPI_REGREQ() -void +void Qmgr::execAPI_VERSION_REQ(Signal * signal) { jamEntry(); ApiVersionReq * const req = (ApiVersionReq *)signal->getDataPtr(); - + Uint32 senderRef = req->senderRef; Uint32 nodeId = req->nodeId; ApiVersionConf * conf = (ApiVersionConf *)req; if(getNodeInfo(nodeId).m_connected) + { conf->version = getNodeInfo(nodeId).m_version; + struct in_addr in= globalTransporterRegistry.get_connect_address(nodeId); + conf->inet_addr= in.s_addr; + } else + { conf->version = 0; + conf->inet_addr= 0; + } conf->nodeId = nodeId; - struct in_addr in= globalTransporterRegistry.get_connect_address(nodeId); - conf->inet_addr= in.s_addr; - sendSignal(senderRef, + sendSignal(senderRef, GSN_API_VERSION_CONF, signal, ApiVersionConf::SignalLength, JBB); - - } From c6fc5d35ccfbfe2319d962e20d51b4b1db4a92ea Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Tue, 6 Dec 2005 14:25:12 +0100 Subject: [PATCH 6/9] Final review fix of #14233: Crash after tampering with the mysql.proc table. Changed variable type and added comment in sp.c. --- sql/sp.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sql/sp.cc b/sql/sp.cc index 7dc872f6225..3e5a3cb94f1 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1462,7 +1462,7 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, bool first_no_prelock, bool *tabs_changed) { int ret= 0; - int tabschnd= 0; /* Set if tables changed */ + bool tabschnd= 0; /* Set if tables changed */ bool first= TRUE; DBUG_ENTER("sp_cache_routines_and_add_tables_aux"); @@ -1512,6 +1512,11 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, */ if (ret == SP_PARSE_ERROR) thd->clear_error(); + /* + If we cleared the parse error, or when db_find_routine() flagged + an error with it's return value without calling my_error(), we + set the generic "mysql.proc table corrupt" error here. + */ if (!thd->net.report_error) { char n[NAME_LEN*2+2]; From 9eee25472e7ba5a1bce1d44bfd054f8aa0e68f2a Mon Sep 17 00:00:00 2001 From: "aivanov@mysql.com" <> Date: Tue, 6 Dec 2005 22:02:40 +0300 Subject: [PATCH 7/9] Fix BUG#14747: "Race condition can cause btr_search_drop_page_hash_index() to crash". Changes from snapshot innodb-5.0-ss52. Note that buf_block_t::index should be protected by btr_search_latch or an s-latch or x-latch on the index page. btr_search_drop_page_hash_index(): Read block->index while holding btr_search_latch and use the cached value in the loop. Remove some redundant assertions. Also fix 13778. When FOREIGN_KEY_CHECKS=0 we still need to check that datatypes between foreign key references are compatible. Also added test cases to 9802. --- innobase/btr/btr0sea.c | 28 ++++-------- innobase/dict/dict0dict.c | 34 ++++++-------- innobase/dict/dict0load.c | 9 ++-- innobase/include/buf0buf.h | 10 ++-- innobase/include/dict0dict.h | 3 +- innobase/include/dict0load.h | 3 +- innobase/include/rem0cmp.h | 3 +- innobase/rem/rem0cmp.c | 11 +++-- innobase/row/row0mysql.c | 64 +++++++++++--------------- mysql-test/r/innodb.result | 84 +++++++++++++++++++++++++++++----- mysql-test/t/innodb.test | 88 +++++++++++++++++++++++++++++++----- sql/ha_innodb.cc | 9 ++++ sql/ha_innodb.h | 2 +- 13 files changed, 231 insertions(+), 117 deletions(-) diff --git a/innobase/btr/btr0sea.c b/innobase/btr/btr0sea.c index 7a4e92a672a..9b1e93fe700 100644 --- a/innobase/btr/btr0sea.c +++ b/innobase/btr/btr0sea.c @@ -904,6 +904,7 @@ btr_search_drop_page_hash_index( ulint* folds; ulint i; mem_heap_t* heap; + dict_index_t* index; ulint* offsets; #ifdef UNIV_SYNC_DEBUG @@ -932,11 +933,16 @@ btr_search_drop_page_hash_index( n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; + index = block->index; - ut_a(n_fields + n_bytes > 0); + /* NOTE: The fields of block must not be accessed after + releasing btr_search_latch, as the index page might only + be s-latched! */ rw_lock_s_unlock(&btr_search_latch); + ut_a(n_fields + n_bytes > 0); + n_recs = page_get_n_recs(page); /* Calculate and cache fold values into an array for fast deletion @@ -949,14 +955,6 @@ btr_search_drop_page_hash_index( rec = page_get_infimum_rec(page); rec = page_rec_get_next(rec); - if (!page_rec_is_supremum(rec)) { - ut_a(n_fields <= rec_get_n_fields(rec, block->index)); - - if (n_bytes > 0) { - ut_a(n_fields < rec_get_n_fields(rec, block->index)); - } - } - tree_id = btr_page_get_index_id(page); prev_fold = 0; @@ -964,18 +962,12 @@ btr_search_drop_page_hash_index( heap = NULL; offsets = NULL; - if (block->index == NULL) { - - mem_analyze_corruption((byte*)block); - - ut_a(block->index != NULL); - } - while (!page_rec_is_supremum(rec)) { /* FIXME: in a mixed tree, not all records may have enough ordering fields: */ - offsets = rec_get_offsets(rec, block->index, - offsets, n_fields + (n_bytes > 0), &heap); + offsets = rec_get_offsets(rec, index, offsets, + n_fields + (n_bytes > 0), &heap); + ut_a(rec_offs_n_fields(offsets) == n_fields + (n_bytes > 0)); fold = rec_fold(rec, offsets, n_fields, n_bytes, tree_id); if (fold == prev_fold && prev_fold != 0) { diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index fb95ffbd80c..8050eebddd8 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -2104,8 +2104,11 @@ dict_foreign_find_index( dict_table_t* table, /* in: table */ const char** columns,/* in: array of column names */ ulint n_cols, /* in: number of columns */ - dict_index_t* types_idx)/* in: NULL or an index to whose types the - column types must match */ + dict_index_t* types_idx, /* in: NULL or an index to whose types the + column types must match */ + ibool check_charsets) /* in: whether to check charsets. + only has an effect if types_idx != + NULL. */ { #ifndef UNIV_HOTBACKUP dict_index_t* index; @@ -2135,7 +2138,8 @@ dict_foreign_find_index( if (types_idx && !cmp_types_are_equal( dict_index_get_nth_type(index, i), - dict_index_get_nth_type(types_idx, i))) { + dict_index_get_nth_type(types_idx, i), + check_charsets)) { break; } @@ -2212,7 +2216,8 @@ dict_foreign_add_to_cache( /*======================*/ /* out: DB_SUCCESS or error code */ dict_foreign_t* foreign, /* in, own: foreign key constraint */ - ibool check_types) /* in: TRUE=check type compatibility */ + ibool check_charsets) /* in: TRUE=check charset + compatibility */ { dict_table_t* for_table; dict_table_t* ref_table; @@ -2248,16 +2253,10 @@ dict_foreign_add_to_cache( } if (for_in_cache->referenced_table == NULL && ref_table) { - dict_index_t* types_idx; - if (check_types) { - types_idx = for_in_cache->foreign_index; - } else { - types_idx = NULL; - } index = dict_foreign_find_index(ref_table, (const char**) for_in_cache->referenced_col_names, for_in_cache->n_fields, - types_idx); + for_in_cache->foreign_index, check_charsets); if (index == NULL) { dict_foreign_error_report(ef, for_in_cache, @@ -2281,16 +2280,10 @@ dict_foreign_add_to_cache( } if (for_in_cache->foreign_table == NULL && for_table) { - dict_index_t* types_idx; - if (check_types) { - types_idx = for_in_cache->referenced_index; - } else { - types_idx = NULL; - } index = dict_foreign_find_index(for_table, (const char**) for_in_cache->foreign_col_names, for_in_cache->n_fields, - types_idx); + for_in_cache->referenced_index, check_charsets); if (index == NULL) { dict_foreign_error_report(ef, for_in_cache, @@ -3097,7 +3090,7 @@ col_loop1: /* Try to find an index which contains the columns as the first fields and in the right order */ - index = dict_foreign_find_index(table, column_names, i, NULL); + index = dict_foreign_find_index(table, column_names, i, NULL, TRUE); if (!index) { mutex_enter(&dict_foreign_err_mutex); @@ -3362,8 +3355,7 @@ try_find_index: if (referenced_table) { index = dict_foreign_find_index(referenced_table, - column_names, i, - foreign->foreign_index); + column_names, i, foreign->foreign_index, TRUE); if (!index) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); diff --git a/innobase/dict/dict0load.c b/innobase/dict/dict0load.c index 9bafcf33553..3281f9926f9 100644 --- a/innobase/dict/dict0load.c +++ b/innobase/dict/dict0load.c @@ -1091,7 +1091,7 @@ dict_load_foreign( /* out: DB_SUCCESS or error code */ const char* id, /* in: foreign constraint id as a null-terminated string */ - ibool check_types)/* in: TRUE=check type compatibility */ + ibool check_charsets)/* in: TRUE=check charset compatibility */ { dict_foreign_t* foreign; dict_table_t* sys_foreign; @@ -1204,7 +1204,7 @@ dict_load_foreign( a new foreign key constraint but loading one from the data dictionary. */ - return(dict_foreign_add_to_cache(foreign, check_types)); + return(dict_foreign_add_to_cache(foreign, check_charsets)); } /*************************************************************************** @@ -1219,7 +1219,8 @@ dict_load_foreigns( /*===============*/ /* out: DB_SUCCESS or error code */ const char* table_name, /* in: table name */ - ibool check_types) /* in: TRUE=check type compatibility */ + ibool check_charsets) /* in: TRUE=check charset + compatibility */ { btr_pcur_t pcur; mem_heap_t* heap; @@ -1319,7 +1320,7 @@ loop: /* Load the foreign constraint definition to the dictionary cache */ - err = dict_load_foreign(id, check_types); + err = dict_load_foreign(id, check_charsets); if (err != DB_SUCCESS) { btr_pcur_close(&pcur); diff --git a/innobase/include/buf0buf.h b/innobase/include/buf0buf.h index ae8d0411c12..24e7a71c02d 100644 --- a/innobase/include/buf0buf.h +++ b/innobase/include/buf0buf.h @@ -745,8 +745,6 @@ struct buf_block_struct{ buffer pool which are index pages, but this flag is not set because we do not keep track of all pages */ - dict_index_t* index; /* index for which the adaptive - hash index has been created */ /* 2. Page flushing fields */ UT_LIST_NODE_T(buf_block_t) flush_list; @@ -833,7 +831,7 @@ struct buf_block_struct{ records with the same prefix should be indexed in the hash index */ - /* The following 4 fields are protected by btr_search_latch: */ + /* The following 6 fields are protected by btr_search_latch: */ ibool is_hashed; /* TRUE if hash index has already been built on this page; note that it does @@ -850,6 +848,12 @@ struct buf_block_struct{ ulint curr_side; /* BTR_SEARCH_LEFT_SIDE or BTR_SEARCH_RIGHT_SIDE in hash indexing */ + dict_index_t* index; /* Index for which the adaptive + hash index has been created. + This field may only be modified + while holding an s-latch or x-latch + on block->lock and an x-latch on + btr_search_latch. */ /* 6. Debug fields */ #ifdef UNIV_SYNC_DEBUG rw_lock_t debug_latch; /* in the debug version, each thread diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index 5215d51cabe..4396611e529 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -197,7 +197,8 @@ dict_foreign_add_to_cache( /*======================*/ /* out: DB_SUCCESS or error code */ dict_foreign_t* foreign, /* in, own: foreign key constraint */ - ibool check_types); /* in: TRUE=check type compatibility */ + ibool check_charsets);/* in: TRUE=check charset + compatibility */ /************************************************************************* Checks if a table is referenced by foreign keys. */ diff --git a/innobase/include/dict0load.h b/innobase/include/dict0load.h index f13620bc6e8..741123614ab 100644 --- a/innobase/include/dict0load.h +++ b/innobase/include/dict0load.h @@ -82,7 +82,8 @@ dict_load_foreigns( /*===============*/ /* out: DB_SUCCESS or error code */ const char* table_name, /* in: table name */ - ibool check_types); /* in: TRUE=check type compatibility */ + ibool check_charsets);/* in: TRUE=check charsets + compatibility */ /************************************************************************ Prints to the standard output information on all tables found in the data dictionary system table. */ diff --git a/innobase/include/rem0cmp.h b/innobase/include/rem0cmp.h index 1b1ee26b809..f6762078cbc 100644 --- a/innobase/include/rem0cmp.h +++ b/innobase/include/rem0cmp.h @@ -24,7 +24,8 @@ cmp_types_are_equal( /* out: TRUE if the types are considered equal in comparisons */ dtype_t* type1, /* in: type 1 */ - dtype_t* type2); /* in: type 2 */ + dtype_t* type2, /* in: type 2 */ + ibool check_charsets); /* in: whether to check charsets */ /***************************************************************** This function is used to compare two data fields for which we know the data type. */ diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 7c33476fb9e..6a463b7d4cf 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -99,7 +99,8 @@ cmp_types_are_equal( /* out: TRUE if the types are considered equal in comparisons */ dtype_t* type1, /* in: type 1 */ - dtype_t* type2) /* in: type 2 */ + dtype_t* type2, /* in: type 2 */ + ibool check_charsets) /* in: whether to check charsets */ { if (dtype_is_non_binary_string_type(type1->mtype, type1->prtype) && dtype_is_non_binary_string_type(type2->mtype, type2->prtype)) { @@ -107,12 +108,12 @@ cmp_types_are_equal( /* Both are non-binary string types: they can be compared if and only if the charset-collation is the same */ - if (dtype_get_charset_coll(type1->prtype) - == dtype_get_charset_coll(type2->prtype)) { + if (check_charsets) { + return(dtype_get_charset_coll(type1->prtype) + == dtype_get_charset_coll(type2->prtype)); + } else { return(TRUE); } - - return(FALSE); } if (dtype_is_binary_string_type(type1->mtype, type1->prtype) diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 82f7daf2ed8..723e305b2ab 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -2132,7 +2132,7 @@ row_table_add_foreign_constraints( if (err == DB_SUCCESS) { /* Check that also referencing constraints are ok */ - err = dict_load_foreigns(name, trx->check_foreigns); + err = dict_load_foreigns(name, TRUE); } if (err != DB_SUCCESS) { @@ -3590,7 +3590,8 @@ row_rename_table_for_mysql( mem_heap_t* heap = NULL; const char** constraints_to_drop = NULL; ulint n_constraints_to_drop = 0; - ibool recovering_temp_table = FALSE; + ibool recovering_temp_table = FALSE; + ibool old_is_tmp, new_is_tmp; ulint len; ulint i; ibool success; @@ -3630,6 +3631,9 @@ row_rename_table_for_mysql( trx->op_info = "renaming table"; trx_start_if_not_started(trx); + old_is_tmp = row_is_mysql_tmp_table_name(old_name); + new_is_tmp = row_is_mysql_tmp_table_name(new_name); + if (row_mysql_is_recovered_tmp_table(new_name)) { recovering_temp_table = TRUE; @@ -3676,7 +3680,7 @@ row_rename_table_for_mysql( len = (sizeof str1) + (sizeof str2) + (sizeof str3) + (sizeof str5) - 4 + ut_strlenq(new_name, '\'') + ut_strlenq(old_name, '\''); - if (row_is_mysql_tmp_table_name(new_name)) { + if (new_is_tmp) { db_name_len = dict_get_db_name_len(old_name) + 1; /* MySQL is doing an ALTER TABLE command and it renames the @@ -3829,7 +3833,7 @@ row_rename_table_for_mysql( the table is stored in a single-table tablespace */ success = dict_table_rename_in_cache(table, new_name, - !row_is_mysql_tmp_table_name(new_name)); + !new_is_tmp); if (!success) { trx->error_state = DB_SUCCESS; trx_general_rollback_for_mysql(trx, FALSE, NULL); @@ -3846,19 +3850,16 @@ row_rename_table_for_mysql( goto funct_exit; } - err = dict_load_foreigns(new_name, trx->check_foreigns); + /* We only want to switch off some of the type checking in + an ALTER, not in a RENAME. */ + + err = dict_load_foreigns(new_name, + old_is_tmp ? trx->check_foreigns : TRUE); - if (row_is_mysql_tmp_table_name(old_name)) { + if (err != DB_SUCCESS) { + ut_print_timestamp(stderr); - /* MySQL is doing an ALTER TABLE command and it - renames the created temporary table to the name - of the original table. In the ALTER TABLE we maybe - created some FOREIGN KEY constraints for the temporary - table. But we want to load also the foreign key - constraint definitions for the original table name. */ - - if (err != DB_SUCCESS) { - ut_print_timestamp(stderr); + if (old_is_tmp) { fputs(" InnoDB: Error: in ALTER TABLE ", stderr); ut_print_name(stderr, trx, new_name); @@ -3866,36 +3867,23 @@ row_rename_table_for_mysql( "InnoDB: has or is referenced in foreign key constraints\n" "InnoDB: which are not compatible with the new table definition.\n", stderr); - - ut_a(dict_table_rename_in_cache(table, - old_name, FALSE)); - trx->error_state = DB_SUCCESS; - trx_general_rollback_for_mysql(trx, FALSE, - NULL); - trx->error_state = DB_SUCCESS; - } - } else { - if (err != DB_SUCCESS) { - - ut_print_timestamp(stderr); - + } else { fputs( " InnoDB: Error: in RENAME TABLE table ", stderr); ut_print_name(stderr, trx, new_name); fputs("\n" - "InnoDB: is referenced in foreign key constraints\n" - "InnoDB: which are not compatible with the new table definition.\n", + "InnoDB: is referenced in foreign key constraints\n" + "InnoDB: which are not compatible with the new table definition.\n", stderr); - - ut_a(dict_table_rename_in_cache(table, - old_name, FALSE)); - - trx->error_state = DB_SUCCESS; - trx_general_rollback_for_mysql(trx, FALSE, - NULL); - trx->error_state = DB_SUCCESS; } + + ut_a(dict_table_rename_in_cache(table, + old_name, FALSE)); + trx->error_state = DB_SUCCESS; + trx_general_rollback_for_mysql(trx, FALSE, + NULL); + trx->error_state = DB_SUCCESS; } } funct_exit: diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index aabc83a71b8..8a01d394e78 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2437,7 +2437,9 @@ a b 20 NULL drop table t1; create table t1 (v varchar(65530), key(v)); -ERROR HY000: Can't create table './test/t1' (errno: 139) +Warnings: +Warning 1071 Specified key was too long; max key length is 767 bytes +drop table t1; create table t1 (v varchar(65536)); Warnings: Note 1246 Converting column 'v' from VARCHAR to TEXT @@ -2577,22 +2579,49 @@ create table t8 (col1 blob, index(col1(767))) character set = latin1 engine = innodb; create table t9 (col1 varchar(512), col2 varchar(512), index(col1, col2)) character set = latin1 engine = innodb; +show create table t9; +Table Create Table +t9 CREATE TABLE `t9` ( + `col1` varchar(512) default NULL, + `col2` varchar(512) default NULL, + KEY `col1` (`col1`,`col2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 drop table t1, t2, t3, t4, t5, t6, t7, t8, t9; -create table t1 (col1 varchar(768), index (col1)) +create table t1 (col1 varchar(768), index(col1)) character set = latin1 engine = innodb; -ERROR HY000: Can't create table './test/t1.frm' (errno: 139) -create table t2 (col1 varchar(768) primary key) +Warnings: +Warning 1071 Specified key was too long; max key length is 767 bytes +create table t2 (col1 varbinary(768), index(col1)) character set = latin1 engine = innodb; -ERROR HY000: Can't create table './test/t2.frm' (errno: 139) -create table t3 (col1 varbinary(768) primary key) +Warnings: +Warning 1071 Specified key was too long; max key length is 767 bytes +create table t3 (col1 text, index(col1(768))) character set = latin1 engine = innodb; -ERROR HY000: Can't create table './test/t3.frm' (errno: 139) -create table t4 (col1 text, index(col1(768))) +Warnings: +Warning 1071 Specified key was too long; max key length is 767 bytes +create table t4 (col1 blob, index(col1(768))) character set = latin1 engine = innodb; -ERROR HY000: Can't create table './test/t4.frm' (errno: 139) -create table t5 (col1 blob, index(col1(768))) +Warnings: +Warning 1071 Specified key was too long; max key length is 767 bytes +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `col1` varchar(768) default NULL, + KEY `col1` (`col1`(767)) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +drop table t1, t2, t3, t4; +create table t1 (col1 varchar(768) primary key) character set = latin1 engine = innodb; -ERROR HY000: Can't create table './test/t5.frm' (errno: 139) +ERROR 42000: Specified key was too long; max key length is 767 bytes +create table t2 (col1 varbinary(768) primary key) +character set = latin1 engine = innodb; +ERROR 42000: Specified key was too long; max key length is 767 bytes +create table t3 (col1 text, primary key(col1(768))) +character set = latin1 engine = innodb; +ERROR 42000: Specified key was too long; max key length is 767 bytes +create table t4 (col1 blob, primary key(col1(768))) +character set = latin1 engine = innodb; +ERROR 42000: Specified key was too long; max key length is 767 bytes CREATE TABLE t1 ( id INT PRIMARY KEY @@ -2772,6 +2801,38 @@ insert into t2 values (4,_ucs2 0x05612020,_ucs2 0x05612020,'taken'); drop table t1; drop table t2; commit; +set foreign_key_checks=0; +create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb; +create table t1(a char(10) primary key, b varchar(20)) engine = innodb; +ERROR HY000: Can't create table './test/t1.frm' (errno: 150) +set foreign_key_checks=1; +drop table t2; +set foreign_key_checks=0; +create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8; +ERROR HY000: Can't create table './test/t2.frm' (errno: 150) +set foreign_key_checks=1; +drop table t1; +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb; +create table t1(a varchar(10) primary key) engine = innodb; +alter table t1 modify column a int; +Got one of the listed errors +set foreign_key_checks=1; +drop table t2,t1; +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1; +create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1; +alter table t1 convert to character set utf8; +set foreign_key_checks=1; +drop table t2,t1; +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1; +create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8; +rename table t3 to t1; +ERROR HY000: Error on rename of './test/t3' to './test/t1' (errno: 150) +set foreign_key_checks=1; +drop table t2,t3; create table t1 (a varchar(255) character set utf8, b varchar(255) character set utf8, c varchar(255) character set utf8, @@ -2785,4 +2846,3 @@ d varchar(255) character set utf8, e varchar(255) character set utf8, key (a,b,c,d,e)) engine=innodb; ERROR 42000: Specified key was too long; max key length is 3072 bytes -End of 5.0 tests diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index a73ecf7c3eb..735deba2b05 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1356,8 +1356,8 @@ source include/varchar.inc; # Clean up filename -- embedded server reports whole path without .frm, # regular server reports relative path with .frm (argh!) --replace_result \\ / $MYSQL_TEST_DIR . /var/master-data/ / t1.frm t1 ---error 1005 create table t1 (v varchar(65530), key(v)); +drop table t1; create table t1 (v varchar(65536)); show create table t1; drop table t1; @@ -1485,7 +1485,7 @@ CREATE TEMPORARY TABLE t2 DROP TABLE t1; # -# Test that index column max sizes are checked (bug #13315) +# Test that index column max sizes are honored (bug #13315) # # prefix index @@ -1512,22 +1512,36 @@ create table t8 (col1 blob, index(col1(767))) create table t9 (col1 varchar(512), col2 varchar(512), index(col1, col2)) character set = latin1 engine = innodb; +show create table t9; + drop table t1, t2, t3, t4, t5, t6, t7, t8, t9; ---error 1005 -create table t1 (col1 varchar(768), index (col1)) +# these should have their index length trimmed +create table t1 (col1 varchar(768), index(col1)) character set = latin1 engine = innodb; ---error 1005 -create table t2 (col1 varchar(768) primary key) +create table t2 (col1 varbinary(768), index(col1)) character set = latin1 engine = innodb; ---error 1005 -create table t3 (col1 varbinary(768) primary key) +create table t3 (col1 text, index(col1(768))) character set = latin1 engine = innodb; ---error 1005 -create table t4 (col1 text, index(col1(768))) +create table t4 (col1 blob, index(col1(768))) character set = latin1 engine = innodb; ---error 1005 -create table t5 (col1 blob, index(col1(768))) + +show create table t1; + +drop table t1, t2, t3, t4; + +# these should be refused +--error 1071 +create table t1 (col1 varchar(768) primary key) + character set = latin1 engine = innodb; +--error 1071 +create table t2 (col1 varbinary(768) primary key) + character set = latin1 engine = innodb; +--error 1071 +create table t3 (col1 text, primary key(col1(768))) + character set = latin1 engine = innodb; +--error 1071 +create table t4 (col1 blob, primary key(col1(768))) character set = latin1 engine = innodb; # @@ -1752,6 +1766,56 @@ drop table t1; drop table t2; commit; +# tests for bugs #9802 and #13778 + +# test that FKs between invalid types are not accepted + +set foreign_key_checks=0; +create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb; +-- error 1005 +create table t1(a char(10) primary key, b varchar(20)) engine = innodb; +set foreign_key_checks=1; +drop table t2; + +# test that FKs between different charsets are not accepted in CREATE even +# when f_k_c is 0 + +set foreign_key_checks=0; +create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1; +-- error 1005 +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8; +set foreign_key_checks=1; +drop table t1; + +# test that invalid datatype conversions with ALTER are not allowed + +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb; +create table t1(a varchar(10) primary key) engine = innodb; +-- error 1025,1025 +alter table t1 modify column a int; +set foreign_key_checks=1; +drop table t2,t1; + +# test that charset conversions with ALTER are allowed when f_k_c is 0 + +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1; +create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1; +alter table t1 convert to character set utf8; +set foreign_key_checks=1; +drop table t2,t1; + +# test that RENAME does not allow invalid charsets when f_k_c is 0 + +set foreign_key_checks=0; +create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=latin1; +create table t3(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=utf8; +-- error 1025 +rename table t3 to t1; +set foreign_key_checks=1; +drop table t2,t3; + # # Test that we can create a large (>1K) key # diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 56e5fd8923f..6c8dc9f81f6 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -2519,6 +2519,12 @@ ha_innobase::open( DBUG_RETURN(0); } +uint +ha_innobase::max_supported_key_part_length() const +{ + return(DICT_MAX_INDEX_COL_LEN - 1); +} + /********************************************************************** Closes a handle to an InnoDB table. */ @@ -4675,6 +4681,9 @@ create_index( 0, prefix_len); } + /* Even though we've defined max_supported_key_part_length, we + still do our own checking using field_lengths to be absolutely + sure we don't create too long indexes. */ error = row_create_index_for_mysql(index, trx, field_lengths); error = convert_error_code_to_mysql(error, NULL); diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index eb4e10e545f..58051624f89 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -110,7 +110,7 @@ class ha_innobase: public handler but currently MySQL does not work with keys whose size is > MAX_KEY_LENGTH */ uint max_supported_key_length() const { return 3500; } - uint max_supported_key_part_length() const { return 3500; } + uint max_supported_key_part_length() const; const key_map *keys_to_use_for_scanning() { return &key_map_full; } bool has_transactions() { return 1;} From 712385568fde05683c330e0453886d15843cf5b5 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Wed, 7 Dec 2005 00:57:15 +0300 Subject: [PATCH 8/9] A fix and a test case for Bug#15392 "Server crashes during prepared statement execute --- mysql-test/r/sp.result | 39 +++++++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 46 ++++++++++++++++++++++++++++++++++++++++++ sql/sp_head.cc | 21 ++++++++++--------- 3 files changed, 96 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 6f0a8623a4c..c78ae13d8ee 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4153,4 +4153,43 @@ A local variable in a nested compound statement takes precedence over table colu a - local variable in a nested compound statement A local variable in a nested compound statement takes precedence over table column in cursors a - local variable in a nested compound statement +drop schema if exists mysqltest1| +Warnings: +Note 1008 Can't drop database 'mysqltest1'; database doesn't exist +drop schema if exists mysqltest2| +Warnings: +Note 1008 Can't drop database 'mysqltest2'; database doesn't exist +drop schema if exists mysqltest3| +Warnings: +Note 1008 Can't drop database 'mysqltest3'; database doesn't exist +create schema mysqltest1| +create schema mysqltest2| +create schema mysqltest3| +use mysqltest3| +create procedure mysqltest1.p1 (out prequestid varchar(100)) +begin +call mysqltest2.p2('call mysqltest3.p3(1, 2)'); +end| +create procedure mysqltest2.p2(in psql text) +begin +declare lsql text; +set @lsql= psql; +prepare lstatement from @lsql; +execute lstatement; +deallocate prepare lstatement; +end| +create procedure mysqltest3.p3(in p1 int) +begin +select p1; +end| +call mysqltest1.p1(@rs)| +ERROR 42000: Incorrect number of arguments for PROCEDURE mysqltest3.p3; expected 1, got 2 +call mysqltest1.p1(@rs)| +ERROR 42000: Incorrect number of arguments for PROCEDURE mysqltest3.p3; expected 1, got 2 +call mysqltest1.p1(@rs)| +ERROR 42000: Incorrect number of arguments for PROCEDURE mysqltest3.p3; expected 1, got 2 +drop schema if exists mysqltest1| +drop schema if exists mysqltest2| +drop schema if exists mysqltest3| +use test| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 81f7609a468..b3760a1d7f5 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4947,6 +4947,52 @@ begin end| call p1("a - stored procedure parameter")| +# +# A test case for Bug#15392 "Server crashes during prepared statement +# execute": make sure that stored procedure check for error conditions +# properly and do not continue execution if an error has been set. +# +# It's necessary to use several DBs because in the original code +# the successful return of mysql_change_db overrode the error from +# execution. +drop schema if exists mysqltest1| +drop schema if exists mysqltest2| +drop schema if exists mysqltest3| +create schema mysqltest1| +create schema mysqltest2| +create schema mysqltest3| +use mysqltest3| + +create procedure mysqltest1.p1 (out prequestid varchar(100)) +begin + call mysqltest2.p2('call mysqltest3.p3(1, 2)'); +end| + +create procedure mysqltest2.p2(in psql text) +begin + declare lsql text; + set @lsql= psql; + prepare lstatement from @lsql; + execute lstatement; + deallocate prepare lstatement; +end| + +create procedure mysqltest3.p3(in p1 int) +begin + select p1; +end| + +--error ER_SP_WRONG_NO_OF_ARGS +call mysqltest1.p1(@rs)| +--error ER_SP_WRONG_NO_OF_ARGS +call mysqltest1.p1(@rs)| +--error ER_SP_WRONG_NO_OF_ARGS +call mysqltest1.p1(@rs)| +drop schema if exists mysqltest1| +drop schema if exists mysqltest2| +drop schema if exists mysqltest3| +use test| + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ff4f898924c..fcd220353fc 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1137,10 +1137,12 @@ int sp_head::execute(THD *thd) original thd->db will then have been freed */ if (dbchanged) { - /* No access check when changing back to where we came from. - (It would generate an error from mysql_change_db() when olddb=="") */ + /* + No access check when changing back to where we came from. + (It would generate an error from mysql_change_db() when olddb=="") + */ if (! thd->killed) - ret= mysql_change_db(thd, olddb, 1); + ret|= (int) mysql_change_db(thd, olddb, 1); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", ("first free for 0x%lx --: 0x%lx->0x%lx, level: %lu, flags %x", @@ -1519,13 +1521,12 @@ int sp_head::execute_procedure(THD *thd, List *args) suv= new Item_func_set_user_var(guv->get_name(), item); /* - we do not check suv->fixed, because it can't be fixed after - creation + Item_func_set_user_var is not fixed after construction, + call fix_fields(). */ - suv->fix_fields(thd, &item); - suv->fix_length_and_dec(); - suv->check(); - suv->update(); + if ((ret= test(!suv || suv->fix_fields(thd, &item) || + suv->check() || suv->update()))) + break; } } } @@ -2097,7 +2098,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, cleanup_items() is called in sp_head::execute() */ - return res; + return res || thd->net.report_error; } From 642984d420c5851729cb2c0dab81febc267239f0 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Tue, 6 Dec 2005 18:18:35 -0800 Subject: [PATCH 9/9] Fix innodb.result file (was missing a line) --- mysql-test/r/innodb.result | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 8a01d394e78..0e0e6a20770 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -2846,3 +2846,4 @@ d varchar(255) character set utf8, e varchar(255) character set utf8, key (a,b,c,d,e)) engine=innodb; ERROR 42000: Specified key was too long; max key length is 3072 bytes +End of 5.0 tests