From 05826af9268264fd2195381b491a72bcf426d531 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Thu, 16 Feb 2006 13:40:37 +0100 Subject: [PATCH 1/7] Fixed BUG#17015: Routine name truncation not an error The name length was checked "the old way", not taking charsets into account, when creating a stored routine. Fixing this enforces the real limit (64 characters) again, and no truncation is possible. --- mysql-test/r/sp-error.result | 12 ++++++++++-- mysql-test/t/sp-error.test | 19 ++++++++++++++++--- sql/sp.cc | 7 ++++++- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index c0e02cbeb6f..cc09461edc1 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -647,10 +647,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/t/sp-error.test b/mysql-test/t/sp-error.test index c4745537de3..9f98b776633 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -926,12 +926,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 @@ -1721,4 +1735,3 @@ create aggregate function bug16896() returns int return 1; #drop procedure if exists bugNNNN| #--enable_warnings #create procedure bugNNNN... - diff --git a/sql/sp.cc b/sql/sp.cc index fe249141fea..53218aece56 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -508,7 +508,12 @@ 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]->charset()->mbmaxlen) > + table->field[MYSQL_PROC_FIELD_NAME]->field_length) { ret= SP_BAD_IDENTIFIER; goto done; From 61f2dc713b5ad13cd1ccea4fc38614bfa748199a Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Fri, 10 Mar 2006 14:04:56 +0100 Subject: [PATCH 2/7] Fixed BUG#16474: SP crashed MySQL fix_fields() was not called for "order by" variables if the type was a "constant integer", and thus interpreted as a column index. However, a local variable is an expression and should not be interpreted as a column index. Instead it behaves just like when using a user variable for instance (i.e. it will not affect the ordering). --- mysql-test/r/sp.result | 29 +++++++++++++++++++++++++++++ mysql-test/t/sp.test | 31 +++++++++++++++++++++++++++++++ sql/sql_select.cc | 6 +++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index b03c49b72e7..f19dbbd2689 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4857,4 +4857,33 @@ i 0 drop table t3| drop procedure bug16887| +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| +delete from t1| drop table t1,t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index e6823693b3d..af7ce57b252 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5717,6 +5717,37 @@ drop table t3| drop procedure bug16887| +# +# 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| +delete from t1| + + # # BUG#NNNN: New bug synopsis # diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 768ae7bf71f..eb92bd1177b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12325,7 +12325,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 must be fixed.) + */ + if (order_item->type() == Item::INT_ITEM && !order_item->is_splocal()) { /* Order by position */ uint count= (uint) order_item->val_int(); if (!count || count > fields.elements) From 71defb7a8020e0797cdc3b7b68c1c96adcb7a4e6 Mon Sep 17 00:00:00 2001 From: "konstantin@mysql.com" <> Date: Tue, 28 Mar 2006 15:06:29 +0400 Subject: [PATCH 3/7] A fix and a test case for Bug#15683 "crash, Function on nested VIEWs, Prepared statement": we didn't mark the nested views as 'prelockng placeholders' when building the prelocking list. This resulted in these views being processed (merged, materialized) before they are actually used. --- mysql-test/r/sp-prelocking.result | 23 +++++++++++++++++++++++ mysql-test/t/sp-prelocking.test | 31 +++++++++++++++++++++++++++++++ sql/sql_view.cc | 1 + 3 files changed, 55 insertions(+) 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/t/sp-prelocking.test b/mysql-test/t/sp-prelocking.test index 049dcc576dd..a7215462afb 100644 --- a/mysql-test/t/sp-prelocking.test +++ b/mysql-test/t/sp-prelocking.test @@ -241,3 +241,34 @@ 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; + diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 2832adc1f8f..39d1ae5c9fb 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -938,6 +938,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, From b310d4fb4d4fb6cf109e95f68192b5532318ae22 Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Tue, 28 Mar 2006 14:16:21 +0200 Subject: [PATCH 4/7] Post review fixes for BUG#16474: SP crashed MySQL. --- mysql-test/r/ps.result | 46 ++++++++++++++++++++++++++++++++++++++++++ mysql-test/r/sp.result | 6 ++++++ mysql-test/t/ps.test | 34 +++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 5 +++++ sql/sql_select.cc | 5 +++-- 5 files changed, 94 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 4d108e06356..9afbca9dcb1 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.result b/mysql-test/r/sp.result index f19dbbd2689..0c9b64f37fa 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4885,5 +4885,11 @@ 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/t/ps.test b/mysql-test/t/ps.test index d6b239c31bf..285b5fb0aa3 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.test b/mysql-test/t/sp.test index af7ce57b252..1d1272b0b2a 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5745,6 +5745,11 @@ 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| diff --git a/sql/sql_select.cc b/sql/sql_select.cc index eb92bd1177b..e31a2068dee 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12327,9 +12327,10 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, /* Local SP variables may be int but are expressions, not positions. - (And they must be fixed.) + (And they can't be used before fix_fields is called for them). */ - if (order_item->type() == Item::INT_ITEM && !order_item->is_splocal()) + // if (order_item->type() == Item::INT_ITEM && !order_item->is_splocal()) + 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) From e51adff01c4969a36ecad010b64e47b04550648e Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Tue, 28 Mar 2006 15:06:06 +0200 Subject: [PATCH 5/7] Review fix for BUG#17015: Routine name truncation not an error --- sql/field.h | 6 ++++++ sql/sp.cc | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sql/field.h b/sql/field.h index 218308ead13..c32f365ec92 100644 --- a/sql/field.h +++ b/sql/field.h @@ -319,6 +319,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 Field::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 53218aece56..93dfd68cef3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -509,11 +509,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) goto done; } - 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]->charset()->mbmaxlen) > - 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; From cc17bb34041fe40d94dcac1ae88d4ce1c884ee2f Mon Sep 17 00:00:00 2001 From: "pem@mysql.com" <> Date: Tue, 28 Mar 2006 16:05:06 +0200 Subject: [PATCH 6/7] Removed forgotten comment line in sql_select.cc. --- sql/sql_select.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 72aa4b5bdad..2e60bdb96f0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12337,7 +12337,6 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, 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->is_splocal()) if (order_item->type() == Item::INT_ITEM && order_item->basic_const_item()) { /* Order by position */ uint count= (uint) order_item->val_int(); From 17785d169bdc4e75151469a0b101378dd49524d2 Mon Sep 17 00:00:00 2001 From: "dlenev@mysql.com" <> Date: Wed, 29 Mar 2006 14:53:00 +0400 Subject: [PATCH 7/7] Proposed fix for bug #17764 "Trigger crashes MyISAM table" A table with an on insert trigger was reported as crashed when the insert was processed with bulk insert mode on (handler::start_bulk_insert). The trigger was also selecting from the same table, and that caused the "crash". The same problem was present when an insert statement, which was processed in bulk mode, also used a stored function that was reading the same table. This fix disables bulk inserts if a statement uses functions or invokes triggers. Implementing more granular checks will require much more code and therefore can hardly be done in 5.0 --- mysql-test/r/trigger.result | 24 ++++++++++++++++++++++++ mysql-test/t/trigger.test | 34 ++++++++++++++++++++++++++++++++++ sql/sql_insert.cc | 23 ++++++++++++++++------- sql/sql_load.cc | 5 +++-- 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index c9a91758336..711399418ac 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; @@ -897,3 +898,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/trigger.test b/mysql-test/t/trigger.test index 1d68b519f1d..d8e2bd7c1cb 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 @@ -1057,3 +1058,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/sql_insert.cc b/sql/sql_insert.cc index eb74144c1ea..6383e87a1c8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -404,11 +404,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; @@ -534,7 +538,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; @@ -2181,7 +2185,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 @@ -2189,6 +2193,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); } @@ -2229,7 +2235,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); } @@ -2332,7 +2339,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) (example: we got a duplicate key @@ -2367,7 +2375,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); /* @@ -2450,7 +2458,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 cc724c102a4..0a667c887ef 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -356,7 +356,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; @@ -373,7 +374,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;