diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index e24cee9e875..6db5e8f8157 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -880,3 +880,49 @@ select row_count(); row_count() 1 drop table t1; +create table t1 (a int, b int); +insert into t1 (a,b) values (2,8),(1,9),(3,7); +prepare stmt from "select * from t1 order by ?"; +execute stmt using @a; +a b +2 8 +1 9 +3 7 +set @a=1; +execute stmt using @a; +a b +1 9 +2 8 +3 7 +set @a=2; +execute stmt using @a; +a b +3 7 +2 8 +1 9 +deallocate prepare stmt; +select * from t1 order by 1; +a b +1 9 +2 8 +3 7 +prepare stmt from "select * from t1 order by ?+1"; +set @a=0; +execute stmt using @a; +a b +2 8 +1 9 +3 7 +set @a=1; +execute stmt using @a; +a b +2 8 +1 9 +3 7 +deallocate prepare stmt; +select * from t1 order by 1+1; +a b +2 8 +1 9 +3 7 +drop table t1; diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 40fe0b09814..931fa2ee402 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -634,10 +634,18 @@ flush tables; return 5; end| ERROR 0A000: FLUSH is not allowed in stored function or trigger -create procedure bug9529_90123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123() +create procedure bug9529_901234567890123456789012345678901234567890123456789012345() begin end| -ERROR 42000: Identifier name 'bug9529_90123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' is too long +ERROR 42000: Identifier name 'bug9529_901234567890123456789012345678901234567890123456789012345' is too long +drop procedure if exists bug17015_0123456789012345678901234567890123456789012345678901234| +create procedure bug17015_0123456789012345678901234567890123456789012345678901234() +begin +end| +show procedure status like 'bug17015%'| +Db Name Type Definer Modified Created Security_type Comment +test bug17015_0123456789012345678901234567890123456789012345678901234 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER +drop procedure bug17015_0123456789012345678901234567890123456789012345678901234| drop procedure if exists bug10969| create procedure bug10969() begin diff --git a/mysql-test/r/sp-prelocking.result b/mysql-test/r/sp-prelocking.result index c2614892016..2335513b28a 100644 --- a/mysql-test/r/sp-prelocking.result +++ b/mysql-test/r/sp-prelocking.result @@ -214,3 +214,26 @@ drop function f1; drop function f2; drop function f3; drop procedure sp1; +drop table if exists t1; +drop view if exists v1, v2, v3; +drop function if exists bug15683; +create table t1 (f1 bigint, f2 varchar(20), f3 bigint); +insert into t1 set f1 = 1, f2 = 'schoenenbourg', f3 = 1; +create view v1 as select 1 from t1 union all select 1; +create view v2 as select 1 from v1; +create view v3 as select 1 as f1 from v2; +create function bug15683() returns bigint +begin +return (select count(*) from v3); +end| +prepare stmt from "select bug15683()"; +execute stmt; +bug15683() +2 +execute stmt; +bug15683() +2 +deallocate prepare stmt; +drop table t1; +drop view v1, v2, v3; +drop function bug15683; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index f31facf6196..c75bce6580f 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4812,4 +4812,39 @@ f1 bug13575(f1) 3 ccc drop function bug13575; drop table t3| +drop procedure if exists bug16474_1| +drop procedure if exists bug16474_2| +delete from t1| +insert into t1 values ('c', 2), ('b', 3), ('a', 1)| +create procedure bug16474_1() +begin +declare x int; +select id from t1 order by x; +end| +create procedure bug16474_2(x int) +select id from t1 order by x| +call bug16474_1()| +id +c +b +a +call bug16474_2(1)| +id +c +b +a +call bug16474_2(2)| +id +c +b +a +drop procedure bug16474_1| +drop procedure bug16474_2| +set @x = 2| +select * from t1 order by @x| +id data +c 2 +b 3 +a 1 +delete from t1| drop table t1,t2; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index dad6f21dffc..ac451c5c34d 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -2,6 +2,7 @@ drop table if exists t1, t2, t3, t4; drop view if exists v1; drop database if exists mysqltest; drop function if exists f1; +drop function if exists f2; drop procedure if exists p1; create table t1 (i int); create trigger trg before insert on t1 for each row set @a:=1; @@ -928,3 +929,26 @@ create trigger t1_bi before insert on t1 for each row return 0; ERROR 42000: RETURN is only allowed in a FUNCTION insert into t1 values (1); drop table t1; +create table t1 (a varchar(64), b int); +create table t2 like t1; +create trigger t1_ai after insert on t1 for each row +set @a:= (select max(a) from t1); +insert into t1 (a) values +("Twas"),("brillig"),("and"),("the"),("slithy"),("toves"), +("Did"),("gyre"),("and"),("gimble"),("in"),("the"),("wabe"); +create trigger t2_ai after insert on t2 for each row +set @a:= (select max(a) from t2); +insert into t2 select * from t1; +load data infile '../std_data_ln/words.dat' into table t1 (a); +drop trigger t1_ai; +drop trigger t2_ai; +create function f1() returns int return (select max(b) from t1); +insert into t1 values +("All",f1()),("mimsy",f1()),("were",f1()),("the",f1()),("borogoves",f1()), +("And",f1()),("the",f1()),("mome", f1()),("raths",f1()),("outgrabe",f1()); +create function f2() returns int return (select max(b) from t2); +insert into t2 select a, f2() from t1; +load data infile '../std_data_ln/words.dat' into table t1 (a) set b:= f1(); +drop table t1; +drop function f1; +drop function f2; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index ffbba991fb6..b54efcad3a3 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -933,4 +933,38 @@ execute ins_call; select row_count(); drop table t1; +# +# BUG#16474: SP crashed MySQL +# (when using "order by localvar", where 'localvar' is just that. +# The actual bug test is in sp.test, this is just testing that we get the +# expected result for prepared statements too, i.e. place holders work as +# textual substitution. If it's a single integer, it works as the (deprecated) +# "order by column#", otherwise it's an expression. +# +create table t1 (a int, b int); +insert into t1 (a,b) values (2,8),(1,9),(3,7); + +# Will order by index +prepare stmt from "select * from t1 order by ?"; +execute stmt using @a; +set @a=1; +execute stmt using @a; +set @a=2; +execute stmt using @a; +deallocate prepare stmt; +# For reference: +select * from t1 order by 1; + +# Will not order by index. +prepare stmt from "select * from t1 order by ?+1"; +set @a=0; +execute stmt using @a; +set @a=1; +execute stmt using @a; +deallocate prepare stmt; +# For reference: +select * from t1 order by 1+1; + +drop table t1; + # End of 5.0 tests diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index da40cdf643a..c9ce26b6dda 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -904,12 +904,26 @@ end| # # BUG#9529: Stored Procedures: No Warning on truncation of procedure name # during creation. -# Note: When using utf8 for mysql.proc, this limit is much higher than before +# BUG#17015: Routine name truncation not an error +# When we started using utf8 for mysql.proc, this limit appeared +# to be higher, but in reality the names were truncated. --error ER_TOO_LONG_IDENT -create procedure bug9529_90123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123() +create procedure bug9529_901234567890123456789012345678901234567890123456789012345() begin end| +--disable_warnings +drop procedure if exists bug17015_0123456789012345678901234567890123456789012345678901234| +--enable_warnings +# Check the upper limit, just to make sure. +create procedure bug17015_0123456789012345678901234567890123456789012345678901234() +begin +end| + +--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00' +show procedure status like 'bug17015%'| +drop procedure bug17015_0123456789012345678901234567890123456789012345678901234| + # # BUG#10969: Stored procedures: crash if default() function @@ -1717,4 +1731,3 @@ end| #drop procedure if exists bugNNNN| #--enable_warnings #create procedure bugNNNN... - diff --git a/mysql-test/t/sp-prelocking.test b/mysql-test/t/sp-prelocking.test index edb8ae5b92d..6287950d216 100644 --- a/mysql-test/t/sp-prelocking.test +++ b/mysql-test/t/sp-prelocking.test @@ -240,3 +240,37 @@ drop function f1; drop function f2; drop function f3; drop procedure sp1; + +# +# Bug#15683 "crash, Function on nested VIEWs, Prepared statement" +# Check that when creating the prelocking list a nested view +# is not merged until it's used. +# +--disable_warnings +drop table if exists t1; +drop view if exists v1, v2, v3; +drop function if exists bug15683; +--enable_warnings +create table t1 (f1 bigint, f2 varchar(20), f3 bigint); +insert into t1 set f1 = 1, f2 = 'schoenenbourg', f3 = 1; +create view v1 as select 1 from t1 union all select 1; +create view v2 as select 1 from v1; +create view v3 as select 1 as f1 from v2; + +delimiter |; +create function bug15683() returns bigint +begin +return (select count(*) from v3); +end| +delimiter ;| + +prepare stmt from "select bug15683()"; +execute stmt; +execute stmt; +deallocate prepare stmt; +drop table t1; +drop view v1, v2, v3; +drop function bug15683; +# +# End of 5.0 tests +# diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 3b1ee7f5076..fe306126e45 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5653,6 +5653,42 @@ select distinct f1, bug13575(f1) from t3 order by f1| drop function bug13575; drop table t3| +# +# BUG#16474: SP crashed MySQL +# (when using "order by localvar", where 'localvar' is just that. +# +--disable_warnings +drop procedure if exists bug16474_1| +drop procedure if exists bug16474_2| +--enable_warnings + +delete from t1| +insert into t1 values ('c', 2), ('b', 3), ('a', 1)| + +create procedure bug16474_1() +begin + declare x int; + + select id from t1 order by x; +end| + +# This does NOT order by column index; variable is an expression. +create procedure bug16474_2(x int) + select id from t1 order by x| + +call bug16474_1()| +call bug16474_2(1)| +call bug16474_2(2)| +drop procedure bug16474_1| +drop procedure bug16474_2| + +# For reference: user variables are expressions too and do not affect ordering. +set @x = 2| +select * from t1 order by @x| + +delete from t1| + + # # BUG#NNNN: New bug synopsis # diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 05a52007eeb..c5925bbd9d5 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -7,6 +7,7 @@ drop table if exists t1, t2, t3, t4; drop view if exists v1; drop database if exists mysqltest; drop function if exists f1; +drop function if exists f2; drop procedure if exists p1; --enable_warnings @@ -1080,3 +1081,36 @@ create table t1 (i int); create trigger t1_bi before insert on t1 for each row return 0; insert into t1 values (1); drop table t1; + +# Test for bug #17764 "Trigger crashes MyISAM table" +# +# Table was reported as crashed when it was subject table of trigger invoked +# by insert statement which was executed with enabled bulk insert mode (which +# is actually set of optimizations enabled by handler::start_bulk_insert()) +# and this trigger also explicitly referenced it. +# The same problem arose when table to which bulk insert was done was also +# referenced in function called by insert statement. +create table t1 (a varchar(64), b int); +create table t2 like t1; +create trigger t1_ai after insert on t1 for each row + set @a:= (select max(a) from t1); +insert into t1 (a) values + ("Twas"),("brillig"),("and"),("the"),("slithy"),("toves"), + ("Did"),("gyre"),("and"),("gimble"),("in"),("the"),("wabe"); +create trigger t2_ai after insert on t2 for each row + set @a:= (select max(a) from t2); +insert into t2 select * from t1; +load data infile '../std_data_ln/words.dat' into table t1 (a); +drop trigger t1_ai; +drop trigger t2_ai; +# Test that the problem for functions is fixed as well +create function f1() returns int return (select max(b) from t1); +insert into t1 values + ("All",f1()),("mimsy",f1()),("were",f1()),("the",f1()),("borogoves",f1()), + ("And",f1()),("the",f1()),("mome", f1()),("raths",f1()),("outgrabe",f1()); +create function f2() returns int return (select max(b) from t2); +insert into t2 select a, f2() from t1; +load data infile '../std_data_ln/words.dat' into table t1 (a) set b:= f1(); +drop table t1; +drop function f1; +drop function f2; diff --git a/sql/field.h b/sql/field.h index 3fbdc0d0aa9..8829aac65bd 100644 --- a/sql/field.h +++ b/sql/field.h @@ -350,6 +350,12 @@ public: /* convert decimal to longlong with overflow check */ longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag, int *err); + /* The max. number of characters */ + inline uint32 char_length() const + { + return field_length / charset()->mbmaxlen; + } + friend bool reopen_table(THD *,struct st_table *,bool); friend int cre_myisam(my_string name, register TABLE *form, uint options, ulonglong auto_increment_value); diff --git a/sql/sp.cc b/sql/sp.cc index ac63847daa3..7fc388860d1 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -535,7 +535,11 @@ db_create_routine(THD *thd, int type, sp_head *sp) ret= SP_GET_FIELD_FAILED; goto done; } - if (sp->m_name.length > table->field[MYSQL_PROC_FIELD_NAME]->field_length) + + if (system_charset_info->cset->numchars(system_charset_info, + sp->m_name.str, + sp->m_name.str+sp->m_name.length) > + table->field[MYSQL_PROC_FIELD_NAME]->char_length()) { ret= SP_BAD_IDENTIFIER; goto done; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 168d3950830..5cb74fd32ee 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -419,11 +419,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, let's *try* to start bulk inserts. It won't necessary start them as values_list.elements should be greater than some - handler dependent - threshold. + We should not start bulk inserts if this statement uses + functions or invokes triggers since they may access + to the same table and therefore should not see its + inconsistent state created by this optimization. So we call start_bulk_insert to perform nesessary checks on values_list.elements, and - if nothing else - to initialize the code to make the call of end_bulk_insert() below safe. */ - if (lock_type != TL_WRITE_DELAYED) + if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode) table->file->start_bulk_insert(values_list.elements); thd->no_trans_update= 0; @@ -549,7 +553,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, else #endif { - if (table->file->end_bulk_insert() && !error) + if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) { table->file->print_error(my_errno,MYF(0)); error=1; @@ -2230,7 +2234,7 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) lex->current_select->options|= OPTION_BUFFER_RESULT; lex->current_select->join->select_options|= OPTION_BUFFER_RESULT; } - else + else if (!thd->prelocked_mode) { /* We must not yet prepare the result table if it is the same as one of the @@ -2238,6 +2242,8 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) indexes on the result table, which may be used during the select, if it is the same table (Bug #6034). Do the preparation after the select phase in select_insert::prepare2(). + We won't start bulk inserts at all if this statement uses functions or + should invoke triggers since they may access to the same table too. */ table->file->start_bulk_insert((ha_rows) 0); } @@ -2288,7 +2294,8 @@ select_insert::prepare(List &values, SELECT_LEX_UNIT *u) int select_insert::prepare2(void) { DBUG_ENTER("select_insert::prepare2"); - if (thd->lex->current_select->options & OPTION_BUFFER_RESULT) + if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && + !thd->prelocked_mode) table->file->start_bulk_insert((ha_rows) 0); DBUG_RETURN(0); } @@ -2391,7 +2398,8 @@ void select_insert::send_error(uint errcode,const char *err) */ DBUG_VOID_RETURN; } - table->file->end_bulk_insert(); + if (!thd->prelocked_mode) + table->file->end_bulk_insert(); /* If at least one row has been inserted/modified and will stay in the table (the table doesn't have transactions) we must write to the binlog (and @@ -2447,7 +2455,7 @@ bool select_insert::send_eof() int error,error2; DBUG_ENTER("select_insert::send_eof"); - error=table->file->end_bulk_insert(); + error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); /* @@ -2556,7 +2564,8 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) thd->cuted_fields=0; if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - table->file->start_bulk_insert((ha_rows) 0); + if (!thd->prelocked_mode) + table->file->start_bulk_insert((ha_rows) 0); thd->no_trans_update= 0; thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 9fcdc969597..599480e616c 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -367,7 +367,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (ignore || handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - table->file->start_bulk_insert((ha_rows) 0); + if (!thd->prelocked_mode) + table->file->start_bulk_insert((ha_rows) 0); table->copy_blobs=1; thd->no_trans_update= 0; @@ -384,7 +385,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error= read_sep_field(thd, info, table_list, fields_vars, set_fields, set_values, read_info, *enclosed, skip_lines, ignore); - if (table->file->end_bulk_insert() && !error) + if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) { table->file->print_error(my_errno, MYF(0)); error= 1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 59c7436d85c..02eb4d72d5c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12410,7 +12410,11 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, Item **select_item; /* The corresponding item from the SELECT clause. */ Field *from_field; /* The corresponding field from the FROM clause. */ - if (order_item->type() == Item::INT_ITEM) + /* + Local SP variables may be int but are expressions, not positions. + (And they can't be used before fix_fields is called for them). + */ + if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item()) { /* Order by position */ uint count= (uint) order_item->val_int(); if (!count || count > fields.elements) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 1b5e91daf3e..18217094ff7 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -940,6 +940,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) tbl->skip_temporary= 1; tbl->belong_to_view= top_view; tbl->referencing_view= table; + tbl->prelocking_placeholder= table->prelocking_placeholder; /* First we fill want_privilege with SELECT_ACL (this is needed for the tables which belongs to view subqueries and temporary table views,