diff options
-rw-r--r-- | client/mysqltest.c | 6 | ||||
-rw-r--r-- | include/my_global.h | 9 | ||||
-rwxr-xr-x | mysql-test/mysql-test-run.pl | 3 | ||||
-rw-r--r-- | mysql-test/mysql-test-run.sh | 1 | ||||
-rw-r--r-- | mysql-test/r/index_merge.result | 22 | ||||
-rw-r--r-- | mysql-test/r/merge.result | 2 | ||||
-rw-r--r-- | mysql-test/r/ps.result | 14 | ||||
-rw-r--r-- | mysql-test/r/trigger.result | 103 | ||||
-rw-r--r-- | mysql-test/r/view.result | 10 | ||||
-rw-r--r-- | mysql-test/t/index_merge.test | 26 | ||||
-rw-r--r-- | mysql-test/t/ps.test | 21 | ||||
-rw-r--r-- | mysql-test/t/trigger.test | 87 | ||||
-rw-r--r-- | mysql-test/t/view.test | 15 | ||||
-rw-r--r-- | sql/field.cc | 9 | ||||
-rw-r--r-- | sql/field.h | 5 | ||||
-rw-r--r-- | sql/ha_myisammrg.cc | 22 | ||||
-rw-r--r-- | sql/sql_base.cc | 3 | ||||
-rw-r--r-- | sql/sql_rename.cc | 28 | ||||
-rw-r--r-- | sql/sql_select.h | 11 | ||||
-rw-r--r-- | sql/sql_show.cc | 3 | ||||
-rw-r--r-- | sql/sql_table.cc | 21 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 286 | ||||
-rw-r--r-- | sql/sql_trigger.h | 17 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 14 | ||||
-rw-r--r-- | storage/myisammrg/myrg_queue.c | 20 |
25 files changed, 698 insertions, 60 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c index 1450ee95c19..5777afaba4c 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3662,8 +3662,10 @@ DYNAMIC_ARRAY patterns; static void init_win_path_patterns() { /* List of string patterns to match in order to find paths */ - const char* paths[] = { "$MYSQL_TEST_DIR", "./test/", 0 }; - int num_paths= 2; + const char* paths[] = { "$MYSQL_TEST_DIR", + "$MYSQL_TMP_DIR", + "./test/", 0 }; + int num_paths= 3; int i; char* p; diff --git a/include/my_global.h b/include/my_global.h index 82b0585757c..56aeb84d9ba 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -43,6 +43,15 @@ #define HAVE_ERRNO_AS_DEFINE #endif /* __CYGWIN__ */ +#if defined(__QNXNTO__) && !defined(FD_SETSIZE) +#define FD_SETSIZE 1024 /* Max number of file descriptor bits in + fd_set, used when calling 'select' + Must be defined before including + "sys/select.h" and "sys/time.h" + */ +#endif + + /* to make command line shorter we'll define USE_PRAGMA_INTERFACE here */ #ifdef USE_PRAGMA_IMPLEMENTATION #define USE_PRAGMA_INTERFACE diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 10645fdf12e..62ce4da253e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1156,6 +1156,7 @@ sub environment_setup () { $ENV{'USE_RUNNING_SERVER'}= $glob_use_running_server; $ENV{'MYSQL_TEST_DIR'}= $glob_mysql_test_dir; $ENV{'MYSQLTEST_VARDIR'}= $opt_vardir; + $ENV{'MYSQL_TMP_DIR'}= $opt_tmpdir; $ENV{'MASTER_MYSOCK'}= $master->[0]->{'path_mysock'}; $ENV{'MASTER_MYSOCK1'}= $master->[1]->{'path_mysock'}; $ENV{'MASTER_MYPORT'}= $master->[0]->{'path_myport'}; @@ -2876,7 +2877,7 @@ sub run_mysqltest ($) { if ( $opt_debug ) { $cmdline_mysqlcheck .= - " --debug=d:t:A,$opt_vardir_trace/log/mysqldump.trace"; + " --debug=d:t:A,$opt_vardir_trace/log/mysqlcheck.trace"; } my $cmdline_mysqldump= "$exe_mysqldump --no-defaults -uroot " . diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index ce62964a3c0..7869a9f6f3a 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -202,6 +202,7 @@ SYST=0 REALT=0 FAST_START="" MYSQL_TMP_DIR=$MYSQL_TEST_DIR/var/tmp +export MYSQL_TMP_DIR # Use a relative path for where the slave will find the dumps # generated by "LOAD DATA" on the master. The path is relative diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index db87253e19a..3a69f56cbd3 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -402,3 +402,25 @@ explain select * from t1 force index(cola,colb) WHERE cola = 'foo' AND colb = 'b id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index_merge cola,colb cola,colb 3,3 NULL 24 Using intersect(cola,colb); Using where drop table t1; +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1 ( +a int, b int, +filler1 char(200), filler2 char(200), +key(a),key(b) +); +insert into t1 select @v:= A.a, @v, 't1', 'filler2' from t0 A, t0 B, t0 C; +create table t2 like t1; +create table t3 ( +a int, b int, +filler1 char(200), filler2 char(200), +key(a),key(b) +) engine=merge union=(t1,t2); +explain select * from t1 where a=1 and b=1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge a,b a,b 5,5 NULL # Using intersect(a,b); Using where +explain select * from t3 where a=1 and b=1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t3 index_merge a,b a,b 5,5 NULL # Using intersect(a,b); Using where +drop table t3; +drop table t0, t1, t2; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index c700de5ccc6..caab508ad96 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -56,8 +56,8 @@ a b 4 Testing 5 table 5 table -6 t1 6 t2 +6 t1 7 Testing 7 Testing 8 table diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 5c0a13fe640..e24cee9e875 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -859,6 +859,20 @@ count(*) 5 deallocate prepare stmt; drop table t1; +prepare stmt from 'create table t1 (a varchar(10) character set utf8)'; +execute stmt; +insert into t1 (a) values (repeat('a', 20)); +select length(a) from t1; +length(a) +10 +drop table t1; +execute stmt; +insert into t1 (a) values (repeat('a', 20)); +select length(a) from t1; +length(a) +10 +drop table t1; +deallocate prepare stmt; create table t1 (id int); prepare ins_call from "insert into t1 (id) values (1)"; execute ins_call; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 62c0d01327d..320f4e5c3d9 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -1,4 +1,4 @@ -drop table if exists t1, t2, t3; +drop table if exists t1, t2, t3, t4; drop view if exists v1; drop database if exists mysqltest; drop function if exists f1; @@ -785,6 +785,107 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0; ERROR 3D000: No database selected drop trigger t1_bi; ERROR 3D000: No database selected +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +insert into t1 values (101); +select @a; +@a +101 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t1 set @a:=new.id +rename table t1 to t2; +insert into t2 values (102); +select @a; +@a +102 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t2 set @a:=new.id +alter table t2 rename to t3; +insert into t3 values (103); +select @a; +@a +103 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t3 set @a:=new.id +alter table t3 rename to t4, add column val int default 0; +insert into t4 values (104, 1); +select @a; +@a +104 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t4 set @a:=new.id +drop trigger t1_bi; +drop table t4; +create database mysqltest; +use mysqltest; +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +insert into t1 values (101); +select @a; +@a +101 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test' or event_object_schema = 'mysqltest'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +mysqltest t1_bi mysqltest t1 set @a:=new.id +rename table t1 to test.t2; +ERROR HY000: Trigger in wrong schema +insert into t1 values (102); +select @a; +@a +102 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test' or event_object_schema = 'mysqltest'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +mysqltest t1_bi mysqltest t1 set @a:=new.id +drop trigger test.t1_bi; +ERROR HY000: Trigger does not exist +drop trigger t1_bi; +drop table t1; +drop database mysqltest; +use test; +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +create trigger t1_ai after insert on t1 for each row set @b:=new.id; +insert into t1 values (101); +select @a, @b; +@a @b +101 101 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t1 set @a:=new.id +test t1_ai test t1 set @b:=new.id +rename table t1 to t2; +ERROR HY000: Can't create/write to file './test/t1_ai.TRN~' (Errcode: 13) +insert into t1 values (102); +select @a, @b; +@a @b +102 102 +select trigger_schema, trigger_name, event_object_schema, +event_object_table, action_statement from information_schema.triggers +where event_object_schema = 'test'; +trigger_schema trigger_name event_object_schema event_object_table action_statement +test t1_bi test t1 set @a:=new.id +test t1_ai test t1 set @b:=new.id +drop trigger t1_bi; +drop trigger t1_ai; +drop table t1; create table t1 (i int); create trigger t1_bi before insert on t1 for each row return 0; ERROR 42000: RETURN is only allowed in a FUNCTION diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 5396db82965..e3d683a58dc 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2529,3 +2529,13 @@ Warnings: Warning 1052 Column 'x' in group statement is ambiguous DROP VIEW v1; DROP TABLE t1; +drop table if exists t1; +drop view if exists v1; +create table t1 (id int); +create view v1 as select * from t1; +drop table t1; +show create view v1; +drop view v1; +// +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_no_cache `test`.`t1`.`id` AS `id` from `t1` diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index 10512902409..3da5711bf7a 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -357,3 +357,29 @@ explain select * from t1 WHERE cola = 'foo' AND colb = 'bar'; explain select * from t1 force index(cola,colb) WHERE cola = 'foo' AND colb = 'bar'; drop table t1; +# +# BUG#17314: Index_merge/intersection not choosen by the optimizer for MERGE tables +# +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1 ( + a int, b int, + filler1 char(200), filler2 char(200), + key(a),key(b) +); +insert into t1 select @v:= A.a, @v, 't1', 'filler2' from t0 A, t0 B, t0 C; +create table t2 like t1; + +create table t3 ( + a int, b int, + filler1 char(200), filler2 char(200), + key(a),key(b) +) engine=merge union=(t1,t2); + +--replace_column 9 # +explain select * from t1 where a=1 and b=1; +--replace_column 9 # +explain select * from t3 where a=1 and b=1; + +drop table t3; +drop table t0, t1, t2; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 48a9f4c19f3..ffbba991fb6 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -900,6 +900,27 @@ execute stmt using @like; deallocate prepare stmt; drop table t1; +# +# Bug#13134 "Length of VARCHAR() utf8 column is increasing when table is +# recreated with PS/SP" +# + +prepare stmt from 'create table t1 (a varchar(10) character set utf8)'; +execute stmt; +--disable_warnings +insert into t1 (a) values (repeat('a', 20)); +--enable_warnings +select length(a) from t1; +drop table t1; +execute stmt; +--disable_warnings +insert into t1 (a) values (repeat('a', 20)); +--enable_warnings +# Check that the data is truncated to the same length +select length(a) from t1; +drop table t1; +deallocate prepare stmt; + # End of 4.1 tests # diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index c3cc8e3f8e8..e1564e33a43 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -3,7 +3,7 @@ # --disable_warnings -drop table if exists t1, t2, t3; +drop table if exists t1, t2, t3, t4; drop view if exists v1; drop database if exists mysqltest; drop function if exists f1; @@ -959,6 +959,91 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0; drop trigger t1_bi; connection default; +# +# Test for bug #13525 "Rename table does not keep info of triggers" +# +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +insert into t1 values (101); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +rename table t1 to t2; +# Trigger should work after rename +insert into t2 values (102); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +# Let us check that the same works for simple ALTER TABLE ... RENAME +alter table t2 rename to t3; +insert into t3 values (103); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +# And for more complex ALTER TABLE +alter table t3 rename to t4, add column val int default 0; +insert into t4 values (104, 1); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +# .TRN file should be updated with new table name +drop trigger t1_bi; +drop table t4; +# Rename between different databases if triggers exist should fail +create database mysqltest; +use mysqltest; +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +insert into t1 values (101); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test' or event_object_schema = 'mysqltest'; +--error ER_TRG_IN_WRONG_SCHEMA +rename table t1 to test.t2; +insert into t1 values (102); +select @a; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test' or event_object_schema = 'mysqltest'; +# There should be no fantom .TRN files +--error ER_TRG_DOES_NOT_EXIST +drop trigger test.t1_bi; +drop trigger t1_bi; +drop table t1; +drop database mysqltest; +use test; +# And now let us check that the properly handle rename if there is some +# error during it (that we rollback such renames completely). +create table t1 (id int); +create trigger t1_bi before insert on t1 for each row set @a:=new.id; +create trigger t1_ai after insert on t1 for each row set @b:=new.id; +insert into t1 values (101); +select @a, @b; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +# Trick which makes update of second .TRN file impossible +system echo dummy >var/master-data/test/t1_ai.TRN~; +system chmod 000 var/master-data/test/t1_ai.TRN~; +--error 1 +rename table t1 to t2; +# 't1' should be still there and triggers should work correctly +insert into t1 values (102); +select @a, @b; +select trigger_schema, trigger_name, event_object_schema, + event_object_table, action_statement from information_schema.triggers + where event_object_schema = 'test'; +system chmod 600 var/master-data/test/t1_ai.TRN; +# Let us check that updates to .TRN files were rolled back too +drop trigger t1_bi; +drop trigger t1_ai; +drop table t1; + # Test for bug #16829 "Firing trigger with RETURN crashes the server" # RETURN is not supposed to be used anywhere except functions, so error # should be returned when one attempts to create trigger with RETURN. diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 5d1b5a80a9b..294a254390f 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2375,3 +2375,18 @@ SELECT IF(x IS NULL, 'blank', 'not blank') AS x FROM v1 GROUP BY x; DROP VIEW v1; DROP TABLE t1; + +# +# BUG#15943: mysql_next_result hangs on invalid SHOW CREATE VIEW +# + +delimiter //; +drop table if exists t1; +drop view if exists v1; +create table t1 (id int); +create view v1 as select * from t1; +drop table t1; +show create view v1; +drop view v1; +// +delimiter ;// diff --git a/sql/field.cc b/sql/field.cc index c2c41bc2ee9..65c0d1b9397 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8297,13 +8297,11 @@ void Field_bit_as_char::sql_type(String &res) const create_field::create_length_to_internal_length() DESCRIPTION - Convert create_field::length from number of characters to number of bytes, - save original value in chars_length. + Convert create_field::length from number of characters to number of bytes. */ void create_field::create_length_to_internal_length(void) { - chars_length= length; switch (sql_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: @@ -8355,7 +8353,7 @@ void create_field::init_for_tmp_table(enum_field_types sql_type_arg, { field_name= ""; sql_type= sql_type_arg; - length= length_arg;; + char_length= length= length_arg;; unireg_check= Field::NONE; interval= 0; charset= &my_charset_bin; @@ -8683,6 +8681,8 @@ bool create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, case FIELD_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ } + /* Remember the value of length */ + char_length= length; if (!(flags & BLOB_FLAG) && ((length > max_field_charlength && fld_type != FIELD_TYPE_SET && @@ -9023,6 +9023,7 @@ create_field::create_field(Field *old_field,Field *orig_field) else interval=0; def=0; + char_length= length; if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) && old_field->ptr && orig_field && diff --git a/sql/field.h b/sql/field.h index 15c54f65ef7..ad8b34594b7 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1410,9 +1410,10 @@ public: */ ulong length; /* - The value of 'length' before a call to create_length_to_internal_length + The value of `length' as set by parser: is the number of characters + for most of the types, or of bytes for BLOBs or numeric types. */ - uint32 chars_length; + uint32 char_length; uint decimals, flags, pack_length, key_length; Field::utype unireg_check; TYPELIB *interval; // Which interval to use diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index a24b32435a8..ec015c5e320 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -308,7 +308,27 @@ void ha_myisammrg::info(uint flag) table->s->keys_in_use.set_prefix(table->s->keys); table->s->db_options_in_use= info.options; mean_rec_length= info.reclength; - block_size=0; + + /* + The handler::block_size is used all over the code in index scan cost + calculations. It is used to get number of disk seeks required to + retrieve a number of index tuples. + If the merge table has N underlying tables, then (assuming underlying + tables have equal size, the only "simple" approach we can use) + retrieving X index records from a merge table will require N times more + disk seeks compared to doing the same on a MyISAM table with equal + number of records. + In the edge case (file_tables > myisam_block_size) we'll get + block_size==0, and index calculation code will act as if we need one + disk seek to retrieve one index tuple. + + TODO: In 5.2 index scan cost calculation will be factored out into a + virtual function in class handler and we'll be able to remove this hack. + */ + block_size= 0; + if (file->tables) + block_size= myisam_block_size / file->tables; + update_time=0; #if SIZEOF_OFF_T > 4 ref_length=6; // Should be big enough diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 73f651cb1eb..2e96b369924 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4416,7 +4416,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { Field_iterator_table_ref it_1, it_2; Natural_join_column *nj_col_1, *nj_col_2; - const char *field_name_1; Query_arena *arena, backup; bool add_columns= TRUE; bool result= TRUE; @@ -4449,6 +4448,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { bool is_created_1; bool found= FALSE; + const char *field_name_1; if (!(nj_col_1= it_1.get_or_create_column_ref(&is_created_1))) goto err; field_name_1= nj_col_1->name(); @@ -4649,7 +4649,6 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, { Field_iterator_table_ref it_1, it_2; Natural_join_column *nj_col_1, *nj_col_2; - bool is_created; Query_arena *arena, backup; bool result= TRUE; List<Natural_join_column> *non_join_columns; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 5f20442cc84..e3468b2b5cf 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -19,6 +19,7 @@ */ #include "mysql_priv.h" +#include "sql_trigger.h" static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, @@ -173,9 +174,30 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) if (table_type == DB_TYPE_UNKNOWN) my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); else - rc= mysql_rename_table(ha_resolve_by_legacy_type(thd, table_type), - ren_table->db, old_alias, - new_table->db, new_alias); + { + if (!(rc= mysql_rename_table(ha_resolve_by_legacy_type(thd, + table_type), + ren_table->db, old_alias, + new_table->db, new_alias))) + { + if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, + old_alias, + new_table->db, + new_alias))) + { + /* + We've succeeded in renaming table's .frm and in updating + corresponding handler data, but have failed to update table's + triggers appropriately. So let us revert operations on .frm + and handler's data and report about failure to rename table. + */ + (void) mysql_rename_table(ha_resolve_by_legacy_type(thd, + table_type), + new_table->db, new_alias, + ren_table->db, old_alias); + } + } + } break; } case FRMTYPE_VIEW: diff --git a/sql/sql_select.h b/sql/sql_select.h index 02ee672b66e..459d2ff89a8 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -176,7 +176,9 @@ typedef struct st_rollup class JOIN :public Sql_alloc { - public: + JOIN(const JOIN &rhs); /* not implemented */ + JOIN& operator=(const JOIN &rhs); /* not implemented */ +public: JOIN_TAB *join_tab,**best_ref; JOIN_TAB **map2table; // mapping between table indexes and JOIN_TABs JOIN_TAB *join_tab_save; // saved join_tab for subquery reexecution @@ -288,13 +290,6 @@ class JOIN :public Sql_alloc init(thd_arg, fields_arg, select_options_arg, result_arg); } - JOIN(JOIN &join) - :Sql_alloc(), fields_list(join.fields_list) - { - init(join.thd, join.fields_list, join.select_options, - join.result); - } - void init(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg, select_result *result_arg) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 15176b0c301..19cf68e5789 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -513,12 +513,15 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) { if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID) DBUG_RETURN(TRUE); + /* Clear all messages with 'error' level status and issue a warning with 'warning' level status in case of invalid view and last error is ER_VIEW_INVALID */ mysql_reset_errors(thd, true); + thd->clear_error(); + push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_INVALID, ER(ER_VIEW_INVALID), diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 17482c4a463..757321b5ccf 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1127,6 +1127,12 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, { CHARSET_INFO *save_cs; + /* + Initialize length from its original value (number of characters), + which was set in the parser. This is necessary if we're + executing a prepared statement for the second time. + */ + sql_field->length= sql_field->char_length; if (!sql_field->charset) sql_field->charset= create_info->default_table_charset; /* @@ -1311,7 +1317,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->charset= (dup_field->charset ? dup_field->charset : create_info->default_table_charset); - sql_field->length= dup_field->chars_length; + sql_field->length= dup_field->char_length; sql_field->pack_length= dup_field->pack_length; sql_field->key_length= dup_field->key_length; sql_field->create_length_to_internal_length(); @@ -4031,6 +4037,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, close_cached_table(thd, table); if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias)) error= -1; + else if (Table_triggers_list::change_table_name(thd, db, table_name, + new_db, new_alias)) + { + VOID(mysql_rename_table(old_db_type, new_db, new_alias, db, + table_name)); + error= -1; + } } VOID(pthread_mutex_unlock(&LOCK_open)); } @@ -4933,7 +4946,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(quick_rm_table(new_db_type,new_db,tmp_name)); } else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db, - new_alias)) + new_alias) || + (new_name != table_name || new_db != db) && // we also do rename + Table_triggers_list::change_table_name(thd, db, table_name, + new_db, new_alias)) + { // Try to get everything back error=1; VOID(quick_rm_table(new_db_type,new_db,new_alias)); diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 0198dba780d..e33dc52df8f 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -65,7 +65,6 @@ File_option sql_modes_parameters= */ static const int TRG_NUM_REQUIRED_PARAMETERS= 4; -static const int TRG_MAX_VERSIONS= 3; /* Structure representing contents of .TRN file which are used to support @@ -318,8 +317,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, { LEX *lex= thd->lex; TABLE *table= tables->table; - char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN], - trigname_path[FN_REFLEN]; + char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; LEX_STRING file, trigname_file; LEX_STRING *trg_def, *name; ulonglong *trg_sql_mode; @@ -472,12 +470,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, definer_host->str, NullS) - trg_definer->str; if (!sql_create_definition_file(NULL, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + (gptr)this, triggers_file_parameters, 0)) return 0; err_with_cleanup: - my_delete(trigname_path, MYF(MY_WME)); + my_delete(trigname_buff, MYF(MY_WME)); return 1; } @@ -497,7 +494,8 @@ err_with_cleanup: True - error */ -static bool rm_trigger_file(char *path, char *db, char *table_name) +static bool rm_trigger_file(char *path, const char *db, + const char *table_name) { build_table_filename(path, FN_REFLEN-1, db, table_name, triggers_file_ext); return my_delete(path, MYF(MY_WME)); @@ -519,7 +517,8 @@ static bool rm_trigger_file(char *path, char *db, char *table_name) True - error */ -static bool rm_trigname_file(char *path, char *db, char *trigger_name) +static bool rm_trigname_file(char *path, const char *db, + const char *trigger_name) { build_table_filename(path, FN_REFLEN-1, db, trigger_name, trigname_file_ext); return my_delete(path, MYF(MY_WME)); @@ -527,6 +526,35 @@ static bool rm_trigname_file(char *path, char *db, char *trigger_name) /* + Helper function that saves .TRG file for Table_triggers_list object. + + SYNOPSIS + save_trigger_file() + triggers Table_triggers_list object for which file should be saved + db Name of database for subject table + table_name Name of subject table + + RETURN VALUE + FALSE Success + TRUE Error +*/ + +static bool save_trigger_file(Table_triggers_list *triggers, const char *db, + const char *table_name) +{ + char file_buff[FN_REFLEN]; + LEX_STRING file; + + file.length= build_table_filename(file_buff, FN_REFLEN-1, db, table_name, + triggers_file_ext); + file.str= file_buff; + return sql_create_definition_file(NULL, &file, &triggers_file_type, + (gptr)triggers, triggers_file_parameters, + 0); +} + + +/* Drop trigger for table. SYNOPSIS @@ -579,16 +607,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) } else { - char file_buff[FN_REFLEN]; - LEX_STRING file; - - file.length= build_table_filename(file_buff, FN_REFLEN-1, - tables->db, tables->table_name, - triggers_file_ext); - file.str= file_buff; - if (sql_create_definition_file(NULL, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + if (save_trigger_file(this, tables->db, tables->table_name)) return 1; } @@ -827,12 +846,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!names_only && triggers->prepare_record1_accessors(table)) DBUG_RETURN(1); - char *trg_name_buff; List_iterator_fast<ulonglong> itm(triggers->definition_modes_list); List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list); LEX *old_lex= thd->lex, lex; sp_rcontext *save_spcont= thd->spcont; ulong save_sql_mode= thd->variables.sql_mode; + LEX_STRING *on_table_name; thd->lex= &lex; @@ -898,6 +917,21 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, &table->mem_root)) goto err_with_lex_cleanup; + if (!(on_table_name= (LEX_STRING*) alloc_root(&table->mem_root, + sizeof(LEX_STRING)))) + goto err_with_lex_cleanup; + *on_table_name= lex.ident; + if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root)) + goto err_with_lex_cleanup; + + /* + Let us check that we correctly update trigger definitions when we + rename tables with triggers. + */ + DBUG_ASSERT(!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) && + !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, + table_name)); + if (names_only) { lex_end(&lex); @@ -1063,7 +1097,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig) lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; DBUG_RETURN(sp_add_to_query_tables(thd, lex, trig->m_db.str, - trigname.trigger_table.str, TL_WRITE)); + trigname.trigger_table.str, TL_IGNORE)); } @@ -1133,6 +1167,218 @@ end: } +/* + Update .TRG file after renaming triggers' subject table + (change name of table in triggers' definitions). + + SYNOPSIS + change_table_name_in_triggers() + thd Thread context + db_name Database of subject table + old_table_name Old subject table's name + new_table_name New subject table's name + + RETURN VALUE + FALSE Success + TRUE Failure +*/ + +bool +Table_triggers_list::change_table_name_in_triggers(THD *thd, + const char *db_name, + LEX_STRING *old_table_name, + LEX_STRING *new_table_name) +{ + char path_buff[FN_REFLEN]; + LEX_STRING *def, *on_table_name, new_def; + ulonglong *sql_mode; + ulong save_sql_mode= thd->variables.sql_mode; + List_iterator_fast<LEX_STRING> it_def(definitions_list); + List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list); + List_iterator_fast<ulonglong> it_mode(definition_modes_list); + uint on_q_table_name_len, before_on_len; + String buff; + + DBUG_ASSERT(definitions_list.elements == on_table_names_list.elements && + definitions_list.elements == definition_modes_list.elements); + + while ((def= it_def++)) + { + on_table_name= it_on_table_name++; + thd->variables.sql_mode= *(it_mode++); + + /* Construct CREATE TRIGGER statement with new table name. */ + buff.length(0); + before_on_len= on_table_name->str - def->str; + buff.append(def->str, before_on_len); + buff.append(STRING_WITH_LEN("ON ")); + append_identifier(thd, &buff, new_table_name->str, new_table_name->length); + on_q_table_name_len= buff.length() - before_on_len; + buff.append(on_table_name->str + on_table_name->length, + def->length - (before_on_len + on_table_name->length)); + /* + It is OK to allocate some memory on table's MEM_ROOT since this + table instance will be thrown out at the end of rename anyway. + */ + new_def.str= memdup_root(&table->mem_root, buff.ptr(), buff.length()); + new_def.length= buff.length(); + on_table_name->str= new_def.str + before_on_len; + on_table_name->length= on_q_table_name_len; + *def= new_def; + } + + thd->variables.sql_mode= save_sql_mode; + + if (thd->is_fatal_error) + return TRUE; /* OOM */ + + if (save_trigger_file(this, db_name, new_table_name->str)) + return TRUE; + if (rm_trigger_file(path_buff, db_name, old_table_name->str)) + { + (void) rm_trigger_file(path_buff, db_name, new_table_name->str); + return TRUE; + } + return FALSE; +} + + +/* + Iterate though Table_triggers_list::names_list list and update .TRN files + after renaming triggers' subject table. + + SYNOPSIS + change_table_name_in_trignames() + db_name Database of subject table + new_table_name New subject table's name + stopper Pointer to Table_triggers_list::names_list at + which we should stop updating. + + RETURN VALUE + 0 Success + non-0 Failure, pointer to Table_triggers_list::names_list element + for which update failed. +*/ + +LEX_STRING* +Table_triggers_list::change_table_name_in_trignames(const char *db_name, + LEX_STRING *new_table_name, + LEX_STRING *stopper) +{ + char trigname_buff[FN_REFLEN]; + struct st_trigname trigname; + LEX_STRING trigname_file; + LEX_STRING *trigger; + List_iterator_fast<LEX_STRING> it_name(names_list); + + while ((trigger= it_name++) != stopper) + { + trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, + db_name, trigger->str, + trigname_file_ext); + trigname_file.str= trigname_buff; + + trigname.trigger_table= *new_table_name; + + if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type, + (gptr)&trigname, trigname_file_parameters, + 0)) + return trigger; + } + + return 0; +} + + +/* + Update .TRG and .TRN files after renaming triggers' subject table. + + SYNOPSIS + change_table_name() + thd Thread context + db Old database of subject table + old_table Old name of subject table + new_db New database for subject table + new_table New name of subject table + + NOTE + This method tries to leave trigger related files in consistent state, + i.e. it either will complete successfully, or will fail leaving files + in their initial state. + + RETURN VALUE + FALSE Success + TRUE Error +*/ + +bool Table_triggers_list::change_table_name(THD *thd, const char *db, + const char *old_table, + const char *new_db, + const char *new_table) +{ + TABLE table; + bool result= 0; + LEX_STRING *err_trigname; + DBUG_ENTER("change_table_name"); + + bzero(&table, sizeof(table)); + init_alloc_root(&table.mem_root, 8192, 0); + + safe_mutex_assert_owner(&LOCK_open); + + if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE)) + { + result= 1; + goto end; + } + if (table.triggers) + { + LEX_STRING_WITH_INIT old_table_name(old_table, strlen(old_table)); + LEX_STRING_WITH_INIT new_table_name(new_table, strlen(new_table)); + /* + Since triggers should be in the same schema as their subject tables + moving table with them between two schemas raises too many questions. + (E.g. what should happen if in new schema we already have trigger + with same name ?). + */ + if (my_strcasecmp(table_alias_charset, db, new_db)) + { + my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); + result= 1; + goto end; + } + if (table.triggers->change_table_name_in_triggers(thd, db, + &old_table_name, + &new_table_name)) + { + result= 1; + goto end; + } + if ((err_trigname= table.triggers->change_table_name_in_trignames( + db, &new_table_name, 0))) + { + /* + If we were unable to update one of .TRN files properly we will + revert all changes that we have done and report about error. + We assume that we will be able to undo our changes without errors + (we can't do much if there will be an error anyway). + */ + (void) table.triggers->change_table_name_in_trignames(db, + &old_table_name, + err_trigname); + (void) table.triggers->change_table_name_in_triggers(thd, db, + &new_table_name, + &old_table_name); + result= 1; + goto end; + } + } +end: + delete table.triggers; + free_root(&table.mem_root, MYF(0)); + DBUG_RETURN(result); +} + bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event, trg_action_time_type time_type, diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 8992de63b37..b67c22e0588 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -47,6 +47,11 @@ class Table_triggers_list: public Sql_alloc */ List<LEX_STRING> names_list; /* + List of "ON table_name" parts in trigger definitions, used for + updating trigger definitions during RENAME TABLE. + */ + List<LEX_STRING> on_table_names_list; + /* Key representing triggers for this table in set of all stored routines used by statement. TODO: We won't need this member once triggers namespace will be @@ -97,7 +102,10 @@ public: static bool check_n_load(THD *thd, const char *db, const char *table_name, TABLE *table, bool names_only); static bool drop_all_triggers(THD *thd, char *db, char *table_name); - + static bool change_table_name(THD *thd, const char *db, + const char *old_table, + const char *new_db, + const char *new_table); bool has_delete_triggers() { return (bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] || @@ -117,6 +125,13 @@ public: private: bool prepare_record1_accessors(TABLE *table); + LEX_STRING* change_table_name_in_trignames(const char *db_name, + LEX_STRING *new_table_name, + LEX_STRING *stopper); + bool change_table_name_in_triggers(THD *thd, + const char *db_name, + LEX_STRING *old_table_name, + LEX_STRING *new_table_name); }; extern const LEX_STRING trg_action_time_type_names[]; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c2c9c710ec6..db5e72d1637 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -10931,7 +10931,7 @@ view_check_option: trigger_tail: TRIGGER_SYM remember_name sp_name trg_action_time trg_event - ON table_ident FOR_SYM EACH_SYM ROW_SYM + ON remember_name table_ident remember_end FOR_SYM EACH_SYM ROW_SYM { LEX *lex= Lex; sp_head *sp; @@ -10948,6 +10948,8 @@ trigger_tail: sp->init(lex); lex->trigger_definition_begin= $2; + lex->ident.str= $7; + lex->ident.length= $9 - $7; sp->m_type= TYPE_ENUM_TRIGGER; lex->sphead= sp; @@ -10975,7 +10977,7 @@ trigger_tail: sp->init_strings(YYTHD, lex, $3); /* Restore flag if it was cleared above */ - YYTHD->client_capabilities |= $<ulong_num>11; + YYTHD->client_capabilities |= $<ulong_num>13; sp->restore_thd_mem_root(YYTHD); if (sp->is_not_allowed_in_function("trigger")) @@ -10985,15 +10987,11 @@ trigger_tail: We have to do it after parsing trigger body, because some of sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. - - QQ: What are other consequences of this? - - QQ: Could we loosen lock type in certain cases ? */ - if (!lex->select_lex.add_table_to_list(YYTHD, $7, + if (!lex->select_lex.add_table_to_list(YYTHD, $8, (LEX_STRING*) 0, TL_OPTION_UPDATING, - TL_WRITE)) + TL_IGNORE)) YYABORT; } ; diff --git a/storage/myisammrg/myrg_queue.c b/storage/myisammrg/myrg_queue.c index 7172b9f0e2a..2e600a526c0 100644 --- a/storage/myisammrg/myrg_queue.c +++ b/storage/myisammrg/myrg_queue.c @@ -18,12 +18,26 @@ static int queue_key_cmp(void *keyseg, byte *a, byte *b) { - MI_INFO *aa=((MYRG_TABLE *)a)->table; - MI_INFO *bb=((MYRG_TABLE *)b)->table; + MYRG_TABLE *ma= (MYRG_TABLE *)a; + MYRG_TABLE *mb= (MYRG_TABLE *)b; + MI_INFO *aa= ma->table; + MI_INFO *bb= mb->table; uint not_used[2]; int ret= ha_key_cmp((HA_KEYSEG *)keyseg, aa->lastkey, bb->lastkey, USE_WHOLE_KEY, SEARCH_FIND, not_used); - return ret < 0 ? -1 : ret > 0 ? 1 : 0; + if (ret < 0) + return -1; + if (ret > 0) + return 1; + + /* + If index tuples have the same values, let the record with least rowid + value be "smaller", so index scans return records ordered by (keytuple, + rowid). This is used by index_merge access method, grep for ROR in + sql/opt_range.cc for details. + */ + return (ma->file_offset < mb->file_offset)? -1 : (ma->file_offset > + mb->file_offset) ? 1 : 0; } /* queue_key_cmp */ |