From 4b0cce48737bf90eb33e389706df824cc00d7e5c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Feb 2006 21:51:43 +0300 Subject: [PATCH 01/13] BUG#17314: Can't use index_merge/intersection for MERGE tables 1. Fix index access costs for MERGE tables, set block_size=myisam_block_size/#underlying_tables instead of 0 which it was before. 2. Make index scans on MERGE table to return records in (key_tuple, merge_table_rowid) order, instead of just (key_tuple) order. This makes an index scan on MERGE table to be truly a ROR-scan which is a requirement for index_merge union/intersection. myisammrg/myrg_queue.c: BUG#17314: Make index scans on MERGE table return records ordered by (keytuple, merge_table_rowid). mysql-test/r/index_merge.result: Testcase for BUG#17314 mysql-test/r/merge.result: BUG#17314: update testcase result mysql-test/t/index_merge.test: Testcase for BUG#17314 sql/ha_myisammrg.cc: BUG#17314: For MERGE tables, set handler::block_size to myisam_block_size/#underlying_tables, and not to 0. --- myisammrg/myrg_queue.c | 20 +++++++++++++++++--- mysql-test/r/index_merge.result | 22 ++++++++++++++++++++++ mysql-test/r/merge.result | 2 +- mysql-test/t/index_merge.test | 26 ++++++++++++++++++++++++++ sql/ha_myisammrg.cc | 22 +++++++++++++++++++++- 5 files changed, 87 insertions(+), 5 deletions(-) diff --git a/myisammrg/myrg_queue.c b/myisammrg/myrg_queue.c index 7172b9f0e2a..2e600a526c0 100644 --- a/myisammrg/myrg_queue.c +++ b/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 */ 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 7e3ccc83d73..58e1f86b3f9 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/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/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index da4136def68..9780f163634 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -288,7 +288,27 @@ void ha_myisammrg::info(uint flag) table->s->db_options_in_use= info.options; table->s->is_view= 1; 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 From 559abcf5d973213727e2dda42ee1577b4914564e Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 13 Feb 2006 19:53:34 +0300 Subject: [PATCH 02/13] Fixes bug #15943. resets error flag for show create view command, to allow proper processing of multiple sql statements sent as a single command. mysql-test/r/view.result: result file update for #15943 test case mysql-test/t/view.test: test case added for bug #15943 --- mysql-test/r/view.result | 10 ++++++++++ mysql-test/t/view.test | 15 +++++++++++++++ sql/sql_show.cc | 7 +++++++ 3 files changed, 32 insertions(+) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index e209e393d00..6b96bf7ca17 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2538,3 +2538,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/view.test b/mysql-test/t/view.test index ed44bf3b7c0..dcd2b4de8dc 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2380,3 +2380,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/sql_show.cc b/sql/sql_show.cc index 1b854a005ce..37a922cf103 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -365,6 +365,13 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) { if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID) DBUG_RETURN(TRUE); + + /* + Need this for proper processing of multiple sql statements + sent as single command + */ + thd->net.report_error= 0; + /* Clear all messages with 'error' level status and issue a warning with 'warning' level status in From 9cf3f255bded9642b6b6b23b4510514c08bcbe84 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 21 Feb 2006 19:52:20 +0300 Subject: [PATCH 03/13] A fix and a test case for Bug#13134 "Length of VARCHAR() utf8 column is increasing when table is recreated with PS/SP": make use of create_field::char_length more consistent in the code. Reinit create_field::length from create_field::char_length for every execution of a prepared statement (actually fixes the bug). mysql-test/r/ps.result: Test results fixed (Bug#13134) mysql-test/t/ps.test: A test case for Bug#13134 "Length of VARCHAR() utf8 column is increasing when table is recreated with PS/SP" sql/field.cc: Move initialization of create_field::char_length to the constructor of create_field. sql/field.h: Rename chars_length to char_length (to be consistent with how this term is used throughout the rest of the code). sql/sql_parse.cc: Initialize char_length in add_field_to_list. This function effectively works as another create_field constructor. sql/sql_table.cc: Reinit length from char_length for every field in mysql_prepare_table. This is not needed if we're executing a statement for the first time, however, at subsequent executions length contains the number of bytes, not characters (as it's expected to). --- mysql-test/r/ps.result | 17 +++++++++++++++++ mysql-test/t/ps.test | 24 +++++++++++++++++++++++- sql/field.cc | 5 ++--- sql/field.h | 2 +- sql/sql_parse.cc | 2 ++ sql/sql_table.cc | 8 +++++++- 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index ba9336c20bb..5404681e222 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -733,3 +733,20 @@ count(*) 5 deallocate prepare stmt; drop table t1; +drop table if exists t1; +Warnings: +Note 1051 Unknown 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; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index b0755d06414..b1f6be819d4 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -763,5 +763,27 @@ execute stmt using @like; deallocate prepare stmt; drop table t1; -# End of 4.1 tests +# +# Bug#13134 "Length of VARCHAR() utf8 column is increasing when table is +# recreated with PS/SP" +# + +drop table if exists t1; +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/sql/field.cc b/sql/field.cc index b1d9167aee2..fd6eba9e492 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -6516,13 +6516,11 @@ bool Field_num::eq_def(Field *field) 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: @@ -6775,6 +6773,7 @@ create_field::create_field(Field *old_field,Field *orig_field) break; } + char_length= length; decimals= old_field->decimals(); if (sql_type == FIELD_TYPE_STRING) { diff --git a/sql/field.h b/sql/field.h index 04f1bd68c7a..966549516b1 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1188,7 +1188,7 @@ public: /* The value of 'length' before a call to create_length_to_internal_length */ - uint32 chars_length; + uint32 char_length; uint decimals,flags,pack_length; Field::utype unireg_check; TYPELIB *interval; // Which interval to use diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1daa0a5ffec..21a335637b9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4480,6 +4480,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, NOT_FIXED_DEC-1) : 0; new_field->sql_type=type; new_field->length=0; + new_field->char_length= 0; new_field->change=change; new_field->interval=0; new_field->pack_length=0; @@ -4750,6 +4751,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, FIELD_TYPE_STRING : new_field->sql_type, new_field->length); + new_field->char_length= new_field->length; lex->create_list.push_back(new_field); lex->last_field=new_field; DBUG_RETURN(0); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 71cbc0be1e3..616db8b0424 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -488,6 +488,12 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, for (field_no=0; (sql_field=it++) ; field_no++) { + /* + 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; /* @@ -665,7 +671,7 @@ 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->create_length_to_internal_length(); sql_field->decimals= dup_field->decimals; From 258ad72c83154ef23bb4b9c0f492aa3e97b0bbed Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Feb 2006 16:21:43 +0300 Subject: [PATCH 04/13] Cleanup. sql/sql_base.cc: Cleanup, remove a warning. sql/sql_select.h: Cleanup: remove a warning. --- sql/sql_base.cc | 3 +-- sql/sql_select.h | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 37d4c80a0d0..4415bb501f8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3594,7 +3594,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; @@ -3627,6 +3626,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(); @@ -3823,7 +3823,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 *non_join_columns; diff --git a/sql/sql_select.h b/sql/sql_select.h index 9046398faaf..95a71142426 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -174,7 +174,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 @@ -286,12 +288,6 @@ class JOIN :public Sql_alloc init(thd_arg, fields_arg, select_options_arg, result_arg); } - JOIN(JOIN &join) - :fields_list(join.fields_list) - { - init(join.thd, join.fields_list, join.select_options, - join.result); - } void init(THD *thd_arg, List &fields_arg, ulonglong select_options_arg, select_result *result_arg) From ea9ea952be77d51db158df54b8ac627c73ff8713 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Feb 2006 23:41:15 +0300 Subject: [PATCH 05/13] Remove 'delayed' to make the test deterministic (already fixed in 5.0). A post-review fix (Bug#13134) mysql-test/r/heap.result: Remove 'delayed' to make the test deterministic. mysql-test/r/ps.result: Remove an unneeded drop table (test case for Bug#13134) mysql-test/t/heap.test: Remove 'delayed' to make the test deterministic. mysql-test/t/ps.test: A post-review fix (Bug#13134) --- mysql-test/r/heap.result | 4 ++-- mysql-test/r/ps.result | 3 --- mysql-test/t/heap.test | 4 ++-- mysql-test/t/ps.test | 1 - 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result index e3b9f7db984..6bb9d0c87ee 100644 --- a/mysql-test/r/heap.result +++ b/mysql-test/r/heap.result @@ -297,11 +297,11 @@ insert into t1 values ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd ERROR 23000: Duplicate entry 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl' for key 1 drop table t1; CREATE TABLE t1 (a int, key(a)) engine=heap; -insert delayed into t1 values (0); +insert into t1 values (0); delete from t1; select * from t1; a -insert delayed into t1 values (0), (1); +insert into t1 values (0), (1); select * from t1 where a = 0; a 0 diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 5404681e222..e94c2952893 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -733,9 +733,6 @@ count(*) 5 deallocate prepare stmt; drop table t1; -drop table if exists t1; -Warnings: -Note 1051 Unknown table 't1' prepare stmt from 'create table t1 (a varchar(10) character set utf8)'; execute stmt; insert into t1 (a) values (repeat('a', 20)); diff --git a/mysql-test/t/heap.test b/mysql-test/t/heap.test index a8128b79e3b..50147b4182d 100644 --- a/mysql-test/t/heap.test +++ b/mysql-test/t/heap.test @@ -238,10 +238,10 @@ drop table t1; # Bug 12796: Record doesn't show when selecting through index # CREATE TABLE t1 (a int, key(a)) engine=heap; -insert delayed into t1 values (0); +insert into t1 values (0); delete from t1; select * from t1; -insert delayed into t1 values (0), (1); +insert into t1 values (0), (1); select * from t1 where a = 0; drop table t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index b1f6be819d4..af885a5c02f 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -769,7 +769,6 @@ drop table t1; # recreated with PS/SP" # -drop table if exists t1; prepare stmt from 'create table t1 (a varchar(10) character set utf8)'; execute stmt; --disable_warnings From f2fd3c2781e889c83f820d8bd5c6a4d25d297b9d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Feb 2006 00:12:04 +0300 Subject: [PATCH 06/13] After-merge fixes (Bug#13134) --- sql/field.cc | 4 +++- sql/sql_parse.cc | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 31fbf9868e4..35312caf313 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8269,7 +8269,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; @@ -8597,6 +8597,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 && diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8cd8a064580..978cab704c0 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5791,7 +5791,6 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, interval_list, cs, uint_geom_type)) DBUG_RETURN(1); - new_field->char_length= new_field->length; lex->create_list.push_back(new_field); lex->last_field=new_field; DBUG_RETURN(0); From f8386dfa4821b79b6f88f8d9950ed370ba22e508 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Feb 2006 23:50:36 +0300 Subject: [PATCH 07/13] Fix for bug #13525 "Rename table does not keep info of triggers". Let us transfer triggers associated with table when we rename it (but only if we are not changing database to which table belongs, in the latter case we will emit error). mysql-test/r/trigger.result: Added test for bug #13525 "Rename table does not keep info of triggers". mysql-test/t/trigger.test: Added test for bug #13525 "Rename table does not keep info of triggers". sql/sql_rename.cc: rename_tables(): Now after renaming table's .FRM file and updating handler data we call Table_triggers_list::change_table_name() which is reponsible for updating .TRG and .TRN files. sql/sql_table.cc: mysql_alter_table(): Now in case when ALTER should rename table we call Table_triggers_list::change_table_name() which is responsible for updating .TRG and .TRN files after renaming table. sql/sql_trigger.cc: Added Table_triggers_list::change_table_name() method and change_table_name_in_triggers()/trignames() methods responsible for updating .TRG and .TRN files for table during its renaming. Two small cleanups - removed versioning for .TRG files (since it was not working before anyway) and emphasized that type of lock specified in tables list is unimportant for DROP TABLE (since this statement uses name-locking). sql/sql_trigger.h: Table_triggers_list: Added on_table_names_list member to store pointers and lenghts of "ON table_name" parts in triggers' definitions to be able easily change them during RENAME TABLE. Added change_table_name() method and change_table_name_in_trignames/triggers() helper methods responsible for updating .TRG and .TRN files. sql/sql_yacc.yy: trigger_tail: To be able properly update triggers' definitions with new table names when renaming tables we need to know where in CREATE TRIGGER statement "ON db_name.table_name" part resides. Small cleanup - let us emphasize that for CREATE TRIGGER statement lock type which is specified in table list is unimportant since name-locking is used. --- mysql-test/r/trigger.result | 103 ++++++++++++- mysql-test/t/trigger.test | 87 ++++++++++- sql/sql_rename.cc | 23 ++- sql/sql_table.cc | 13 +- sql/sql_trigger.cc | 290 +++++++++++++++++++++++++++++++++--- sql/sql_trigger.h | 17 ++- sql/sql_yacc.yy | 16 +- 7 files changed, 513 insertions(+), 36 deletions(-) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index e0048515fed..24b881d4bfa 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; @@ -786,3 +786,104 @@ 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; diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index b4074897689..8c849715274 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; @@ -961,3 +961,88 @@ create trigger test.t1_bi before insert on t1 for each row set @a:=0; --error ER_NO_DB_ERROR 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; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 80fcb973028..74951029de9 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, @@ -176,8 +177,26 @@ 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(table_type, ren_table->db, old_alias, - new_table->db, new_alias); + { + if (!(rc= mysql_rename_table(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(table_type, new_table->db, new_alias, + ren_table->db, old_alias); + } + } + } break; } case FRMTYPE_VIEW: diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ba4a606537f..b3fc24f3a4d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3258,6 +3258,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)); } @@ -3806,7 +3813,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 8a26c2bafa2..90c62838759 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -58,7 +58,6 @@ static File_option triggers_file_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 @@ -455,8 +454,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, definer_host->str, NullS) - trg_definer->str; if (!sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + (gptr)this, triggers_file_parameters, 0)) return 0; err_with_cleanup: @@ -480,7 +478,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) { strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", table_name, triggers_file_ext, NullS); @@ -504,7 +503,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) { strxnmov(path, FN_REFLEN, mysql_data_home, "/", db, "/", trigger_name, trigname_file_ext, NullS); @@ -513,6 +513,38 @@ 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 dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; + LEX_STRING dir, file; + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db, "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + file.length= strxnmov(file_buff, FN_REFLEN, table_name, triggers_file_ext, + NullS) - file_buff; + file.str= file_buff; + + return sql_create_definition_file(&dir, &file, &triggers_file_type, + (gptr)triggers, triggers_file_parameters, 0); +} + + /* Drop trigger for table. @@ -566,20 +598,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables) } else { - char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; - LEX_STRING dir, file; - - strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", tables->db, - "/", NullS); - dir.length= unpack_filename(dir_buff, dir_buff); - dir.str= dir_buff; - file.length= strxnmov(file_buff, FN_REFLEN, tables->table_name, - triggers_file_ext, NullS) - file_buff; - file.str= file_buff; - - if (sql_create_definition_file(&dir, &file, &triggers_file_type, - (gptr)this, triggers_file_parameters, - TRG_MAX_VERSIONS)) + if (save_trigger_file(this, tables->db, tables->table_name)) return 1; } @@ -819,13 +838,13 @@ 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 itm(triggers->definition_modes_list); List_iterator_fast 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; @@ -890,6 +909,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); @@ -1055,7 +1089,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)); } @@ -1125,6 +1159,220 @@ 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 it_def(definitions_list); + List_iterator_fast it_on_table_name(on_table_names_list); + List_iterator_fast 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 dir_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; + struct st_trigname trigname; + LEX_STRING dir, trigname_file; + LEX_STRING *trigger; + List_iterator_fast it_name(names_list); + + strxnmov(dir_buff, FN_REFLEN, mysql_data_home, "/", db_name, "/", NullS); + dir.length= unpack_filename(dir_buff, dir_buff); + dir.str= dir_buff; + + while ((trigger= it_name++) != stopper) + { + trigname_file.length= strxnmov(trigname_buff, FN_REFLEN, trigger->str, + trigname_file_ext, NullS) - trigname_buff; + trigname_file.str= trigname_buff; + + trigname.trigger_table= *new_table_name; + + if (sql_create_definition_file(&dir, &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 51002683897..caf6c5175fc 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -46,6 +46,11 @@ class Table_triggers_list: public Sql_alloc used in CREATE/DROP TRIGGER for looking up trigger by name. */ List names_list; + /* + List of "ON table_name" parts in trigger definitions, used for + updating trigger definitions during RENAME TABLE. + */ + List on_table_names_list; /* Key representing triggers for this table in set of all stored routines used by statement. @@ -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] || @@ -122,6 +130,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 25e10362ece..766aeab6376 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9070,8 +9070,8 @@ 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 + TRIGGER_SYM remember_name sp_name trg_action_time trg_event + ON remember_name table_ident remember_end FOR_SYM EACH_SYM ROW_SYM { LEX *lex= Lex; sp_head *sp; @@ -9088,7 +9088,9 @@ 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; lex->spname= $3; @@ -9123,15 +9125,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; } ; From 4fb56f153dbc76c7ccb68159ff6e68761400fd73 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 26 Feb 2006 20:25:24 +0300 Subject: [PATCH 08/13] Fixed test for bug #13525 "Rename table does not keep info of triggers" after merging fix for it with main tree. --- mysql-test/r/trigger.result | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index ea232e081ba..320f4e5c3d9 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -795,7 +795,7 @@ 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_bi test t1 set @a:=new.id rename table t1 to t2; insert into t2 values (102); select @a; @@ -805,7 +805,7 @@ 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 +test t1_bi test t2 set @a:=new.id alter table t2 rename to t3; insert into t3 values (103); select @a; @@ -815,7 +815,7 @@ 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 +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; @@ -825,7 +825,7 @@ 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 +test t1_bi test t4 set @a:=new.id drop trigger t1_bi; drop table t4; create database mysqltest; @@ -840,7 +840,7 @@ 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 +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); @@ -851,7 +851,7 @@ 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 +mysqltest t1_bi mysqltest t1 set @a:=new.id drop trigger test.t1_bi; ERROR HY000: Trigger does not exist drop trigger t1_bi; @@ -869,8 +869,8 @@ 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 +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); @@ -881,8 +881,8 @@ 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 +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; From 399617efe6ef5c861cedc6501acff6235e77d3c6 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 26 Feb 2006 19:54:09 +0100 Subject: [PATCH 09/13] Look for and "convert" paths that start with $MYSQL_TMP_DIR client/mysqltest.c: Also look for "$MYSQL_TMP_DIR" when looking for paths to convert mysql-test/mysql-test-run.pl: Export $MYSQL_TMP_DIR mysql-test/mysql-test-run.sh: Export $MYSQL_TMP_DIR --- client/mysqltest.c | 6 ++++-- mysql-test/mysql-test-run.pl | 1 + mysql-test/mysql-test-run.sh | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 7f5ded13d62..9aedee8aa30 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3093,8 +3093,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/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ca180020036..77f2dbee4b3 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1117,6 +1117,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'}; diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index c4038f88da4..343c883b8da 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 From 3dea6246e705657b890d9339b236b266819f9bfe Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 26 Feb 2006 23:36:53 +0100 Subject: [PATCH 10/13] Trace mysqlcheck to file mysqlcheck.trcae mysql-test/mysql-test-run.pl: Let mysqlcheck have it's own trace file --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 77f2dbee4b3..dcdf48ec7ac 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2650,7 +2650,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 " . From b97082c5836a48ff792183c555dcc3f41f0d5453 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Feb 2006 10:08:35 +0100 Subject: [PATCH 11/13] Bug#17716 Slave crash in net_clear on qnx - Set FD_SETSIZE before including "sys/select.h" include/my_global.h: Define FD_SETSIZE on QNX before including "sys/select.h" or "sys/time.h". This defines number of bits in fd_set type used for 'select' --- include/my_global.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/my_global.h b/include/my_global.h index 0df9ac78eb2..969617c084b 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 From fefdd728ff508e18e85af9ee93944f3f368ff8d4 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Feb 2006 16:52:14 +0300 Subject: [PATCH 12/13] Postfix for #15943. Explicit call of thd->clear_error() is added. --- sql/sql_show.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 37a922cf103..8920efa87ab 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -366,18 +366,14 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) if (!table_list->view || thd->net.last_errno != ER_VIEW_INVALID) DBUG_RETURN(TRUE); - /* - Need this for proper processing of multiple sql statements - sent as single command - */ - thd->net.report_error= 0; - /* 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), From 475eb3e9179fa4c1af3e0daa980dc6f30025ac28 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Feb 2006 20:00:06 +0300 Subject: [PATCH 13/13] Fixed test results after bad auto-merge. --- mysql-test/r/ps.result | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index a7c05e9acad..4d108e06356 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;