From 1200228277d3016f2c34665fca788dc35a1a402e Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 8 Jan 2009 15:16:44 +0100 Subject: [PATCH 01/30] Bug#39893: Crash if select on a partitioned table, when partitioning is disabled Problem was that it tried to run partitioning function calls when opening a partitioned table, when it was explicitly disabled. Solution is to check if the partitioning plugin is ready to use before using any partitioning specific calls. --- mysql-test/r/disabled_partition.require | 2 + mysql-test/r/not_partition.result | 45 ++++++++++ mysql-test/r/partition_disabled.result | 93 +++++++++++++++++++++ mysql-test/std_data/parts/t1.frm | Bin 0 -> 8554 bytes mysql-test/t/not_partition.test | 25 +++++- mysql-test/t/partition_disabled-master.opt | 1 + mysql-test/t/partition_disabled.test | 85 +++++++++++++++++++ sql/sql_yacc.yy | 4 +- sql/table.cc | 9 ++ 9 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 mysql-test/r/disabled_partition.require create mode 100644 mysql-test/r/partition_disabled.result create mode 100644 mysql-test/std_data/parts/t1.frm create mode 100644 mysql-test/t/partition_disabled-master.opt create mode 100644 mysql-test/t/partition_disabled.test diff --git a/mysql-test/r/disabled_partition.require b/mysql-test/r/disabled_partition.require new file mode 100644 index 00000000000..a21c259447a --- /dev/null +++ b/mysql-test/r/disabled_partition.require @@ -0,0 +1,2 @@ +Variable_name Value +have_partitioning DISABLED diff --git a/mysql-test/r/not_partition.result b/mysql-test/r/not_partition.result index 9e205a09d78..f516f8634ce 100644 --- a/mysql-test/r/not_partition.result +++ b/mysql-test/r/not_partition.result @@ -1,3 +1,48 @@ +DROP TABLE IF EXISTS t1; +FLUSH TABLES; +SELECT * FROM t1; +ERROR 42000: Unknown table engine 'partition' +TRUNCATE TABLE t1; +ERROR 42000: Unknown table engine 'partition' +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze Error Unknown table engine 'partition' +test.t1 analyze error Corrupt +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Error Unknown table engine 'partition' +test.t1 check error Corrupt +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize Error Unknown table engine 'partition' +test.t1 optimize error Corrupt +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error Unknown table engine 'partition' +test.t1 repair error Corrupt +ALTER TABLE t1 REPAIR PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 repair Error Unknown table engine 'partition' +test.t1 repair error Corrupt +ALTER TABLE t1 CHECK PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 check Error Unknown table engine 'partition' +test.t1 check error Corrupt +ALTER TABLE t1 OPTIMIZE PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 optimize Error Unknown table engine 'partition' +test.t1 optimize error Corrupt +ALTER TABLE t1 ANALYZE PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 analyze Error Unknown table engine 'partition' +test.t1 analyze error Corrupt +ALTER TABLE t1 REBUILD PARTITION ALL; +ERROR 42000: Unknown table engine 'partition' +ALTER TABLE t1 ENGINE Memory; +ERROR 42000: Unknown table engine 'partition' +ALTER TABLE t1 ADD (new INT); +ERROR 42000: Unknown table engine 'partition' +DROP TABLE t1; CREATE TABLE t1 ( firstname VARCHAR(25) NOT NULL, lastname VARCHAR(25) NOT NULL, diff --git a/mysql-test/r/partition_disabled.result b/mysql-test/r/partition_disabled.result new file mode 100644 index 00000000000..df36f56a328 --- /dev/null +++ b/mysql-test/r/partition_disabled.result @@ -0,0 +1,93 @@ +DROP TABLE IF EXISTS t1; +FLUSH TABLES; +SELECT * FROM t1; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +TRUNCATE TABLE t1; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 analyze error Corrupt +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 check error Corrupt +OPTIMIZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 optimize Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 optimize error Corrupt +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 repair error Corrupt +ALTER TABLE t1 REPAIR PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 repair Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 repair error Corrupt +ALTER TABLE t1 CHECK PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 check Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 check error Corrupt +ALTER TABLE t1 OPTIMIZE PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 optimize Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 optimize error Corrupt +ALTER TABLE t1 ANALYZE PARTITION ALL; +Table Op Msg_type Msg_text +test.t1 analyze Error The MySQL server is running with the --skip-partition option so it cannot execute this statement +test.t1 analyze error Corrupt +ALTER TABLE t1 REBUILD PARTITION ALL; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +ALTER TABLE t1 ENGINE Memory; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +ALTER TABLE t1 ADD (new INT); +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +DROP TABLE t1; +CREATE TABLE t1 ( +firstname VARCHAR(25) NOT NULL, +lastname VARCHAR(25) NOT NULL, +username VARCHAR(16) NOT NULL, +email VARCHAR(35), +joined DATE NOT NULL +) +PARTITION BY KEY(joined) +PARTITIONS 6; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +ALTER TABLE t1 PARTITION BY KEY(joined) PARTITIONS 2; +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +drop table t1; +ERROR 42S02: Unknown table 't1' +CREATE TABLE t1 ( +firstname VARCHAR(25) NOT NULL, +lastname VARCHAR(25) NOT NULL, +username VARCHAR(16) NOT NULL, +email VARCHAR(35), +joined DATE NOT NULL +) +PARTITION BY RANGE( YEAR(joined) ) ( +PARTITION p0 VALUES LESS THAN (1960), +PARTITION p1 VALUES LESS THAN (1970), +PARTITION p2 VALUES LESS THAN (1980), +PARTITION p3 VALUES LESS THAN (1990), +PARTITION p4 VALUES LESS THAN MAXVALUE +); +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +drop table t1; +ERROR 42S02: Unknown table 't1' +CREATE TABLE t1 (id INT, purchased DATE) +PARTITION BY RANGE( YEAR(purchased) ) +SUBPARTITION BY HASH( TO_DAYS(purchased) ) +SUBPARTITIONS 2 ( +PARTITION p0 VALUES LESS THAN (1990), +PARTITION p1 VALUES LESS THAN (2000), +PARTITION p2 VALUES LESS THAN MAXVALUE +); +ERROR HY000: The MySQL server is running with the --skip-partition option so it cannot execute this statement +drop table t1; +ERROR 42S02: Unknown table 't1' +create table t1 (a varchar(10) charset latin1 collate latin1_bin); +insert into t1 values (''),(' '),('a'),('a '),('a '); +explain partitions select * from t1 where a='a ' OR a='a'; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 5 Using where +drop table t1; diff --git a/mysql-test/std_data/parts/t1.frm b/mysql-test/std_data/parts/t1.frm new file mode 100644 index 0000000000000000000000000000000000000000..6709a93310e2e034528a5f0e6fdd63882f4ce62e GIT binary patch literal 8554 zcmeI&F%E(-6b9hG1ri7g17UILC=)Im04G?KD2pQ_4r&Ys@NOQ#`-o3#Iy;j1{{!#Q zrVaT<+NX+A4@eZSl7av(Iz$LNAk`+3^0))}=QRR~0x))tPYO_g0u-PC1t>rP3Q&Lo z6rcbFW-OpFxv%?n5ib389LC{%%4E*8)vjy0X1g)#gQ=^1ZI-?L2jEt+p#TLaKmiI+ zfC3bt00k&O0SZuHt^!9ThyH&Y1%(o^2n6t*LyJtt-zlM$PIL2PWDniL*KrIDEN-%c KCzmIte!>fpart_info= new partition_info(); diff --git a/sql/table.cc b/sql/table.cc index 95753e5353a..bf3c3711f31 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -914,6 +914,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, we unlock the old value of share->db_plugin before replacing it with a globally locked version of tmp_plugin */ + /* Check if the partitioning engine is ready */ + if (!plugin_is_ready(&name, MYSQL_STORAGE_ENGINE_PLUGIN)) + { + error= 8; + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-partition"); + my_free(buff, MYF(0)); + goto err; + } plugin_unlock(NULL, share->db_plugin); share->db_plugin= ha_lock_engine(NULL, partition_hton); DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", From 388955a4c318d0041e1fbf0b3c40b33f3477a709 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 9 Jul 2009 15:05:30 +0300 Subject: [PATCH 02/30] Bug #45962: memory leak after 'sort aborted' errors When the function exits with an error it was not freeing the local Unique class instance. Fixed my making sure all the places where the function returns from are freeing the Unique instance --- sql/opt_range.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e3aef02637f..47067c03a85 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8065,7 +8065,10 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() if (cur_quick->file->inited != handler::NONE) cur_quick->file->ha_index_end(); if (cur_quick->init() || cur_quick->reset()) + { + delete unique; DBUG_RETURN(1); + } } if (result) @@ -8073,13 +8076,17 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() if (result != HA_ERR_END_OF_FILE) { cur_quick->range_end(); + delete unique; DBUG_RETURN(result); } break; } if (thd->killed) + { + delete unique; DBUG_RETURN(1); + } /* skip row if it will be retrieved by clustered PK scan */ if (pk_quick_select && pk_quick_select->row_in_ranges()) @@ -8088,8 +8095,10 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() cur_quick->file->position(cur_quick->record); result= unique->unique_add((char*)cur_quick->file->ref); if (result) + { + delete unique; DBUG_RETURN(1); - + } } /* From 8bb24383f6200402f0114238c8c5e74fd50ce6b6 Mon Sep 17 00:00:00 2001 From: "timothy.smith@sun.com" <> Date: Tue, 21 Jul 2009 19:50:50 +0200 Subject: [PATCH 03/30] Set version number for mysql-5.0.82sp1 release --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index e4d7e0975d9..f27fb4c8f65 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.82) +AM_INIT_AUTOMAKE(mysql, 5.0.82sp1) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 From 2cf7f53bfc09c8d6f4ed2d3191976c28f4f483a9 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 21 Jul 2009 19:55:33 +0200 Subject: [PATCH 04/30] Backport into build-200907211706-5.0.82sp1 > ------------------------------------------------------------ > revno: 2733 > revision-id: gshchepa@mysql.com-20090430192037-9p1etcynkglte2j3 > parent: aelkin@mysql.com-20090430143246-zfqaz0t7uoluzdz2 > committer: Gleb Shchepa > branch nick: mysql-5.0-bugteam > timestamp: Fri 2009-05-01 00:20:37 +0500 > message: > Bug #37362: Crash in do_field_eq > > EXPLAIN EXTENDED of nested query containing a error: > > 1054 Unknown column '...' in 'field list' > > may cause a server crash. > > > Parse error like described above forces a call to > JOIN::destroy() on malformed subquery. > That JOIN::destroy function closes and frees temporary > tables. However, temporary fields of these tables > may be listed in st_select_lex::group_list of outer > query, and that st_select_lex may not cleanup them > properly. So, after the JOIN::destroy call that > st_select_lex::group_list may have Item_field > objects with dangling pointers to freed temporary > table Field objects. That caused a crash. --- mysql-test/r/subselect3.result | 19 +++++++++++++++++++ mysql-test/t/subselect3.test | 19 +++++++++++++++++++ sql/sql_select.cc | 8 ++++++++ 3 files changed, 46 insertions(+) diff --git a/mysql-test/r/subselect3.result b/mysql-test/r/subselect3.result index 9a6f4436ff0..759c6689be8 100644 --- a/mysql-test/r/subselect3.result +++ b/mysql-test/r/subselect3.result @@ -849,4 +849,23 @@ ROW(1,2) = (SELECT 1, 1) ROW(1,2) IN (SELECT 1, 1) SELECT ROW(1,2) = (SELECT 1, 2), ROW(1,2) IN (SELECT 1, 2); ROW(1,2) = (SELECT 1, 2) ROW(1,2) IN (SELECT 1, 2) 1 1 +CREATE TABLE t1 (a INT, b INT, c INT); +INSERT INTO t1 VALUES (1,1,1), (1,1,1); +EXPLAIN EXTENDED +SELECT c FROM +( SELECT +(SELECT COUNT(a) FROM +(SELECT COUNT(b) FROM t1) AS x GROUP BY c +) FROM t1 GROUP BY b +) AS y; +ERROR 42S22: Unknown column 'c' in 'field list' +SHOW WARNINGS; +Level Code Message +Note 1276 Field or reference 'test.t1.a' of SELECT #3 was resolved in SELECT #2 +Note 1276 Field or reference 'test.t1.c' of SELECT #3 was resolved in SELECT #2 +Error 1054 Unknown column 'c' in 'field list' +Note 1003 select `c` AS `c` from (select (select count(`test`.`t1`.`a`) AS `COUNT(a)` from (select count(`test`.`t1`.`b`) AS `COUNT(b)` from `test`.`t1`) `x` group by `c`) AS `(SELECT COUNT(a) FROM +(SELECT COUNT(b) FROM t1) AS x GROUP BY c +)` from `test`.`t1` group by `test`.`t1`.`b`) `y` +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/subselect3.test b/mysql-test/t/subselect3.test index 2d88d1660b0..6f08ebef86d 100644 --- a/mysql-test/t/subselect3.test +++ b/mysql-test/t/subselect3.test @@ -669,4 +669,23 @@ SELECT ROW(1,2) = (SELECT NULL, 1), ROW(1,2) IN (SELECT NULL, 1); SELECT ROW(1,2) = (SELECT 1, 1), ROW(1,2) IN (SELECT 1, 1); SELECT ROW(1,2) = (SELECT 1, 2), ROW(1,2) IN (SELECT 1, 2); +# +# Bug #37362 Crash in do_field_eq +# +CREATE TABLE t1 (a INT, b INT, c INT); +INSERT INTO t1 VALUES (1,1,1), (1,1,1); + +--error 1054 +EXPLAIN EXTENDED + SELECT c FROM + ( SELECT + (SELECT COUNT(a) FROM + (SELECT COUNT(b) FROM t1) AS x GROUP BY c + ) FROM t1 GROUP BY b + ) AS y; +SHOW WARNINGS; + +DROP TABLE t1; + + --echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ced01c2db47..587c0b85ce6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2172,6 +2172,14 @@ JOIN::destroy() cond_equal= 0; cleanup(1); + /* Cleanup items referencing temporary table columns */ + if (!tmp_all_fields3.is_empty()) + { + List_iterator_fast it(tmp_all_fields3); + Item *item; + while ((item= it++)) + item->cleanup(); + } if (exec_tmp_table1) free_tmp_table(thd, exec_tmp_table1); if (exec_tmp_table2) From 57a171a705115c313f70867a4deb2b787214df3a Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 21 Jul 2009 19:56:35 +0200 Subject: [PATCH 05/30] Backport into build-200907211706-5.0.82sp1 > ------------------------------------------------------------ > revno: 2763 > revision-id: sergey.glukhov@sun.com-20090602063813-33mh88cz5vpa2jqe > parent: alexey.kopytov@sun.com-20090601124224-zgt3yov9wou590e9 > committer: Sergey Glukhov > branch nick: mysql-5.0-bugteam > timestamp: Tue 2009-06-02 11:38:13 +0500 > message: > Bug#45152 crash with round() function on longtext column in a derived table > The crash happens due to wrong max_length value which is set on > Item_func_round::fix_length_and_dec() stage. The value is set to > args[0]->max_length which is too big in case of LONGTEXT(LONGBLOB) fields. > The fix is to set max_length using float_length() function. --- mysql-test/r/func_math.result | 7 +++++++ mysql-test/t/func_math.test | 9 +++++++++ sql/item_func.cc | 4 ++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index 87cfb5b86a5..9681a8a4302 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -390,4 +390,11 @@ a ROUND(a) -1e+16 -10000000000000002 1e+16 10000000000000002 DROP TABLE t1; +CREATE TABLE t1(f1 LONGTEXT) engine=myisam; +INSERT INTO t1 VALUES ('a'); +SELECT 1 FROM (SELECT ROUND(f1) AS a FROM t1) AS s WHERE a LIKE 'a'; +1 +SELECT 1 FROM (SELECT ROUND(f1, f1) AS a FROM t1) AS s WHERE a LIKE 'a'; +1 +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 593cfe90c1b..2c1094213e4 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -250,4 +250,13 @@ SELECT a, ROUND(a) FROM t1; DROP TABLE t1; +# +# Bug#45152 crash with round() function on longtext column in a derived table +# +CREATE TABLE t1(f1 LONGTEXT) engine=myisam; +INSERT INTO t1 VALUES ('a'); +SELECT 1 FROM (SELECT ROUND(f1) AS a FROM t1) AS s WHERE a LIKE 'a'; +SELECT 1 FROM (SELECT ROUND(f1, f1) AS a FROM t1) AS s WHERE a LIKE 'a'; +DROP TABLE t1; + --echo End of 5.0 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 3cbb278ea48..a93240a94d6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1958,8 +1958,8 @@ void Item_func_round::fix_length_and_dec() unsigned_flag= args[0]->unsigned_flag; if (!args[1]->const_item()) { - max_length= args[0]->max_length; decimals= args[0]->decimals; + max_length= float_length(decimals); if (args[0]->result_type() == DECIMAL_RESULT) { max_length++; @@ -1979,8 +1979,8 @@ void Item_func_round::fix_length_and_dec() if (args[0]->decimals == NOT_FIXED_DEC) { - max_length= args[0]->max_length; decimals= min(decimals_to_set, NOT_FIXED_DEC); + max_length= float_length(decimals); hybrid_type= REAL_RESULT; return; } From 4461edde0a6e37328f2101d8c75a2267cc6ed50d Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 21 Jul 2009 19:59:04 +0200 Subject: [PATCH 06/30] Backport into build-200907211706-5.0.82sp1 > ------------------------------------------------------------ > revno: 2772 > revision-id: joro@sun.com-20090615133815-eb007p5793in33p5 > parent: joro@sun.com-20090612140659-4hj1tta9p8wvcw4k > committer: Georgi Kodinov > branch nick: B44810-5.0-bugteam > timestamp: Mon 2009-06-15 16:38:15 +0300 > message: > Bug #44810: index merge and order by with low sort_buffer_size > crashes server! > > The problem affects the scenario when index merge is followed by a filesort > and the sort buffer is not big enough for all the sort keys. > In this case the filesort function will read the data to the end through the > index merge quick access method (and thus closing the cursor etc), > but will leave the pointer to the quick select method in place. > It will then create a temporary file to hold the results of the filesort and > will add it as a sort output file (in sort.io_cache). > Note that filesort will copy the original 'sort' structure in an automatic > variable and restore it after it's done. > As a result at exiting filesort() we have a sort.io_cache filled in and > nothing else (as a result of close of the cursors at end of reading data > through index merge). > Now create_sort_index() will note that there is a select and will clean it up > (as it's been used already by filesort() reading the data in). While doing that > a special case in the index merge destructor will clean up the sort.io_cache, > assuming it's an output of the index merge method and is not needed anymore. > As a result the code that tries to read the data back from the filesort output > will get no data in both memory and disk and will crash. > > Fixed similarly to how filesort() does it : by copying the sort.io_cache structure > to a local variable, removing the pointer to the io_cache (so that it's not freed > by QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT) and restoring the original > structure (together with the valid pointer) after the cleanup is done. > This is a safe thing to do because all the structures are already cleaned up by > hitting the end of the index merge's read method (QUICK_INDEX_MERGE_SELECT::get_next()) > and the cleanup code being written in a way that tolerates repeating cleanups. --- mysql-test/r/index_merge.result | 24 ++++++++++++++++++++++++ mysql-test/t/index_merge.test | 26 ++++++++++++++++++++++++++ sql/sql_select.cc | 15 +++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result index f3fce29c910..4b3b0fb54fc 100644 --- a/mysql-test/r/index_merge.result +++ b/mysql-test/r/index_merge.result @@ -555,4 +555,28 @@ a 1 2 drop table t0, t1, t2, t3; +# +# BUG#44810: index merge and order by with low sort_buffer_size +# crashes server! +# +CREATE TABLE t1(a VARCHAR(128),b VARCHAR(128),KEY(A),KEY(B)); +INSERT INTO t1 VALUES (REPEAT('a',128),REPEAT('b',128)); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +SET SESSION sort_buffer_size=1; +Warnings: +Warning 1292 Truncated incorrect sort_buffer_size value: '1' +EXPLAIN +SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%' +ORDER BY a,b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge a,b a,b 131,131 NULL 64 Using sort_union(a,b); Using where; Using filesort +SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%' +ORDER BY a,b; +SET SESSION sort_buffer_size=DEFAULT; +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test index 7f176a4cd11..ebe0fa42fcd 100644 --- a/mysql-test/t/index_merge.test +++ b/mysql-test/t/index_merge.test @@ -503,4 +503,30 @@ where exists (select 1 from t2, t3 drop table t0, t1, t2, t3; +--echo # +--echo # BUG#44810: index merge and order by with low sort_buffer_size +--echo # crashes server! +--echo # +CREATE TABLE t1(a VARCHAR(128),b VARCHAR(128),KEY(A),KEY(B)); +INSERT INTO t1 VALUES (REPEAT('a',128),REPEAT('b',128)); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +SET SESSION sort_buffer_size=1; +EXPLAIN +SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%' + ORDER BY a,b; +# we don't actually care about the result : we're checking if it crashes +--disable_result_log +SELECT * FROM t1 FORCE INDEX(a,b) WHERE a LIKE 'a%' OR b LIKE 'b%' + ORDER BY a,b; +--enable_result_log + +SET SESSION sort_buffer_size=DEFAULT; +DROP TABLE t1; + + --echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 587c0b85ce6..364269e067e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12865,8 +12865,23 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, tab->records= table->sort.found_records; // For SQL_CALC_ROWS if (select) { + /* + We need to preserve tablesort's output resultset here, because + QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by + SQL_SELECT::cleanup()) may free it assuming it's the result of the quick + select operation that we no longer need. Note that all the other parts of + this data structure are cleaned up when + QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next + SQL_SELECT::cleanup() call changes sort.io_cache alone. + */ + IO_CACHE *tablesort_result_cache; + + tablesort_result_cache= table->sort.io_cache; + table->sort.io_cache= NULL; + select->cleanup(); // filesort did select tab->select= 0; + table->sort.io_cache= tablesort_result_cache; } tab->select_cond=0; tab->last_inner= 0; From a734d03cc5671bd350e3ffcd2c86da08088596e0 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Tue, 21 Jul 2009 20:00:26 +0200 Subject: [PATCH 07/30] Backport into build-200907211706-5.0.82sp1 > ------------------------------------------------------------ > revno: 2792 > revision-id: sergey.glukhov@sun.com-20090703083500-jq8vhw0tqr37j7te > parent: bernt.johnsen@sun.com-20090703083610-o7l4s8syz05rc4w0 > committer: Sergey Glukhov > branch nick: mysql-5.0-bugteam > timestamp: Fri 2009-07-03 13:35:00 +0500 > message: > Bug#45806 crash when replacing into a view with a join! > The crash happend because for views which are joins > we have table_list->table == 0 and > table_list->table->'any method' call leads to crash. > The fix is to perform table_list->table->file->extra() > method for all tables belonging to view. --- mysql-test/r/view.result | 111 +++++++++++++++++++++++++++++++++++++++ mysql-test/t/view.test | 32 +++++++++++ sql/sql_insert.cc | 32 +++++++++-- 3 files changed, 172 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 58aa614c508..e8971e8d16b 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3659,6 +3659,117 @@ DROP TABLE t1; # -- End of test case for Bug#34337. +# +# Bug #45806 crash when replacing into a view with a join! +# +CREATE TABLE t1(a INT UNIQUE); +CREATE VIEW v1 AS SELECT t1.a FROM t1, t1 AS a; +INSERT INTO t1 VALUES (1), (2); +REPLACE INTO v1(a) SELECT 1 FROM t1,t1 AS c; +SELECT * FROM v1; +a +1 +2 +1 +2 +REPLACE INTO v1(a) SELECT 3 FROM t1,t1 AS c; +SELECT * FROM v1; +a +1 +2 +3 +1 +2 +3 +1 +2 +3 +DELETE FROM t1 WHERE a=3; +INSERT INTO v1(a) SELECT 1 FROM t1,t1 AS c +ON DUPLICATE KEY UPDATE `v1`.`a`= 1; +SELECT * FROM v1; +a +1 +2 +1 +2 +CREATE VIEW v2 AS SELECT t1.a FROM t1, v1 AS a; +REPLACE INTO v2(a) SELECT 1 FROM t1,t1 AS c; +SELECT * FROM v2; +a +1 +2 +1 +2 +1 +2 +1 +2 +REPLACE INTO v2(a) SELECT 3 FROM t1,t1 AS c; +SELECT * FROM v2; +a +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +INSERT INTO v2(a) SELECT 1 FROM t1,t1 AS c +ON DUPLICATE KEY UPDATE `v2`.`a`= 1; +SELECT * FROM v2; +a +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 +DROP VIEW v1; +DROP VIEW v2; +DROP TABLE t1; +# -- End of test case for Bug#45806 # ----------------------------------------------------------------- # -- Bug#35193 VIEW query is rewritten without "FROM DUAL", # -- causing syntax error diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 6437e546697..0e8f17d9497 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -3680,6 +3680,38 @@ SELECT * FROM v1 IGNORE INDEX (c2) WHERE c2=2; DROP VIEW v1; DROP TABLE t1; +--echo # +--echo # Bug #45806 crash when replacing into a view with a join! +--echo # +CREATE TABLE t1(a INT UNIQUE); +CREATE VIEW v1 AS SELECT t1.a FROM t1, t1 AS a; +INSERT INTO t1 VALUES (1), (2); + +REPLACE INTO v1(a) SELECT 1 FROM t1,t1 AS c; +SELECT * FROM v1; +REPLACE INTO v1(a) SELECT 3 FROM t1,t1 AS c; +SELECT * FROM v1; +DELETE FROM t1 WHERE a=3; +INSERT INTO v1(a) SELECT 1 FROM t1,t1 AS c +ON DUPLICATE KEY UPDATE `v1`.`a`= 1; +SELECT * FROM v1; + +CREATE VIEW v2 AS SELECT t1.a FROM t1, v1 AS a; + +REPLACE INTO v2(a) SELECT 1 FROM t1,t1 AS c; +SELECT * FROM v2; +REPLACE INTO v2(a) SELECT 3 FROM t1,t1 AS c; +SELECT * FROM v2; +INSERT INTO v2(a) SELECT 1 FROM t1,t1 AS c +ON DUPLICATE KEY UPDATE `v2`.`a`= 1; +SELECT * FROM v2; + +DROP VIEW v1; +DROP VIEW v2; +DROP TABLE t1; + +--echo # -- End of test case for Bug#45806 + --echo # ----------------------------------------------------------------- --echo # -- End of 5.0 tests. --echo # ----------------------------------------------------------------- diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index d9027e3f5b9..2f03b43de4d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1147,6 +1147,33 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, } +/* + Get extra info for tables we insert into + + @param table table(TABLE object) we insert into, + might be NULL in case of view + @param table(TABLE_LIST object) or view we insert into +*/ + +static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables) +{ + if (table) + { + if(table->reginfo.lock_type != TL_WRITE_DELAYED) + table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); + return; + } + + DBUG_ASSERT(tables->view); + List_iterator it(*tables->view_tables); + TABLE_LIST *tbl; + while ((tbl= it++)) + prepare_for_positional_update(tbl->table, tbl); + + return; +} + + /* Prepare items in INSERT statement @@ -1297,9 +1324,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, Only call extra() handler method if we are not performing a DELAYED operation. It will instead be executed by delayed insert thread. */ - if ((duplic == DUP_UPDATE || duplic == DUP_REPLACE) && - (table->reginfo.lock_type != TL_WRITE_DELAYED)) - table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); + if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) + prepare_for_positional_update(table, table_list); DBUG_RETURN(FALSE); } From 89b9fbd4512ca19fa06f05ddee071eb0fdfb5b76 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Thu, 6 Aug 2009 14:28:39 +0200 Subject: [PATCH 08/30] Bug#46478: timestamp field incorrectly defaulted when partition is reoganized. Problem was that table->timestamp_field_type was not changed before copying rows between partitions. fixed by setting it to TIMESTAMP_NO_AUTO_SET as the first thing in fast_alter_partition_table, so that all if-branches is covered. --- mysql-test/r/partition.result | 37 +++++++++++++++++++++++++++++++++++ mysql-test/t/partition.test | 25 +++++++++++++++++++++++ sql/sql_partition.cc | 3 +++ 3 files changed, 65 insertions(+) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 8b95f4e7e12..ceb1da8349b 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1,4 +1,41 @@ drop table if exists t1, t2; +CREATE TABLE t1 ( +a timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, +b varchar(10), +PRIMARY KEY (a) +) +PARTITION BY RANGE (to_days(a)) ( +PARTITION p1 VALUES LESS THAN (733407), +PARTITION pmax VALUES LESS THAN MAXVALUE +); +INSERT INTO t1 VALUES ('2007-07-30 17:35:48', 'p1'); +INSERT INTO t1 VALUES ('2009-07-14 17:35:55', 'pmax'); +INSERT INTO t1 VALUES ('2009-09-21 17:31:42', 'pmax'); +SELECT * FROM t1; +a b +2007-07-30 17:35:48 p1 +2009-07-14 17:35:55 pmax +2009-09-21 17:31:42 pmax +ALTER TABLE t1 REORGANIZE PARTITION pmax INTO ( +PARTITION p3 VALUES LESS THAN (733969), +PARTITION pmax VALUES LESS THAN MAXVALUE); +SELECT * FROM t1; +a b +2007-07-30 17:35:48 p1 +2009-07-14 17:35:55 pmax +2009-09-21 17:31:42 pmax +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `b` varchar(10) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (to_days(a)) +(PARTITION p1 VALUES LESS THAN (733407) ENGINE = MyISAM, + PARTITION p3 VALUES LESS THAN (733969) ENGINE = MyISAM, + PARTITION pmax VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ +DROP TABLE t1; CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a)) ENGINE=MyISAM PARTITION BY HASH (a); diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 542a992bb0e..74c5f8e0a67 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -14,6 +14,31 @@ drop table if exists t1, t2; --enable_warnings +# +# Bug#46478: timestamp field incorrectly defaulted when partition is reorganized +# +CREATE TABLE t1 ( + a timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + b varchar(10), + PRIMARY KEY (a) +) +PARTITION BY RANGE (to_days(a)) ( + PARTITION p1 VALUES LESS THAN (733407), + PARTITION pmax VALUES LESS THAN MAXVALUE +); + +INSERT INTO t1 VALUES ('2007-07-30 17:35:48', 'p1'); +INSERT INTO t1 VALUES ('2009-07-14 17:35:55', 'pmax'); +INSERT INTO t1 VALUES ('2009-09-21 17:31:42', 'pmax'); + +SELECT * FROM t1; +ALTER TABLE t1 REORGANIZE PARTITION pmax INTO ( + PARTITION p3 VALUES LESS THAN (733969), + PARTITION pmax VALUES LESS THAN MAXVALUE); +SELECT * FROM t1; +SHOW CREATE TABLE t1; +DROP TABLE t1; + # # Bug#36001: Partitions: spelling and using some error messages # diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index a181a6b3f13..61766e5c509 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -6077,6 +6077,9 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->pack_frm_len= 0; thd->work_part_info= part_info; + /* Never update timestamp columns when alter */ + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + if (fast_alter_partition & HA_PARTITION_ONE_PHASE) { /* From fba963ea93c3b3d715b78fcb1356c9d6119e68f4 Mon Sep 17 00:00:00 2001 From: "sunanda.menon@sun.com" <> Date: Tue, 11 Aug 2009 07:16:52 +0200 Subject: [PATCH 09/30] Raise version number after cloning 5.0.85 --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index ddebde47d12..a70bb3ca27f 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.85) +AM_INIT_AUTOMAKE(mysql, 5.0.86) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=85 +NDB_VERSION_BUILD=86 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? From 2c21af95ded7c308c79aa5966711c21e5434afbb Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 11 Aug 2009 13:13:06 -0300 Subject: [PATCH 10/30] Update test case result due to mis-merge. --- mysql-test/r/view.result | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index aa2294ef926..48bcfbf7975 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -3659,6 +3659,51 @@ DROP TABLE t1; # -- End of test case for Bug#34337. +# ----------------------------------------------------------------- +# -- Bug#35193 VIEW query is rewritten without "FROM DUAL", +# -- causing syntax error +# ----------------------------------------------------------------- + +CREATE VIEW v1 AS SELECT 1 FROM DUAL WHERE 1; + +SELECT * FROM v1; +1 +1 +SHOW CREATE TABLE v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1` from DUAL where 1 + +DROP VIEW v1; + +# -- End of test case for Bug#35193. + +CREATE VIEW v1 AS SELECT 1; +DROP VIEW v1; +CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, INDEX (c2)); +INSERT INTO t1 VALUES (1,1), (2,2), (3,3); +SELECT * FROM t1 USE INDEX (PRIMARY) WHERE c1=2; +c1 c2 +2 2 +SELECT * FROM t1 USE INDEX (c2) WHERE c2=2; +c1 c2 +2 2 +CREATE VIEW v1 AS SELECT c1, c2 FROM t1; +SHOW INDEX FROM v1; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +SELECT * FROM v1 USE INDEX (PRIMARY) WHERE c1=2; +ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' +SELECT * FROM v1 FORCE INDEX (PRIMARY) WHERE c1=2; +ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' +SELECT * FROM v1 IGNORE INDEX (PRIMARY) WHERE c1=2; +ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' +SELECT * FROM v1 USE INDEX (c2) WHERE c2=2; +ERROR HY000: Key 'c2' doesn't exist in table 'v1' +SELECT * FROM v1 FORCE INDEX (c2) WHERE c2=2; +ERROR HY000: Key 'c2' doesn't exist in table 'v1' +SELECT * FROM v1 IGNORE INDEX (c2) WHERE c2=2; +ERROR HY000: Key 'c2' doesn't exist in table 'v1' +DROP VIEW v1; +DROP TABLE t1; # # Bug #45806 crash when replacing into a view with a join! # @@ -3771,51 +3816,6 @@ DROP VIEW v2; DROP TABLE t1; # -- End of test case for Bug#45806 # ----------------------------------------------------------------- -# -- Bug#35193 VIEW query is rewritten without "FROM DUAL", -# -- causing syntax error -# ----------------------------------------------------------------- - -CREATE VIEW v1 AS SELECT 1 FROM DUAL WHERE 1; - -SELECT * FROM v1; -1 -1 -SHOW CREATE TABLE v1; -View Create View -v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select 1 AS `1` from DUAL where 1 - -DROP VIEW v1; - -# -- End of test case for Bug#35193. - -CREATE VIEW v1 AS SELECT 1; -DROP VIEW v1; -CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, INDEX (c2)); -INSERT INTO t1 VALUES (1,1), (2,2), (3,3); -SELECT * FROM t1 USE INDEX (PRIMARY) WHERE c1=2; -c1 c2 -2 2 -SELECT * FROM t1 USE INDEX (c2) WHERE c2=2; -c1 c2 -2 2 -CREATE VIEW v1 AS SELECT c1, c2 FROM t1; -SHOW INDEX FROM v1; -Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment -SELECT * FROM v1 USE INDEX (PRIMARY) WHERE c1=2; -ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' -SELECT * FROM v1 FORCE INDEX (PRIMARY) WHERE c1=2; -ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' -SELECT * FROM v1 IGNORE INDEX (PRIMARY) WHERE c1=2; -ERROR HY000: Key 'PRIMARY' doesn't exist in table 'v1' -SELECT * FROM v1 USE INDEX (c2) WHERE c2=2; -ERROR HY000: Key 'c2' doesn't exist in table 'v1' -SELECT * FROM v1 FORCE INDEX (c2) WHERE c2=2; -ERROR HY000: Key 'c2' doesn't exist in table 'v1' -SELECT * FROM v1 IGNORE INDEX (c2) WHERE c2=2; -ERROR HY000: Key 'c2' doesn't exist in table 'v1' -DROP VIEW v1; -DROP TABLE t1; -# ----------------------------------------------------------------- # -- Bug#40825: Error 1356 while selecting from a view # -- with a "HAVING" clause though query works # ----------------------------------------------------------------- From 4b80599a6250dc7244953038078e79a53ff5647b Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Tue, 11 Aug 2009 13:14:27 -0300 Subject: [PATCH 11/30] Fix tree name. --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index f79c1cd6319..557df1b1ffe 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "commits@lists.mysql.com" post_push_to = "commits@lists.mysql.com" -tree_name = "mysql-5.0" +tree_name = "mysql-5.0-bugteam" From f5be2159fea9800c0d75046ab9c632abb401e9f0 Mon Sep 17 00:00:00 2001 From: Date: Wed, 12 Aug 2009 11:54:05 +0800 Subject: [PATCH 12/30] BUG#45516 SQL thread does not use database charset properly Replication SQL thread does not set database default charset to thd->variables.collation_database properly, when executing LOAD DATA binlog. This bug can be repeated by using "LOAD DATA" command in STATEMENT mode. This patch adds code to find the default character set of the current database then assign it to thd->db_charset when slave server begins to execute a relay log. The test of this bug is added into rpl_loaddata_charset.test --- mysql-test/include/rpl_loaddata_charset.inc | 35 ++++++++++++++++++ mysql-test/r/rpl_loaddata_charset.result | 41 +++++++++++++++++++++ mysql-test/std_data/loaddata_utf8.dat | 3 ++ mysql-test/t/rpl_loaddata_charset.test | 17 +++++++++ sql/log_event.cc | 10 +++++ 5 files changed, 106 insertions(+) create mode 100644 mysql-test/include/rpl_loaddata_charset.inc create mode 100644 mysql-test/std_data/loaddata_utf8.dat diff --git a/mysql-test/include/rpl_loaddata_charset.inc b/mysql-test/include/rpl_loaddata_charset.inc new file mode 100644 index 00000000000..f94ff4ed069 --- /dev/null +++ b/mysql-test/include/rpl_loaddata_charset.inc @@ -0,0 +1,35 @@ +connection master; +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest CHARSET UTF8; +USE mysqltest; +CREATE TABLE t (cl varchar(100)) CHARSET UTF8; + +if (!$LOAD_LOCAL) +{ + LOAD DATA INFILE '../std_data_ln/loaddata_utf8.dat' INTO TABLE t + FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; +} +if ($LOAD_LOCAL) +{ + LOAD DATA LOCAL INFILE './std_data/loaddata_utf8.dat' INTO TABLE t + FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; +} + +save_master_pos; +echo ----------content on master----------; +SELECT hex(cl) FROM t; + +connection slave; +sync_with_master; +echo ----------content on slave----------; +USE mysqltest; +SELECT hex(cl) FROM t; + +connection master; +DROP DATABASE mysqltest; +save_master_pos; +connection slave; +sync_with_master; diff --git a/mysql-test/r/rpl_loaddata_charset.result b/mysql-test/r/rpl_loaddata_charset.result index 929d06e74cf..47fee19cf5d 100644 --- a/mysql-test/r/rpl_loaddata_charset.result +++ b/mysql-test/r/rpl_loaddata_charset.result @@ -35,3 +35,44 @@ C3BF D0AA D0AA drop table t1; +-------------test bug#45516------------------ +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest CHARSET UTF8; +USE mysqltest; +CREATE TABLE t (cl varchar(100)) CHARSET UTF8; +LOAD DATA LOCAL INFILE './std_data/loaddata_utf8.dat' INTO TABLE t +FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; +----------content on master---------- +SELECT hex(cl) FROM t; +hex(cl) +E4B880E4BA8CE4B889 +E59B9BE4BA94E585AD +E4B883E585ABE4B99D +----------content on slave---------- +USE mysqltest; +SELECT hex(cl) FROM t; +hex(cl) +E4B880E4BA8CE4B889 +E59B9BE4BA94E585AD +E4B883E585ABE4B99D +DROP DATABASE mysqltest; +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest CHARSET UTF8; +USE mysqltest; +CREATE TABLE t (cl varchar(100)) CHARSET UTF8; +LOAD DATA INFILE '../std_data_ln/loaddata_utf8.dat' INTO TABLE t +FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; +----------content on master---------- +SELECT hex(cl) FROM t; +hex(cl) +E4B880E4BA8CE4B889 +E59B9BE4BA94E585AD +E4B883E585ABE4B99D +----------content on slave---------- +USE mysqltest; +SELECT hex(cl) FROM t; +hex(cl) +E4B880E4BA8CE4B889 +E59B9BE4BA94E585AD +E4B883E585ABE4B99D +DROP DATABASE mysqltest; diff --git a/mysql-test/std_data/loaddata_utf8.dat b/mysql-test/std_data/loaddata_utf8.dat new file mode 100644 index 00000000000..fc7a28229d4 --- /dev/null +++ b/mysql-test/std_data/loaddata_utf8.dat @@ -0,0 +1,3 @@ +一二三 +四五六 +七八九 diff --git a/mysql-test/t/rpl_loaddata_charset.test b/mysql-test/t/rpl_loaddata_charset.test index 7f2389cb9f6..05dbdd01b39 100644 --- a/mysql-test/t/rpl_loaddata_charset.test +++ b/mysql-test/t/rpl_loaddata_charset.test @@ -31,3 +31,20 @@ select hex(a) from t1; connection master; drop table t1; sync_slave_with_master; + +# +# Bug#45516 +# When slave SQL thread executing LOAD DATA command, the +# thd->variables.collation_database was not set properly to the default +# database charset +# + +echo -------------test bug#45516------------------; + +# LOAD DATA INFILE +let $LOAD_LOCAL=1; +source include/rpl_loaddata_charset.inc; + +# LOAD DATA LOCAL INFILE +let $LOAD_LOCAL=0; +source include/rpl_loaddata_charset.inc; diff --git a/sql/log_event.cc b/sql/log_event.cc index 9b0f8e97a28..40e29e58ab6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1918,6 +1918,8 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, { const char *new_db= rewrite_db(db); int expected_error,actual_error= 0; + HA_CREATE_INFO db_options; + /* Colleagues: please never free(thd->catalog) in MySQL. This would lead to bugs as here thd->catalog is a part of an alloced block, not an entire @@ -1926,6 +1928,14 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, */ thd->catalog= catalog_len ? (char *) catalog : (char *)""; thd->set_db(new_db, (uint) strlen(new_db)); /* allocates a copy of 'db' */ + + /* + Setting the character set and collation of the current database thd->db. + */ + load_db_opt_by_name(thd, thd->db, &db_options); + if (db_options.default_table_charset) + thd->db_charset= db_options.default_table_charset; + thd->variables.auto_increment_increment= auto_increment_increment; thd->variables.auto_increment_offset= auto_increment_offset; From d82d7ccccbe7b8e28ef60f2c09b95fbdf63b7f8d Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 12 Aug 2009 14:57:41 +0400 Subject: [PATCH 13/30] A follow up patch for Bug#45829 "CREATE TABLE TRANSACTIONAL PAGE_CHECKSUM ROW_FORMAT=PAGE accepted, does nothing" Remove unused code that would lead to warnings when compiling sql_yacc.yy. --- sql/handler.h | 6 ------ sql/sql_yacc.yy | 8 -------- sql/table.h | 4 ---- 3 files changed, 18 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index 01f673ecd51..726926bcb46 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -895,8 +895,6 @@ class partition_info; struct st_partition_iter; #define NOT_A_PARTITION_ID ((uint32)-1) -enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES }; - typedef struct st_ha_create_information { CHARSET_INFO *table_charset, *default_table_charset; @@ -918,14 +916,10 @@ typedef struct st_ha_create_information uint options; /* OR of HA_CREATE_ options */ uint merge_insert_method; uint extra_size; /* length of extra data segment */ - /** Transactional or not. Unused; reserved for future versions. */ - enum ha_choice transactional; bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ - /** Per-page checksums or not. Unused; reserved for future versions. */ - enum ha_choice page_checksum; } HA_CREATE_INFO; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 320b43c8e5c..11250404da6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -494,7 +494,6 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, enum enum_tx_isolation tx_isolation; enum Cast_target cast_type; enum Item_udftype udf_type; - enum ha_choice choice; CHARSET_INFO *charset; thr_lock_type lock_type; interval_type interval, interval_time_st; @@ -1164,8 +1163,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type ulonglong_num real_ulonglong_num size_number -%type choice - %type part_bit_expr @@ -9066,11 +9063,6 @@ dec_num: | FLOAT_NUM ; -choice: - ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; } - | DEFAULT { $$= HA_CHOICE_UNDEF; } - ; - procedure_clause: /* empty */ | PROCEDURE ident /* Procedure name */ diff --git a/sql/table.h b/sql/table.h index 653d04b149e..3b6a0871d09 100644 --- a/sql/table.h +++ b/sql/table.h @@ -361,10 +361,6 @@ typedef struct st_table_share } enum row_type row_type; /* How rows are stored */ enum tmp_table_type tmp_table; - /** Transactional or not. Unused; reserved for future versions. */ - enum ha_choice transactional; - /** Per-page checksums or not. Unused; reserved for future versions. */ - enum ha_choice page_checksum; uint ref_count; /* How many TABLE objects uses this */ uint open_count; /* Number of tables in open list */ From 68b96702a0873694619ce4675c8722588526b265 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Wed, 12 Aug 2009 17:46:12 +0500 Subject: [PATCH 14/30] Disable main.index_merge_innodb with InnoDB plugin. The test case is not ready to run with innoplug-1.0.4. --- mysql-test/lib/mtr_cases.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index c9c0f1796e5..873339166b3 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -494,6 +494,7 @@ sub collect_one_suite($) next if ($test->{'skip'} || !$test->{'innodb_test'}); # Exceptions next if ($test->{'name'} eq 'main.innodb'); # Failed with wrong errno (fk) + next if ($test->{'name'} eq 'main.index_merge_innodb'); # Explain diff # innodb_file_per_table is rw with innodb_plugin next if ($test->{'name'} eq 'sys_vars.innodb_file_per_table_basic'); # innodb_lock_wait_timeout is rw with innodb_plugin From a75f9342280d97c488a51b49e47bc7d943e27772 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Wed, 12 Aug 2009 17:11:06 +0400 Subject: [PATCH 15/30] A follow up patch for the follow up patch for Bug#45829 "CREATE TABLE TRANSACTIONAL PAGE_CHECKSUM ROW_FORMAT=PAGE accepted, does nothing". Put back stubs for members of structures that are shared between sql/ and pluggable storage engines. to not break ABI unnecessarily. To be NULL-merged into 5.4, where we do break the ABI already. --- sql/handler.h | 4 ++++ sql/table.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/sql/handler.h b/sql/handler.h index 726926bcb46..f76f940fd37 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -895,6 +895,8 @@ class partition_info; struct st_partition_iter; #define NOT_A_PARTITION_ID ((uint32)-1) +enum enum_ha_unused { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES }; + typedef struct st_ha_create_information { CHARSET_INFO *table_charset, *default_table_charset; @@ -916,10 +918,12 @@ typedef struct st_ha_create_information uint options; /* OR of HA_CREATE_ options */ uint merge_insert_method; uint extra_size; /* length of extra data segment */ + enum enum_ha_unused unused1; bool table_existed; /* 1 in create if table existed */ bool frm_only; /* 1 if no ha_create_table() */ bool varchar; /* 1 if table has a VARCHAR */ enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ + enum enum_ha_unused unused2; } HA_CREATE_INFO; diff --git a/sql/table.h b/sql/table.h index 3b6a0871d09..40372fa91cf 100644 --- a/sql/table.h +++ b/sql/table.h @@ -361,6 +361,8 @@ typedef struct st_table_share } enum row_type row_type; /* How rows are stored */ enum tmp_table_type tmp_table; + enum enum_ha_unused unused1; + enum enum_ha_unused unused2; uint ref_count; /* How many TABLE objects uses this */ uint open_count; /* Number of tables in open list */ From 21cbee126a6b360b4bc7adbd1a15067684827558 Mon Sep 17 00:00:00 2001 From: "karen.langford@sun.com" <> Date: Wed, 12 Aug 2009 16:41:07 +0200 Subject: [PATCH 16/30] Raise version number after cloning 5.1.38 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index b3a5abdc38d..5b9f3e0f200 100644 --- a/configure.in +++ b/configure.in @@ -10,7 +10,7 @@ AC_CANONICAL_SYSTEM # # When changing major version number please also check switch statement # in mysqlbinlog::check_master_version(). -AM_INIT_AUTOMAKE(mysql, 5.1.38) +AM_INIT_AUTOMAKE(mysql, 5.1.39) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 From 14345a20050c4706874b4dba59742fd3b0cd4271 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Wed, 12 Aug 2009 23:43:20 +0400 Subject: [PATCH 17/30] Fix 5.1 build error. --- storage/innodb_plugin/include/btr0cur.h | 2 +- storage/innodb_plugin/include/trx0types.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index b2d43ae3254..480a3877e54 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -618,7 +618,7 @@ enum btr_cur_method { hash_node, and might be necessary to update */ BTR_CUR_BINARY, /*!< success using the binary search */ - BTR_CUR_INSERT_TO_IBUF, /*!< performed the intended insert to + BTR_CUR_INSERT_TO_IBUF /*!< performed the intended insert to the insert buffer */ }; diff --git a/storage/innodb_plugin/include/trx0types.h b/storage/innodb_plugin/include/trx0types.h index 08cc9622d02..24cf57d53d5 100644 --- a/storage/innodb_plugin/include/trx0types.h +++ b/storage/innodb_plugin/include/trx0types.h @@ -70,7 +70,7 @@ typedef struct trx_named_savept_struct trx_named_savept_t; enum trx_rb_ctx { RB_NONE = 0, /*!< no rollback */ RB_NORMAL, /*!< normal rollback */ - RB_RECOVERY, /*!< rolling back an incomplete transaction, + RB_RECOVERY /*!< rolling back an incomplete transaction, in crash recovery */ }; From 69d0ddaed60f1aca0bfddd56a352822398531cf7 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Wed, 12 Aug 2009 23:45:01 +0400 Subject: [PATCH 18/30] Ignore auto-generated file: libmysqld/examples/mysqltest.cc --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index fad758b54b8..6c6a43dacab 100644 --- a/.bzrignore +++ b/.bzrignore @@ -3063,3 +3063,4 @@ sql/share/slovak sql/share/spanish sql/share/swedish sql/share/ukrainian +libmysqld/examples/mysqltest.cc From 10e90a1fe5154e78402fe4b3868e79d1f2076239 Mon Sep 17 00:00:00 2001 From: Date: Thu, 13 Aug 2009 10:48:57 +0800 Subject: [PATCH 19/30] BUG#45574 CREATE IF NOT EXISTS is not binlogged if the object exists There is an inconsistency with DROP DATABASE|TABLE|EVENT IF EXISTS and CREATE DATABASE|TABLE|EVENT IF NOT EXISTS. DROP IF EXISTS statements are binlogged even if either the DB, TABLE or EVENT does not exist. In contrast, Only the CREATE EVENT IF NOT EXISTS is binlogged when the EVENT exists. This patch fixes the following cases for all the replication formats: CREATE DATABASE IF NOT EXISTS. CREATE TABLE IF NOT EXISTS, CREATE TABLE IF NOT EXISTS ... LIKE, CREAET TABLE IF NOT EXISTS ... SELECT. --- .../rpl/r/rpl_create_if_not_exists.result | 33 ++++++++ .../rpl_create_tmp_table_if_not_exists.result | 22 +++++ .../suite/rpl/t/rpl_create_if_not_exists.test | 70 ++++++++++++++++ .../t/rpl_create_tmp_table_if_not_exists.test | 41 +++++++++ sql/sql_db.cc | 7 +- sql/sql_insert.cc | 52 +++++++----- sql/sql_table.cc | 83 +++++++++++++------ 7 files changed, 255 insertions(+), 53 deletions(-) create mode 100644 mysql-test/suite/rpl/r/rpl_create_if_not_exists.result create mode 100644 mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result create mode 100644 mysql-test/suite/rpl/t/rpl_create_if_not_exists.test create mode 100644 mysql-test/suite/rpl/t/rpl_create_tmp_table_if_not_exists.test diff --git a/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result new file mode 100644 index 00000000000..536f40dc7f1 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_create_if_not_exists.result @@ -0,0 +1,33 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE IF NOT EXISTS mysqltest; +USE mysqltest; +CREATE TABLE IF NOT EXISTS t(c1 int); +CREATE TABLE IF NOT EXISTS t1 LIKE t; +CREATE TABLE IF NOT EXISTS t2 SELECT * FROM t; +CREATE EVENT IF NOT EXISTS e +ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR +DO SELECT now(); +DROP DATABASE mysqltest; +CREATE DATABASE IF NOT EXISTS mysqltest; +USE mysqltest; +CREATE TABLE IF NOT EXISTS t(c1 int); +CREATE TABLE IF NOT EXISTS t1 LIKE t; +CREATE TABLE IF NOT EXISTS t2 SELECT * FROM t; +CREATE EVENT IF NOT EXISTS e +ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR +DO SELECT now(); +SHOW TABLES in mysqltest; +Tables_in_mysqltest +t +t1 +t2 +SHOW EVENTS in mysqltest; +Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation +mysqltest e @ SYSTEM ONE TIME # NULL NULL NULL NULL SLAVESIDE_DISABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci +DROP DATABASE IF EXISTS mysqltest; diff --git a/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result b/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result new file mode 100644 index 00000000000..8d0b61cc6d8 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_create_tmp_table_if_not_exists.result @@ -0,0 +1,22 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +DROP DATABASE IF EXISTS mysqltest; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int); +CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int); +CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp; +show binlog events from ; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query # # DROP DATABASE IF EXISTS mysqltest +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int) +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int) +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp +master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp diff --git a/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test new file mode 100644 index 00000000000..5faf95a4d84 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_create_if_not_exists.test @@ -0,0 +1,70 @@ +# BUG#45574: +# SP: CREATE DATABASE|TABLE IF NOT EXISTS not binlogged if routine exists. +# +# There is an inconsistency with DROP DATABASE|TABLE|EVENT IF EXISTS and +# CREATE DATABASE|TABLE|EVENT IF NOT EXISTS. DROP IF EXISTS statements are +# binlogged even if either the DB, TABLE or EVENT does not exist. In +# contrast, Only the CREATE EVENT IF NOT EXISTS is binlogged when the EVENT +# exists. +# +# This problem caused some of the tests to fail randomly on PB or PB2. +# +# Description: +# Fixed this bug by adding calls to write_bin_log in: +# mysql_create_db +# mysql_create_table_no_lock +# mysql_create_like_table +# create_table_from_items +# +# Test is implemented as follows: +# i) test each "CREATE IF NOT EXISTS" (DDL), found in MySQL 5.1 manual +# exclude CREATE TEMPORARY TABLE, on existent objects; +# +# Note: +# rpl_create_tmp_table_if_not_exists.test tests CREATE TEMPORARY TABLE cases. +# +# References: +# http://dev.mysql.com/doc/refman/5.1/en/sql-syntax-data-definition.html +# + +source include/master-slave.inc; +disable_warnings; +DROP DATABASE IF EXISTS mysqltest; + +CREATE DATABASE IF NOT EXISTS mysqltest; +USE mysqltest; +CREATE TABLE IF NOT EXISTS t(c1 int); +CREATE TABLE IF NOT EXISTS t1 LIKE t; +CREATE TABLE IF NOT EXISTS t2 SELECT * FROM t; +CREATE EVENT IF NOT EXISTS e +ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR +DO SELECT now(); +sync_slave_with_master; + +connection slave; +#DROP database from slave. +#The database and all tables can be recreated in slave +#if binlog of the second CREATE command is recorded and sent from master to slave. +DROP DATABASE mysqltest; + +connection master; +CREATE DATABASE IF NOT EXISTS mysqltest; +USE mysqltest; +CREATE TABLE IF NOT EXISTS t(c1 int); +CREATE TABLE IF NOT EXISTS t1 LIKE t; +CREATE TABLE IF NOT EXISTS t2 SELECT * FROM t; +CREATE EVENT IF NOT EXISTS e +ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR +DO SELECT now(); +sync_slave_with_master; + +connection slave; +SHOW TABLES in mysqltest; +#Execution time changes in each run. So we disregard it by calling replace_column. +replace_column 6 #; +SHOW EVENTS in mysqltest; + + +connection master; +DROP DATABASE IF EXISTS mysqltest; +source include/master-slave-end.inc; diff --git a/mysql-test/suite/rpl/t/rpl_create_tmp_table_if_not_exists.test b/mysql-test/suite/rpl/t/rpl_create_tmp_table_if_not_exists.test new file mode 100644 index 00000000000..a06dfa54cb1 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_create_tmp_table_if_not_exists.test @@ -0,0 +1,41 @@ +# BUG#45574: +# SP: CREATE DATABASE|TABLE IF NOT EXISTS not binlogged if routine exists. +# +# There is an inconsistency with DROP DATABASE|TABLE|EVENT IF EXISTS and +# CREATE DATABASE|TABLE|EVENT IF NOT EXISTS. DROP IF EXISTS statements are +# binlogged even if either the DB, TABLE or EVENT does not exist. In +# contrast, Only the CREATE EVENT IF NOT EXISTS is binlogged when the EVENT +# exists. +# +# This problem caused some of the tests to fail randomly on PB or PB2. +# +# Test is implemented as follows: +# +# i) test each "CREATE TEMPORARY TABLE IF EXISTS" (DDL), found in MySQL +# 5.1 manual, on existent objects; +# ii) show binlog events; +# +# Note: +# rpl_create_if_not_exists.test tests other cases. +# +# References: +# http://dev.mysql.com/doc/refman/5.1/en/sql-syntax-data-definition.html +# + +source include/master-slave.inc; +#CREATE TEMPORARY TABLE statements are not binlogged in row mode, +#So it must be test by itself. +source include/have_binlog_format_mixed_or_statement.inc; +disable_warnings; + +DROP DATABASE IF EXISTS mysqltest; + +CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int); +CREATE TEMPORARY TABLE IF NOT EXISTS tmp(c1 int); +CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp1 LIKE tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS tmp2 SELECT * FROM tmp; +source include/show_binlog_events.inc; + +source include/master-slave-end.inc; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 98d17fdd318..3fca5bd7df6 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -658,10 +658,8 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, } push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db); - if (!silent) - my_ok(thd); error= 0; - goto exit; + goto not_silent; } else { @@ -698,7 +696,8 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, happened. (This is a very unlikely senario) */ } - + +not_silent: if (!silent) { char *query; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 7d581f8c3a9..a799cbea4c2 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -3392,25 +3392,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, bool not_used; DBUG_ENTER("create_table_from_items"); - DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000);); - - if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && - create_table->table->db_stat) - { - /* Table already exists and was open at open_and_lock_tables() stage. */ - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - create_info->table_existed= 1; // Mark that table existed - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), - create_table->table_name); - DBUG_RETURN(create_table->table); - } - - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name); - DBUG_RETURN(0); - } - tmp_table.alias= 0; tmp_table.timestamp_field= 0; tmp_table.s= &share; @@ -3612,10 +3593,35 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) thd->binlog_start_trans_and_stmt(); } - if (!(table= create_table_from_items(thd, create_info, create_table, - alter_info, &values, - &extra_lock, hook_ptr))) - DBUG_RETURN(-1); // abort() deletes table + DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000);); + + if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) && + create_table->table->db_stat) + { + /* Table already exists and was open at open_and_lock_tables() stage. */ + if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + { + /* Mark that table existed */ + create_info->table_existed= 1; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), + create_table->table_name); + if (thd->current_stmt_binlog_row_based) + binlog_show_create_table(&(create_table->table), 1); + table= create_table->table; + } + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name); + DBUG_RETURN(-1); + } + } + else + if (!(table= create_table_from_items(thd, create_info, create_table, + alter_info, &values, + &extra_lock, hook_ptr))) + /* abort() deletes table */ + DBUG_RETURN(-1); if (extra_lock) { diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 0066c66eb59..81d00f46000 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3475,6 +3475,41 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field) } +/* + Write CREATE TABLE binlog + + SYNOPSIS + write_create_table_bin_log() + thd Thread object + create_info Create information + internal_tmp_table Set to 1 if this is an internal temporary table + + DESCRIPTION + This function only is called in mysql_create_table_no_lock and + mysql_create_table + + RETURN VALUES + NONE + */ +static inline void write_create_table_bin_log(THD *thd, + const HA_CREATE_INFO *create_info, + bool internal_tmp_table) +{ + /* + Don't write statement if: + - It is an internal temporary table, + - Row-based logging is used and it we are creating a temporary table, or + - The binary log is not open. + Otherwise, the statement shall be binlogged. + */ + if (!internal_tmp_table && + (!thd->current_stmt_binlog_row_based || + (thd->current_stmt_binlog_row_based && + !(create_info->options & HA_LEX_CREATE_TMP_TABLE)))) + write_bin_log(thd, TRUE, thd->query, thd->query_length); +} + + /* Create a table @@ -3738,6 +3773,7 @@ bool mysql_create_table_no_lock(THD *thd, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); error= 0; + write_create_table_bin_log(thd, create_info, internal_tmp_table); goto err; } my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); @@ -3858,18 +3894,7 @@ bool mysql_create_table_no_lock(THD *thd, thd->thread_specific_used= TRUE; } - /* - Don't write statement if: - - It is an internal temporary table, - - Row-based logging is used and it we are creating a temporary table, or - - The binary log is not open. - Otherwise, the statement shall be binlogged. - */ - if (!internal_tmp_table && - (!thd->current_stmt_binlog_row_based || - (thd->current_stmt_binlog_row_based && - !(create_info->options & HA_LEX_CREATE_TMP_TABLE)))) - write_bin_log(thd, TRUE, thd->query, thd->query_length); + write_create_table_bin_log(thd, create_info, internal_tmp_table); error= FALSE; unlock_and_end: VOID(pthread_mutex_unlock(&LOCK_open)); @@ -3885,6 +3910,7 @@ warn: ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); create_info->table_existed= 1; // Mark that table existed + write_create_table_bin_log(thd, create_info, internal_tmp_table); goto unlock_and_end; } @@ -3936,6 +3962,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name, table_name); create_info->table_existed= 1; result= FALSE; + write_create_table_bin_log(thd, create_info, internal_tmp_table); } else { @@ -5276,6 +5303,24 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, goto err; /* purecov: inspected */ } +goto binlog; + +table_exists: + if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + { + char warn_buff[MYSQL_ERRMSG_SIZE]; + my_snprintf(warn_buff, sizeof(warn_buff), + ER(ER_TABLE_EXISTS_ERROR), table_name); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TABLE_EXISTS_ERROR,warn_buff); + } + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + goto err; + } + +binlog: DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000);); /* @@ -5339,20 +5384,6 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, write_bin_log(thd, TRUE, thd->query, thd->query_length); res= FALSE; - goto err; - -table_exists: - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - { - char warn_buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(warn_buff, sizeof(warn_buff), - ER(ER_TABLE_EXISTS_ERROR), table_name); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_TABLE_EXISTS_ERROR,warn_buff); - res= FALSE; - } - else - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); err: if (name_lock) From d7c288f600f47f1d8e41ba10de2ead3b74fa65c5 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Thu, 13 Aug 2009 17:21:01 +0100 Subject: [PATCH 20/30] BUG#46130 Slave does not correctly handle "expected errors" In STATEMENT based replication, a statement that failed on the master but that updated non-transactional tables is written to binary log with the error code appended to it. On the slave, the statement is executed and the same error is expected. However, when an "expected error" did not happen on the slave and was either ignored or was related to a concurrency issue on the master, the slave did not rollback the effects of the statement and as such inconsistencies might happen. To fix the problem, we automatically rollback a statement that should have failed on a slave but succeded and whose expected failure is either ignored or stems from a concurrency issue on the master. --- mysql-test/suite/rpl/r/rpl_concurrency_error.result | 2 ++ mysql-test/suite/rpl/t/rpl_concurrency_error.test | 13 ++++++------- sql/log_event.cc | 13 +++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_concurrency_error.result b/mysql-test/suite/rpl/r/rpl_concurrency_error.result index 88ad3da6450..83e5f66a9c4 100644 --- a/mysql-test/suite/rpl/r/rpl_concurrency_error.result +++ b/mysql-test/suite/rpl/r/rpl_concurrency_error.result @@ -101,6 +101,8 @@ master-bin.000001 # Query # # use `test`; UPDATE t SET f = 'dark blue 1' WHERE f master-bin.000001 # Query # # use `test`; INSERT INTO t VALUES (6 + (1 * 10),"brown") master-bin.000001 # Query # # use `test`; INSERT INTO n VALUES (now(),"brown") master-bin.000001 # Xid # # COMMIT /* XID */ +source include/diff_master_slave.inc; +source include/diff_master_slave.inc; ######################################################################## # Cleanup ######################################################################## diff --git a/mysql-test/suite/rpl/t/rpl_concurrency_error.test b/mysql-test/suite/rpl/t/rpl_concurrency_error.test index 816abb5739f..da2951afb1a 100644 --- a/mysql-test/suite/rpl/t/rpl_concurrency_error.test +++ b/mysql-test/suite/rpl/t/rpl_concurrency_error.test @@ -125,14 +125,13 @@ while ($type) connection master; sync_slave_with_master; -# Re-enable this after fixing BUG#46130 -#connection master; -#let $diff_statement= SELECT * FROM t order by i; -#source include/diff_master_slave.inc; +connection master; +let $diff_statement= SELECT * FROM t order by i; +source include/diff_master_slave.inc; -#connection master; -#let $diff_statement= SELECT * FROM n order by d, f; -#source include/diff_master_slave.inc; +connection master; +let $diff_statement= SELECT * FROM n order by d, f; +source include/diff_master_slave.inc; --echo ######################################################################## --echo # Cleanup diff --git a/sql/log_event.cc b/sql/log_event.cc index e7cbbaba38e..49d5478c3c4 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3160,7 +3160,7 @@ compare_errors: /* If we expected a non-zero error code, and we don't get the same error - code, and none of them should be ignored. + code, and it should be ignored or is related to a concurrency issue. */ actual_error= thd->is_error() ? thd->main_da.sql_errno() : 0; DBUG_PRINT("info",("expected_error: %d sql_errno: %d", @@ -3183,7 +3183,8 @@ Default database: '%s'. Query: '%s'", thd->is_slave_error= 1; } /* - If we get the same error code as expected, or they should be ignored. + If we get the same error code as expected and it is not a concurrency + issue, or should be ignored. */ else if ((expected_error == actual_error && !concurrency_error_code(expected_error)) || @@ -3193,6 +3194,14 @@ Default database: '%s'. Query: '%s'", clear_all_errors(thd, const_cast(rli)); thd->killed= THD::NOT_KILLED; } + /* + If we expected a non-zero error code and get nothing and, it is a concurrency + issue or should be ignored. + */ + else if (expected_error && !actual_error && + (concurrency_error_code(expected_error) || + ignored_error_code(expected_error))) + ha_autocommit_or_rollback(thd, TRUE); /* Other cases: mostly we expected no error and get one. */ From 1aec6f7acfcbaeee4c484c622cd53c806b3a09fd Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Fri, 14 Aug 2009 00:49:28 +0500 Subject: [PATCH 21/30] Fix for bug #46614: Assertion in show_create_trigger() on SHOW CREATE TRIGGER + MERGE table Problem: SHOW CREATE TRIGGER erroneously relies on fact that we have the only underlying table for a trigger (wrong for merge tables). Fix: remove erroneous assert(). --- mysql-test/r/merge.result | 12 ++++++++++++ mysql-test/t/merge.test | 11 +++++++++++ sql/sql_show.cc | 2 -- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index d3563e9f1c1..893ea5acf88 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2207,4 +2207,16 @@ ERROR HY000: Table storage engine for 'm1' doesn't have this option DROP TABLE m1,t1,t2,t3,t4,t5,t6,t7; SELECT 1 FROM m1; ERROR 42S02: Table 'test.m1' doesn't exist +# +# Bug #46614: Assertion in show_create_trigger() +# +CREATE TABLE t1(a int); +CREATE TABLE t2(a int); +CREATE TABLE t3(a int) ENGINE = MERGE UNION(t1, t2); +CREATE TRIGGER tr1 AFTER INSERT ON t3 FOR EACH ROW CALL foo(); +SHOW CREATE TRIGGER tr1; +Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation +tr1 CREATE DEFINER=`root`@`localhost` TRIGGER tr1 AFTER INSERT ON t3 FOR EACH ROW CALL foo() latin1 latin1_swedish_ci latin1_swedish_ci +DROP TRIGGER tr1; +DROP TABLE t1, t2, t3; End of 5.1 tests diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 39c805d2b5b..015ae28c155 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -1622,4 +1622,15 @@ DROP TABLE m1,t1,t2,t3,t4,t5,t6,t7; --error ER_NO_SUCH_TABLE SELECT 1 FROM m1; # Should not hang! +--echo # +--echo # Bug #46614: Assertion in show_create_trigger() +--echo # +CREATE TABLE t1(a int); +CREATE TABLE t2(a int); +CREATE TABLE t3(a int) ENGINE = MERGE UNION(t1, t2); +CREATE TRIGGER tr1 AFTER INSERT ON t3 FOR EACH ROW CALL foo(); +SHOW CREATE TRIGGER tr1; +DROP TRIGGER tr1; +DROP TABLE t1, t2, t3; + --echo End of 5.1 tests diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ae75609e2b6..5c2c351652b 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7071,8 +7071,6 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) /* Perform closing actions and return error status. */ } - DBUG_ASSERT(num_tables == 1); - Table_triggers_list *triggers= lst->table->triggers; if (!triggers) From 97dbd987f0f7d32b846b4ee7e311c8b35d1c431e Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Thu, 13 Aug 2009 17:07:20 -0300 Subject: [PATCH 22/30] Bug#46013: rpl_extraColmaster_myisam fails on pb2 Bug#45243: crash on win in sql thread clear_tables_to_lock() -> free() Bug#45242: crash on win in mysql_close() -> free() Bug#45238: rpl_slave_skip, rpl_change_master failed (lost connection) for STOP SLAVE Bug#46030: rpl_truncate_3innodb causes server crash on windows Bug#46014: rpl_stm_reset_slave crashes the server sporadically in pb2 When killing a user session on the server, it's necessary to interrupt (notify) the thread associated with the session that the connection is being killed so that the thread is woken up if waiting for I/O. On a few platforms (Mac, Windows and HP-UX) where the SIGNAL_WITH_VIO_CLOSE flag is defined, this interruption procedure is to asynchronously close the underlying socket of the connection. In order to enable this schema, each connection serving thread registers its VIO (I/O interface) so that other threads can access it and close the connection. But only the owner thread of the VIO might delete it as to guarantee that other threads won't see freed memory (the thread unregisters the VIO before deleting it). A side note: closing the socket introduces a harmless race that might cause a thread attempt to read from a closed socket, but this is deemed acceptable. The problem is that this infrastructure was meant to only be used by server threads, but the slave I/O thread was registering the VIO of a mysql handle (a client API structure that represents a connection to another server instance) as a active connection of the thread. But under some circumstances such as network failures, the client API might destroy the VIO associated with a handle at will, yet the VIO wouldn't be properly unregistered. This could lead to accesses to freed data if a thread attempted to kill a slave I/O thread whose connection was already broken. There was a attempt to work around this by checking whether the socket was being interrupted, but this hack didn't work as intended due to the aforementioned race -- attempting to read from the socket would yield a "bad file descriptor" error. The solution is to add a hook to the client API that is called from the client code before the VIO of a handle is deleted. This hook allows the slave I/O thread to detach the active vio so it does not point to freed memory. --- .../instance-manager/mysql_connection.cc | 12 +++++++++ sql-common/client.c | 3 +++ sql/client_settings.h | 8 ++++++ sql/slave.cc | 25 +++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index 1803108c39d..fc7b4ef591b 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -120,6 +120,18 @@ void my_net_local_init(NET *net) C_MODE_END +/* + Unused stub hook required for linking the client API. +*/ + +C_MODE_START + +void slave_io_thread_detach_vio() +{ +} + +C_MODE_END + /* Every resource, which we can fail to acquire, is allocated in init(). diff --git a/sql-common/client.c b/sql-common/client.c index 5475d5d5160..0cd7ef78478 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -911,6 +911,9 @@ void end_server(MYSQL *mysql) { init_sigpipe_variables DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio))); +#ifdef MYSQL_SERVER + slave_io_thread_detach_vio(); +#endif set_sigpipe(mysql); vio_delete(mysql->net.vio); reset_sigpipe(mysql); diff --git a/sql/client_settings.h b/sql/client_settings.h index f0742cd8046..4f06c15a29e 100644 --- a/sql/client_settings.h +++ b/sql/client_settings.h @@ -33,3 +33,11 @@ #define mysql_server_init(a,b,c) 0 +#ifdef HAVE_REPLICATION +C_MODE_START +void slave_io_thread_detach_vio(); +C_MODE_END +#else +#define slave_io_thread_detach_vio() +#endif + diff --git a/sql/slave.cc b/sql/slave.cc index c5565902832..17855ed04e5 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4737,6 +4737,31 @@ void end_relay_log_info(RELAY_LOG_INFO* rli) DBUG_VOID_RETURN; } + +/** + Hook to detach the active VIO before closing a connection handle. + + The client API might close the connection (and associated data) + in case it encounters a unrecoverable (network) error. This hook + is called from the client code before the VIO handle is deleted + allows the thread to detach the active vio so it does not point + to freed memory. + + Other calls to THD::clear_active_vio throughout this module are + redundant due to the hook but are left in place for illustrative + purposes. +*/ + +extern "C" void slave_io_thread_detach_vio() +{ +#ifdef SIGNAL_WITH_VIO_CLOSE + THD *thd= current_thd; + if (thd->slave_thread) + thd->clear_active_vio(); +#endif +} + + /* Try to connect until successful or slave killed From 0c8690e5694ba0f70ef3a5deedad910d161c36ba Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 19 Aug 2009 10:59:17 +0300 Subject: [PATCH 23/30] backport of Chad's fix for bug #39326 to 5.0-bugteam --- scripts/mysqld_safe.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index e6b015e7d16..71b833c80cf 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -113,16 +113,27 @@ parse_arguments() { MY_PWD=`pwd` # Check for the directories we would expect from a binary release install -if test -f ./share/mysql/english/errmsg.sys -a -x ./bin/mysqld +if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" +then + # BASEDIR is already overridden on command line. Do not re-set. + + # Use BASEDIR to discover le. + if test -x "$MY_BASEDIR_VERSION/libexec/mysqld" + then + ledir="$MY_BASEDIR_VERSION/libexec" + else + ledir="$MY_BASEDIR_VERSION/bin" + fi +elif test -f ./share/mysql/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld" then MY_BASEDIR_VERSION=$MY_PWD # Where bin, share and data are ledir=$MY_BASEDIR_VERSION/bin # Where mysqld is + ledir="$MY_PWD/bin" # Where mysqld is # Check for the directories we would expect from a source install -elif test -f ./share/mysql/english/errmsg.sys -a \ - -x ./libexec/mysqld +elif test -f ./share/mysql/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION=$MY_PWD # Where libexec, share and var are - ledir=$MY_BASEDIR_VERSION/libexec # Where mysqld is + ledir="$MY_PWD/libexec" # Where mysqld is # Since we didn't find anything, used the compiled-in defaults else MY_BASEDIR_VERSION=@prefix@ @@ -181,7 +192,10 @@ err_log= # Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] # and then merge with the command line arguments -if test -x ./bin/my_print_defaults +if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" +then + print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults" +elif test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" elif test -x @bindir@/my_print_defaults From 4207e50e23cd5b964e4223b61048cd2b5729358f Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 19 Aug 2009 15:14:57 +0300 Subject: [PATCH 24/30] Bug #46019: ERROR 1356 When selecting from within another view that has Group By Table access rights checking function check_grant() assumed that no view is opened when it's called. This is not true with nested views where the inner view needs materialization. In this case the view is already materialized when check_grant() is called for it. This caused check_grant() to not look for table level grants on the materialized view table. Fixed by checking if a view is already materialized and if it is check table level grants using the original table name (not the ones of the materialized temp table). --- mysql-test/r/view_grant.result | 24 +++++++++++++++++++++++ mysql-test/t/view_grant.test | 36 ++++++++++++++++++++++++++++++++++ sql/sql_acl.cc | 12 ++++++++---- sql/table.h | 23 ++++++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index e7a50451cec..c9b31b00f69 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -945,4 +945,28 @@ DROP USER foo; DROP VIEW db1.v1; DROP TABLE db1.t1; DROP DATABASE db1; +# +# Bug #46019: ERROR 1356 When selecting from within another +# view that has Group By +# +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1 (a INT); +CREATE SQL SECURITY INVOKER VIEW v1 AS +SELECT a FROM t1 GROUP BY a; +CREATE SQL SECURITY INVOKER VIEW v2 AS +SELECT a FROM v1; +CREATE USER u1; +GRANT SELECT ON TABLE t1 TO u1; +GRANT SELECT, SHOW VIEW ON TABLE v1 TO u1; +GRANT SELECT, SHOW VIEW ON TABLE v2 TO u1; +SELECT a FROM v1; +a +SELECT a FROM v2; +a +DROP USER u1; +DROP VIEW v1,v2; +DROP TABLE t1; +USE test; +DROP DATABASE db1; End of 5.0 tests. diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index ff17cde5184..0a8456afec2 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1232,6 +1232,42 @@ DROP TABLE db1.t1; DROP DATABASE db1; connection default; + +--echo # +--echo # Bug #46019: ERROR 1356 When selecting from within another +--echo # view that has Group By +--echo # + +CREATE DATABASE db1; +USE db1; + +CREATE TABLE t1 (a INT); + +CREATE SQL SECURITY INVOKER VIEW v1 AS + SELECT a FROM t1 GROUP BY a; + +CREATE SQL SECURITY INVOKER VIEW v2 AS + SELECT a FROM v1; + +CREATE USER u1; + +GRANT SELECT ON TABLE t1 TO u1; +GRANT SELECT, SHOW VIEW ON TABLE v1 TO u1; +GRANT SELECT, SHOW VIEW ON TABLE v2 TO u1; + +CONNECT (u1, localhost, u1,,db1); +CONNECTION u1; + +SELECT a FROM v1; +SELECT a FROM v2; + +CONNECTION default; +DISCONNECT u1; +DROP USER u1; +DROP VIEW v1,v2; +DROP TABLE t1; +USE test; +DROP DATABASE db1; --echo End of 5.0 tests. # Wait till we reached the initial number of concurrent sessions diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ab4e518d5dd..a411101fcfd 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3650,11 +3650,14 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, continue; // ok if (!(~table->grant.privilege & want_access) || - table->derived || table->schema_table) + table->is_non_materialized_derived_table() || table->schema_table) { /* It is subquery in the FROM clause. VIEW set table->derived after - table opening, but this function always called before table opening. + table opening, but this function is mostly called before table opening. + When it's called after table opening e.g. for nested views with + materialization we shoud check the materialized table for access as + any other table. */ if (!table->referencing_view) { @@ -3667,9 +3670,10 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } continue; } + if (!(grant_table= table_hash_search(sctx->host, sctx->ip, - table->db, sctx->priv_user, - table->table_name,0))) + table->get_db_name(), sctx->priv_user, + table->get_table_name(), 0))) { want_access &= ~table->grant.privilege; goto err; // No grants diff --git a/sql/table.h b/sql/table.h index db996d45320..ce9364fad4d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -771,6 +771,29 @@ struct TABLE_LIST void reinit_before_use(THD *thd); Item_subselect *containing_subselect(); + /** + @brief True if this TABLE_LIST represents an not yet materialized + derived table, i.e. the result of a subquery or view execution. + */ + bool is_non_materialized_derived_table() const + { + return derived && !derived_result; + } + + /** + @brief Returns the name of the database that the referenced table belongs + to. + */ + char *get_db_name() { return view != NULL ? view_db.str : db; } + + /** + @brief Returns the name of the table that this TABLE_LIST represents. + + @details The unqualified table name or view name for a table or view, + respectively. + */ + char *get_table_name() { return view != NULL ? view_name.str : table_name; } + private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); From f132d6b4c1d3d645354c03d06e6595b5e5c42390 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Wed, 19 Aug 2009 16:38:18 +0100 Subject: [PATCH 25/30] BUG#45694 Deadlock in replicated statement is not retried If the SQL Thread fails to execute an event due to a temporary error (e.g. ER_LOCK_DEADLOCK) and the option "--slave_transaction_retries" is set the SQL Thread should not be aborted and the transaction should be restarted from the beginning and re-executed. Unfortunately, a wrong interpretation of the THD::is_fatal_error was preventing this behavior. In a nutshell, "this variable is set to TRUE if an execution of a compound statement cannot continue. In particular, it is used to disable access to the CONTINUE or EXIT handlers of stored routines. So even temporary errors may have this variable set. To fix the bug, we have done what follows: DBUG_ENTER("has_temporary_error"); - if (thd->is_fatal_error) - DBUG_RETURN(0); - DBUG_EXECUTE_IF("all_errors_are_temporary_errors", if (thd->main_da.is_error()) { --- sql/slave.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 3b64e23ece5..1a903d8ac12 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2010,9 +2010,6 @@ static int has_temporary_error(THD *thd) { DBUG_ENTER("has_temporary_error"); - if (thd->is_fatal_error) - DBUG_RETURN(0); - DBUG_EXECUTE_IF("all_errors_are_temporary_errors", if (thd->main_da.is_error()) { From ea6dc4145c562c9f88055d2fe4ac971e0f9bfa95 Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Thu, 20 Aug 2009 13:56:29 +0200 Subject: [PATCH 26/30] Bug#46616: Assertion `!table->auto_increment_field_not_null' on view manipulations The bespoke flag was not properly reset after last call to fill_record. Fixed by resetting in caller mysql_update. --- mysql-test/r/auto_increment.result | 14 ++++++++++++++ mysql-test/t/auto_increment.test | 16 ++++++++++++++++ sql/sql_update.cc | 1 + 3 files changed, 31 insertions(+) diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 90ba00e1019..44b2e3797b2 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -440,3 +440,17 @@ a val 2 1 3 1 drop table t1; +# +# Bug#46616: Assertion `!table->auto_increment_field_not_null' on view +# manipulations +# +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (1), (1); +CREATE TABLE t2 ( a INT AUTO_INCREMENT KEY ); +CREATE TABLE IF NOT EXISTS t2 AS SELECT a FROM t1; +ERROR 23000: Duplicate entry '1' for key 1 +UPDATE t2 SET a = 2; +SELECT a FROM t2; +a +2 +DROP TABLE t1, t2; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index 5d22bdd46a0..00849a3b131 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -292,3 +292,19 @@ update t1 set a=2 where a=1; insert into t1 (val) values (1); select * from t1; drop table t1; +--echo # +--echo # Bug#46616: Assertion `!table->auto_increment_field_not_null' on view +--echo # manipulations +--echo # +CREATE TABLE t1 ( a INT ); +INSERT INTO t1 VALUES (1), (1); + +CREATE TABLE t2 ( a INT AUTO_INCREMENT KEY ); +--error ER_DUP_ENTRY +CREATE TABLE IF NOT EXISTS t2 AS SELECT a FROM t1; + +UPDATE t2 SET a = 2; + +SELECT a FROM t2; + +DROP TABLE t1, t2; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index f95f0a22a71..c18c34fc1d1 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -527,6 +527,7 @@ int mysql_update(THD *thd, table->file->unlock_row(); thd->row_count++; } + table->auto_increment_field_not_null= FALSE; /* Caching the killed status to pass as the arg to query event constuctor; The cached value can not change whereas the killed status can From f59400f4c390adffd7370b69e885c4aac795105f Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Fri, 21 Aug 2009 10:55:35 +0500 Subject: [PATCH 27/30] Fix for bug #46456 [Ver->Prg]: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash Problem: if one has an open "HANDLER t1", further "TRUNCATE t1" doesn't close the handler and leaves handler table hash in an inconsistent state, that may lead to a server crash. Fix: TRUNCATE should implicitly close all open handlers. Doc. request: the fact should be described in the manual accordingly. --- mysql-test/r/handler_myisam.result | 16 ++++++++++++++++ mysql-test/t/handler_myisam.test | 19 +++++++++++++++++++ sql/sql_delete.cc | 4 ++++ 3 files changed, 39 insertions(+) diff --git a/mysql-test/r/handler_myisam.result b/mysql-test/r/handler_myisam.result index dde6a4586bc..90a1bdfe6be 100644 --- a/mysql-test/r/handler_myisam.result +++ b/mysql-test/r/handler_myisam.result @@ -741,3 +741,19 @@ USE information_schema; HANDLER COLUMNS OPEN; ERROR HY000: Incorrect usage of HANDLER OPEN and information_schema USE test; +# +# BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash +# +CREATE TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +TRUNCATE t1; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +DROP TABLE t1; +CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +TRUNCATE t1; +HANDLER t1 READ FIRST; +ERROR 42S02: Unknown table 't1' in HANDLER +DROP TABLE t1; +End of 5.1 tests diff --git a/mysql-test/t/handler_myisam.test b/mysql-test/t/handler_myisam.test index 644c28de5b2..da02a90af0f 100644 --- a/mysql-test/t/handler_myisam.test +++ b/mysql-test/t/handler_myisam.test @@ -19,3 +19,22 @@ let $other_engine_type= MEMORY; let $other_handler_engine_type= MyISAM; --source include/handler.inc + +--echo # +--echo # BUG #46456: HANDLER OPEN + TRUNCATE + DROP (temporary) TABLE, crash +--echo # +CREATE TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +TRUNCATE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; +DROP TABLE t1; + +CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1; +HANDLER t1 OPEN; +TRUNCATE t1; +--error ER_UNKNOWN_TABLE +HANDLER t1 READ FIRST; +DROP TABLE t1; + +--echo End of 5.1 tests diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0d4bf3f0b8f..d2f90fa9288 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1066,6 +1066,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) DBUG_ENTER("mysql_truncate"); bzero((char*) &create_info,sizeof(create_info)); + + /* Remove tables from the HANDLER's hash. */ + mysql_ha_rm_tables(thd, table_list, FALSE); + /* If it is a temporary table, close and regenerate it */ if (!dont_send_ok && (table= find_temporary_table(thd, table_list))) { From 37cff7c04768a75ac6dface603771a8fe86f8984 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 21 Aug 2009 17:10:55 +0300 Subject: [PATCH 28/30] Revert of the fix for bug #46019. --- mysql-test/r/view_grant.result | 24 ----------------------- mysql-test/t/view_grant.test | 36 ---------------------------------- sql/sql_acl.cc | 12 ++++-------- sql/table.h | 23 ---------------------- 4 files changed, 4 insertions(+), 91 deletions(-) diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index c9b31b00f69..e7a50451cec 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -945,28 +945,4 @@ DROP USER foo; DROP VIEW db1.v1; DROP TABLE db1.t1; DROP DATABASE db1; -# -# Bug #46019: ERROR 1356 When selecting from within another -# view that has Group By -# -CREATE DATABASE db1; -USE db1; -CREATE TABLE t1 (a INT); -CREATE SQL SECURITY INVOKER VIEW v1 AS -SELECT a FROM t1 GROUP BY a; -CREATE SQL SECURITY INVOKER VIEW v2 AS -SELECT a FROM v1; -CREATE USER u1; -GRANT SELECT ON TABLE t1 TO u1; -GRANT SELECT, SHOW VIEW ON TABLE v1 TO u1; -GRANT SELECT, SHOW VIEW ON TABLE v2 TO u1; -SELECT a FROM v1; -a -SELECT a FROM v2; -a -DROP USER u1; -DROP VIEW v1,v2; -DROP TABLE t1; -USE test; -DROP DATABASE db1; End of 5.0 tests. diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 0a8456afec2..ff17cde5184 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1232,42 +1232,6 @@ DROP TABLE db1.t1; DROP DATABASE db1; connection default; - ---echo # ---echo # Bug #46019: ERROR 1356 When selecting from within another ---echo # view that has Group By ---echo # - -CREATE DATABASE db1; -USE db1; - -CREATE TABLE t1 (a INT); - -CREATE SQL SECURITY INVOKER VIEW v1 AS - SELECT a FROM t1 GROUP BY a; - -CREATE SQL SECURITY INVOKER VIEW v2 AS - SELECT a FROM v1; - -CREATE USER u1; - -GRANT SELECT ON TABLE t1 TO u1; -GRANT SELECT, SHOW VIEW ON TABLE v1 TO u1; -GRANT SELECT, SHOW VIEW ON TABLE v2 TO u1; - -CONNECT (u1, localhost, u1,,db1); -CONNECTION u1; - -SELECT a FROM v1; -SELECT a FROM v2; - -CONNECTION default; -DISCONNECT u1; -DROP USER u1; -DROP VIEW v1,v2; -DROP TABLE t1; -USE test; -DROP DATABASE db1; --echo End of 5.0 tests. # Wait till we reached the initial number of concurrent sessions diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index a411101fcfd..ab4e518d5dd 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3650,14 +3650,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, continue; // ok if (!(~table->grant.privilege & want_access) || - table->is_non_materialized_derived_table() || table->schema_table) + table->derived || table->schema_table) { /* It is subquery in the FROM clause. VIEW set table->derived after - table opening, but this function is mostly called before table opening. - When it's called after table opening e.g. for nested views with - materialization we shoud check the materialized table for access as - any other table. + table opening, but this function always called before table opening. */ if (!table->referencing_view) { @@ -3670,10 +3667,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } continue; } - if (!(grant_table= table_hash_search(sctx->host, sctx->ip, - table->get_db_name(), sctx->priv_user, - table->get_table_name(), 0))) + table->db, sctx->priv_user, + table->table_name,0))) { want_access &= ~table->grant.privilege; goto err; // No grants diff --git a/sql/table.h b/sql/table.h index ce9364fad4d..db996d45320 100644 --- a/sql/table.h +++ b/sql/table.h @@ -771,29 +771,6 @@ struct TABLE_LIST void reinit_before_use(THD *thd); Item_subselect *containing_subselect(); - /** - @brief True if this TABLE_LIST represents an not yet materialized - derived table, i.e. the result of a subquery or view execution. - */ - bool is_non_materialized_derived_table() const - { - return derived && !derived_result; - } - - /** - @brief Returns the name of the database that the referenced table belongs - to. - */ - char *get_db_name() { return view != NULL ? view_db.str : db; } - - /** - @brief Returns the name of the table that this TABLE_LIST represents. - - @details The unqualified table name or view name for a table or view, - respectively. - */ - char *get_table_name() { return view != NULL ? view_name.str : table_name; } - private: bool prep_check_option(THD *thd, uint8 check_opt_type); bool prep_where(THD *thd, Item **conds, bool no_where_clause); From 586ee5d616f55120aaa31f98be60ccae0523cd6c Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 21 Aug 2009 17:38:29 +0200 Subject: [PATCH 29/30] Bug#46639: 1030 (HY000): Got error 124 from storage engine on INSERT ... SELECT ... Problem was that when bulk insert is used on an empty table/partition, it disables the indexes for better performance, but in this specific case it also tries to read from that partition using an index, which is not possible since it has been disabled. Solution was to allow index reads on disabled indexes if there are no records. Also reverted the patch for bug#38005, since that was a workaround in the partitioning engine instead of a fix in myisam. --- mysql-test/r/partition.result | 14 ++++++++++++++ mysql-test/t/partition.test | 22 ++++++++++++++++++++++ sql/ha_partition.cc | 33 --------------------------------- storage/myisam/mi_search.c | 10 ++++++++-- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 40c2728ce26..2d54a66fe11 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1,5 +1,19 @@ drop table if exists t1, t2; CREATE TABLE t1 ( +a int NOT NULL, +b int NOT NULL); +CREATE TABLE t2 ( +a int NOT NULL, +b int NOT NULL, +INDEX(b) +) +PARTITION BY HASH(a) PARTITIONS 2; +INSERT INTO t1 VALUES (399, 22); +INSERT INTO t2 VALUES (1, 22), (1, 42); +INSERT INTO t2 SELECT 1, 399 FROM t2, t1 +WHERE t1.b = t2.b; +DROP TABLE t1, t2; +CREATE TABLE t1 ( a timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, b varchar(10), PRIMARY KEY (a) diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 0363327a675..c5ed098b678 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -14,6 +14,28 @@ drop table if exists t1, t2; --enable_warnings +# +# Bug#46639: 1030 (HY000): Got error 124 from storage engine on +# INSERT ... SELECT ... +CREATE TABLE t1 ( + a int NOT NULL, + b int NOT NULL); + +CREATE TABLE t2 ( + a int NOT NULL, + b int NOT NULL, + INDEX(b) +) +PARTITION BY HASH(a) PARTITIONS 2; + +INSERT INTO t1 VALUES (399, 22); +INSERT INTO t2 VALUES (1, 22), (1, 42); + +INSERT INTO t2 SELECT 1, 399 FROM t2, t1 +WHERE t1.b = t2.b; + +DROP TABLE t1, t2; + # # Bug#46478: timestamp field incorrectly defaulted when partition is reorganized # diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b345648260c..ac8c46ec4e3 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4414,17 +4414,6 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) break; case partition_index_first: DBUG_PRINT("info", ("index_first on partition %d", i)); - /* MyISAM engine can fail if we call index_first() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ - if (file->stats.records == 0) - { - error= HA_ERR_END_OF_FILE; - break; - } error= file->index_first(buf); break; case partition_index_first_unordered: @@ -4512,32 +4501,10 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) m_start_key.flag); break; case partition_index_first: - /* MyISAM engine can fail if we call index_first() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ - if (file->stats.records == 0) - { - error= HA_ERR_END_OF_FILE; - break; - } error= file->index_first(rec_buf_ptr); reverse_order= FALSE; break; case partition_index_last: - /* MyISAM engine can fail if we call index_last() when indexes disabled */ - /* that happens if the table is empty. */ - /* Here we use file->stats.records instead of file->records() because */ - /* file->records() is supposed to return an EXACT count, and it can be */ - /* possibly slow. We don't need an exact number, an approximate one- from*/ - /* the last ::info() call - is sufficient. */ - if (file->stats.records == 0) - { - error= HA_ERR_END_OF_FILE; - break; - } error= file->index_last(rec_buf_ptr); reverse_order= TRUE; break; diff --git a/storage/myisam/mi_search.c b/storage/myisam/mi_search.c index 1dd6c6b5f0d..766e54bde30 100644 --- a/storage/myisam/mi_search.c +++ b/storage/myisam/mi_search.c @@ -28,9 +28,15 @@ int _mi_check_index(MI_INFO *info, int inx) { if (inx == -1) /* Use last index */ inx=info->lastinx; - if (inx < 0 || ! mi_is_key_active(info->s->state.key_map, inx)) + if (inx < 0) { - my_errno=HA_ERR_WRONG_INDEX; + my_errno= HA_ERR_WRONG_INDEX; + return -1; + } + if (!mi_is_key_active(info->s->state.key_map, inx)) + { + my_errno= info->s->state.state.records ? HA_ERR_WRONG_INDEX : + HA_ERR_END_OF_FILE; return -1; } if (info->lastinx != inx) /* Index changed */ From 31afccc4073e1859382bf979b1596e260aaee0b3 Mon Sep 17 00:00:00 2001 From: Davi Arnaut Date: Mon, 24 Aug 2009 16:47:08 -0300 Subject: [PATCH 30/30] Bug#45261: Crash, stored procedure + decimal The problem was that creating a DECIMAL column from a decimal value could lead to a failed assertion as decimal values can have a higher precision than those attached to a table. The assert could be triggered by creating a table from a decimal with a large (> 30) scale. Also, there was a problem in calculating the number of digits in the integral and fractional parts if both exceeded the maximum number of digits permitted by the new decimal type. The solution is to ensure that truncation procedure is executed when deducing a DECIMAL column from a decimal value of higher precision. If the integer part is equal to or bigger than the maximum precision for the DECIMAL type (65), the integer part is truncated to fit and the fractional becomes zero. Otherwise, the fractional part is truncated to fit into the space left after the integer part is copied. This patch borrows code and ideas from Martin Hansson's patch. --- mysql-test/r/type_newdecimal.result | 220 +++++++++++++++++++++++++++- mysql-test/t/type_newdecimal.test | 134 +++++++++++++++++ sql/field.cc | 85 +++++++++++ sql/field.h | 9 ++ sql/item.cc | 25 ++-- sql/item.h | 3 +- sql/item_cmpfunc.cc | 6 +- sql/item_func.cc | 52 ++----- sql/item_func.h | 1 + sql/item_sum.cc | 3 +- sql/my_decimal.h | 14 +- sql/sql_select.cc | 41 +----- 12 files changed, 483 insertions(+), 110 deletions(-) diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 748aadee4fb..c3d1e400b23 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1495,9 +1495,9 @@ CREATE TABLE t1 (a int DEFAULT NULL, b int DEFAULT NULL); INSERT INTO t1 VALUES (3,30), (1,10), (2,10); SELECT a+CAST(1 AS decimal(65,30)) AS aa, SUM(b) FROM t1 GROUP BY aa; aa SUM(b) -2.000000000000000000000000000000 10 -3.000000000000000000000000000000 10 -4.000000000000000000000000000000 30 +2.00000000000000000000000000000 10 +3.00000000000000000000000000000 10 +4.00000000000000000000000000000 30 SELECT a+CAST(1 AS decimal(65,31)) AS aa, SUM(b) FROM t1 GROUP BY aa; ERROR 42000: Too big scale 31 specified for column '1'. Maximum is 30. DROP TABLE t1; @@ -1521,13 +1521,13 @@ f1 DROP TABLE t1; CREATE TABLE t1 SELECT 123451234512345123451234512345123451234512345.678906789067890678906789067890678906789067890 AS f1; Warnings: -Warning 1264 Out of range value for column 'f1' at row 1 +Note 1265 Data truncated for column 'f1' at row 1 DESC t1; Field Type Null Key Default Extra -f1 decimal(65,30) NO 0.000000000000000000000000000000 +f1 decimal(65,20) NO 0.00000000000000000000 SELECT f1 FROM t1; f1 -99999999999999999999999999999999999.999999999999999999999999999999 +123451234512345123451234512345123451234512345.67890678906789067891 DROP TABLE t1; select (1.20396873 * 0.89550000 * 0.68000000 * 1.08721696 * 0.99500000 * 1.01500000 * 1.01500000 * 0.99500000); @@ -1595,7 +1595,7 @@ Warnings: Note 1265 Data truncated for column 'my_col' at row 1 DESCRIBE t1; Field Type Null Key Default Extra -my_col decimal(65,30) NO 0.000000000000000000000000000000 +my_col decimal(32,30) NO 0.000000000000000000000000000000 SELECT my_col FROM t1; my_col 1.123456789123456789123456789123 @@ -1625,8 +1625,212 @@ Warnings: Note 1265 Data truncated for column 'my_col' at row 1 DESCRIBE t1; Field Type Null Key Default Extra -my_col decimal(65,30) YES NULL +my_col decimal(30,30) YES NULL SELECT my_col FROM t1; my_col 0.012345687012345687012345687012 DROP TABLE t1; +# +# Bug#45261: Crash, stored procedure + decimal +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001 +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001. +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001.1 /* 1 */ +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 82 */ 1000000000000000000000000000000000000000000000000000000000000000000000000000000001 +AS c1; +Warnings: +Error 1292 Truncated incorrect DECIMAL value: '' +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 40 */ 1000000000000000000000000000000000000001.1000000000000000000000000000000000000001 /* 40 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,25) NO 0.0000000000000000000000000 +SELECT * FROM t1; +c1 +1000000000000000000000000000000000000001.1000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 1 */ 1.10000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 80 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(31,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 1 */ 1.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ +AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(31,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(30,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +0.100000000000000000000000000000 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 45 */ 123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345 /* 45 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,20) NO 0.00000000000000000000 +SELECT * FROM t1; +c1 +123456789012345678901234567890123456789012345.12345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 65 */ 12345678901234567890123456789012345678901234567890123456789012345.1 /* 1 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +12345678901234567890123456789012345678901234567890123456789012345 +DROP TABLE t1; +CREATE TABLE t1 SELECT +/* 66 */ 123456789012345678901234567890123456789012345678901234567890123456.1 /* 1 */ +AS c1; +Warnings: +Warning 1264 Out of range value for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,0) NO 0 +SELECT * FROM t1; +c1 +99999999999999999999999999999999999999999999999999999999999999999 +DROP TABLE t1; +CREATE TABLE t1 SELECT +.123456789012345678901234567890123456789012345678901234567890123456 /* 66 */ +AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(30,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +0.123456789012345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 AS SELECT 123.1234567890123456789012345678901 /* 31 */ AS c1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t1; +Field Type Null Key Default Extra +c1 decimal(33,30) NO 0.000000000000000000000000000000 +SELECT * FROM t1; +c1 +123.123456789012345678901234567890 +DROP TABLE t1; +CREATE TABLE t1 SELECT 1.1 + CAST(1 AS DECIMAL(65,30)) AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,29) NO 0.00000000000000000000000000000 +SELECT * FROM t1; +c1 +2.10000000000000000000000000000 +DROP TABLE t1; +# +# Test that the integer and decimal parts are properly calculated. +# +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT MIN(a + 0.0000000000000000000000000000001) AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 3 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(32,30) YES NULL +DROP TABLE t1,t2; +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT IFNULL(a + 0.0000000000000000000000000000001, NULL) AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +Note 1265 Data truncated for column 'c1' at row 2 +Note 1265 Data truncated for column 'c1' at row 3 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(32,30) YES NULL +DROP TABLE t1,t2; +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT CASE a WHEN 0.1 THEN 0.0000000000000000000000000000000000000000000000000000000000000000001 END AS c1 FROM t1; +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +DESC t2; +Field Type Null Key Default Extra +c1 decimal(31,30) YES NULL +DROP TABLE t1,t2; +# +# Test that variables get maximum precision. +# +SET @decimal= 1.1; +CREATE TABLE t1 SELECT @decimal AS c1; +DESC t1; +Field Type Null Key Default Extra +c1 decimal(65,30) YES NULL +SELECT * FROM t1; +c1 +1.100000000000000000000000000000 +DROP TABLE t1; diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index cd3c3f81510..65bafaae77e 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1286,3 +1286,137 @@ CREATE TABLE t1 SELECT 1 % .1234567891234567891234567891234567891234567891234567 DESCRIBE t1; SELECT my_col FROM t1; DROP TABLE t1; + +--echo # +--echo # Bug#45261: Crash, stored procedure + decimal +--echo # + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001 + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001. + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 81 */ 100000000000000000000000000000000000000000000000000000000000000000000000000000001.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 82 */ 1000000000000000000000000000000000000000000000000000000000000000000000000000000001 + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 40 */ 1000000000000000000000000000000000000001.1000000000000000000000000000000000000001 /* 40 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 1 */ 1.10000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 80 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 1 */ 1.100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + .100000000000000000000000000000000000000000000000000000000000000000000000000000001 /* 81 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 45 */ 123456789012345678901234567890123456789012345.123456789012345678901234567890123456789012345 /* 45 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 65 */ 12345678901234567890123456789012345678901234567890123456789012345.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + /* 66 */ 123456789012345678901234567890123456789012345678901234567890123456.1 /* 1 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT + .123456789012345678901234567890123456789012345678901234567890123456 /* 66 */ + AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 AS SELECT 123.1234567890123456789012345678901 /* 31 */ AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 SELECT 1.1 + CAST(1 AS DECIMAL(65,30)) AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; + +--echo # +--echo # Test that the integer and decimal parts are properly calculated. +--echo # + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT MIN(a + 0.0000000000000000000000000000001) AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT IFNULL(a + 0.0000000000000000000000000000001, NULL) AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +CREATE TABLE t1 (a DECIMAL(30,30)); +INSERT INTO t1 VALUES (0.1),(0.2),(0.3); +CREATE TABLE t2 SELECT CASE a WHEN 0.1 THEN 0.0000000000000000000000000000000000000000000000000000000000000000001 END AS c1 FROM t1; +DESC t2; +DROP TABLE t1,t2; + +--echo # +--echo # Test that variables get maximum precision. +--echo # + +SET @decimal= 1.1; +CREATE TABLE t1 SELECT @decimal AS c1; +DESC t1; +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/field.cc b/sql/field.cc index 3bfb54fbd15..426effa57cd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2485,12 +2485,97 @@ Field_new_decimal::Field_new_decimal(uint32 len_arg, { precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); set_if_smaller(precision, DECIMAL_MAX_PRECISION); + DBUG_ASSERT(precision >= dec); DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && (dec <= DECIMAL_MAX_SCALE)); bin_size= my_decimal_get_binary_size(precision, dec); } +/** + Create a field to hold a decimal value from an item. + + @remark The MySQL DECIMAL data type has a characteristic that needs to be + taken into account when deducing the type from a Item_decimal. + + But first, let's briefly recap what is the new MySQL DECIMAL type: + + The declaration syntax for a decimal is DECIMAL(M,D), where: + + * M is the maximum number of digits (the precision). + It has a range of 1 to 65. + * D is the number of digits to the right of the decimal separator (the scale). + It has a range of 0 to 30 and must be no larger than M. + + D and M are used to determine the storage requirements for the integer + and fractional parts of each value. The integer part is to the left of + the decimal separator and to the right is the fractional part. Hence: + + M is the number of digits for the integer and fractional part. + D is the number of digits for the fractional part. + + Consequently, M - D is the number of digits for the integer part. For + example, a DECIMAL(20,10) column has ten digits on either side of + the decimal separator. + + The characteristic that needs to be taken into account is that the + backing type for Item_decimal is a my_decimal that has a higher + precision (DECIMAL_MAX_POSSIBLE_PRECISION, see my_decimal.h) than + DECIMAL. + + Drawing a comparison between my_decimal and DECIMAL: + + * M has a range of 1 to 81. + * D has a range of 0 to 81. + + There can be a difference in range if the decimal contains a integer + part. This is because the fractional part must always be on a group + boundary, leaving at least one group for the integer part. Since each + group is 9 (DIG_PER_DEC1) digits and there are 9 (DECIMAL_BUFF_LENGTH) + groups, the fractional part is limited to 72 digits if there is at + least one digit in the integral part. + + Although the backing type for a DECIMAL is also my_decimal, every + time a my_decimal is stored in a DECIMAL field, the precision and + scale are explicitly capped at 65 (DECIMAL_MAX_PRECISION) and 30 + (DECIMAL_MAX_SCALE) digits, following my_decimal truncation procedure + (FIX_INTG_FRAC_ERROR). +*/ + +Field_new_decimal * +Field_new_decimal::new_decimal_field(const Item *item) +{ + uint32 len; + uint intg= item->decimal_int_part(), scale= item->decimals; + + DBUG_ASSERT(item->decimal_precision() >= item->decimals); + + /* + Employ a procedure along the lines of the my_decimal truncation process: + - If the integer part is equal to or bigger than the maximum precision: + Truncate integer part to fit and the fractional becomes zero. + - Otherwise: + Truncate fractional part to fit. + */ + if (intg >= DECIMAL_MAX_PRECISION) + { + intg= DECIMAL_MAX_PRECISION; + scale= 0; + } + else + { + uint room= min(DECIMAL_MAX_PRECISION - intg, DECIMAL_MAX_SCALE); + if (scale > room) + scale= room; + } + + len= my_decimal_precision_to_length(intg + scale, scale, item->unsigned_flag); + + return new Field_new_decimal(len, item->maybe_null, item->name, scale, + item->unsigned_flag); +} + + int Field_new_decimal::reset(void) { store_value(&decimal_zero); diff --git a/sql/field.h b/sql/field.h index 5136f760fc1..a9299256f88 100644 --- a/sql/field.h +++ b/sql/field.h @@ -608,6 +608,10 @@ protected: class Field_num :public Field { public: + /** + The scale of the Field's value, i.e. the number of digits to the right + of the decimal point. + */ const uint8 dec; bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, @@ -766,6 +770,11 @@ public: Field_new_decimal(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, uint8 dec_arg, bool unsigned_arg); + /* + Create a field to hold a decimal value from an item. + Truncates the precision and/or scale if necessary. + */ + static Field_new_decimal *new_decimal_field(const Item *item); enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } Item_result result_type () const { return DECIMAL_RESULT; } diff --git a/sql/item.cc b/sql/item.cc index 2640b74851b..9551c5feaff 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -435,17 +435,26 @@ Item::Item(THD *thd, Item *item): } +/** + Decimal precision of the item. + + @remark The precision must not be capped as it can be used in conjunction + with Item::decimals to determine the size of the integer part when + constructing a decimal data type. + + @see Item::decimal_int_part() + @see Item::decimals +*/ + uint Item::decimal_precision() const { + uint precision= max_length; Item_result restype= result_type(); if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - { - uint prec= - my_decimal_length_to_precision(max_length, decimals, unsigned_flag); - return min(prec, DECIMAL_MAX_PRECISION); - } - return min(max_length, DECIMAL_MAX_PRECISION); + precision= my_decimal_length_to_precision(max_length, decimals, unsigned_flag); + + return precision; } @@ -4902,9 +4911,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length) switch (field_type()) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - field= new Field_new_decimal((uchar*) 0, max_length, null_ptr, 0, - Field::NONE, name, decimals, 0, - unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; case MYSQL_TYPE_TINY: field= new Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, diff --git a/sql/item.h b/sql/item.h index 3dfcd7c2612..464085cb101 100644 --- a/sql/item.h +++ b/sql/item.h @@ -755,9 +755,10 @@ public: virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} + /** Returns the uncapped decimal precision of this item. */ virtual uint decimal_precision() const; inline int decimal_int_part() const - { return my_decimal_int_part(decimal_precision(), decimals); } + { return decimal_precision() - decimals; } /* Returns true if this is constant (during query execution, i.e. its value will not change until next fix_fields) and its value is known. diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1ff9ca6a419..53feb753844 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2183,7 +2183,7 @@ uint Item_func_ifnull::decimal_precision() const int arg1_int_part= args[1]->decimal_int_part(); int max_int_part= max(arg0_int_part, arg1_int_part); int precision= max_int_part + decimals; - return min(precision, DECIMAL_MAX_PRECISION); + return precision; } @@ -2367,7 +2367,7 @@ uint Item_func_if::decimal_precision() const int arg1_prec= args[1]->decimal_int_part(); int arg2_prec= args[2]->decimal_int_part(); int precision=max(arg1_prec,arg2_prec) + decimals; - return min(precision, DECIMAL_MAX_PRECISION); + return precision; } @@ -2775,7 +2775,7 @@ uint Item_func_case::decimal_precision() const if (else_expr_num != -1) set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part()); - return min(max_int_part + decimals, DECIMAL_MAX_PRECISION); + return max_int_part + decimals; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 55d4b37ddb0..6abc78371db 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -452,45 +452,8 @@ Field *Item_func::tmp_table_field(TABLE *table) return make_string_field(table); break; case DECIMAL_RESULT: - { - uint8 dec= decimals; - uint8 intg= decimal_precision() - dec; - uint32 len= max_length; - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - int overflow; - - dec= min(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= max(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - - field= new Field_new_decimal(len, maybe_null, name, dec, unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; - } case ROW_RESULT: default: // This case should never be chosen @@ -4781,6 +4744,19 @@ void Item_func_get_user_var::fix_length_and_dec() } +uint Item_func_get_user_var::decimal_precision() const +{ + uint precision= max_length; + Item_result restype= result_type(); + + /* Default to maximum as the precision is unknown a priori. */ + if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) + precision= DECIMAL_MAX_PRECISION; + + return precision; +} + + bool Item_func_get_user_var::const_item() const { return (!var_entry || current_thd->query_id != var_entry->update_query_id); diff --git a/sql/item_func.h b/sql/item_func.h index 025ac12fe07..fdbbff89e60 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1393,6 +1393,7 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; + uint decimal_precision() const; private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 38251294053..08a48c6ce2f 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -517,8 +517,7 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table, name, table->s, collation.collation); break; case DECIMAL_RESULT: - field= new Field_new_decimal(max_length, maybe_null, name, - decimals, unsigned_flag); + field= Field_new_decimal::new_decimal_field(this); break; case ROW_RESULT: default: diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 21669e82c44..b1df1395dcd 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -48,10 +48,12 @@ C_MODE_END digits * number of decimal digits in one our big digit - number of decimal digits in one our big digit decreased by 1 (because we always put decimal point on the border of our big digits)) + + This value is 65 due to historical reasons partly due to it being used + as the maximum allowed precision and not the actual maximum precision. */ #define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) #define DECIMAL_MAX_SCALE 30 -#define DECIMAL_NOT_SPECIFIED 31 /** maximum length of string representation (number of maximum decimal @@ -75,12 +77,6 @@ inline uint my_decimal_size(uint precision, uint scale) } -inline int my_decimal_int_part(uint precision, uint decimals) -{ - return precision - ((decimals == DECIMAL_NOT_SPECIFIED) ? 0 : decimals); -} - - /** my_decimal class limits 'decimal_t' type to what we need in MySQL. @@ -184,7 +180,7 @@ inline uint my_decimal_length_to_precision(uint length, uint scale, } inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, - uint8 scale, + uint scale, bool unsigned_flag) { /* @@ -196,7 +192,7 @@ inline uint32 my_decimal_precision_to_length_no_truncation(uint precision, (unsigned_flag || !precision ? 0 : 1)); } -inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale, +inline uint32 my_decimal_precision_to_length(uint precision, uint scale, bool unsigned_flag) { /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7f6c5e834a3..5cb0de1ba5c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9377,47 +9377,8 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, new_field->set_derivation(item->collation.derivation); break; case DECIMAL_RESULT: - { - uint8 dec= item->decimals; - uint8 intg= ((Item_decimal *) item)->decimal_precision() - dec; - uint32 len= item->max_length; - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - signed int overflow; - - dec= min(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - +1: for decimal point - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - item->unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= max(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - - new_field= new Field_new_decimal(len, maybe_null, item->name, - dec, item->unsigned_flag); + new_field= Field_new_decimal::new_decimal_field(item); break; - } case ROW_RESULT: default: // This case should never be choosen