From 64accb36bf824354960e5a23078af80adeb3691d Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Thu, 2 Oct 2008 10:46:14 +0300 Subject: [PATCH 01/11] Bug #36968 rpl_temporary_errors.test produces warning in pushbuild backporting a part of the bug patch to 5.1.29 tree which uses an older version of mtr. mysql-test/lib/mtr_report.pl: refining a suppression rule. --- mysql-test/lib/mtr_report.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/lib/mtr_report.pl b/mysql-test/lib/mtr_report.pl index 3d593697ef6..3c78c3ca064 100644 --- a/mysql-test/lib/mtr_report.pl +++ b/mysql-test/lib/mtr_report.pl @@ -312,7 +312,7 @@ sub mtr_report_stats ($) { /Slave: According to the master's version/ or /Slave: Column [0-9]* type mismatch/ or /Slave: Error .* doesn't exist/ or - /Slave: Error .*Deadlock found/ or + /Slave: Deadlock found/ or /Slave: Error .*Unknown table/ or /Slave: Error in Write_rows event: / or /Slave: Field .* of table .* has no default value/ or From 8d9cf89e96fa7606ed76ae5314a6dd0b4ee7fb44 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Thu, 2 Oct 2008 11:02:38 +0200 Subject: [PATCH 02/11] Bug #38360: BLACKHOLE replication with RBR is broken The Blackhole engine did not support row-based replication since the delete_row(), update_row(), and the index and range searching functions were not implemented. This patch adds row-based replication support for the Blackhole engine by implementing the two functions mentioned above, and making the engine pretend that it has found the correct row to delete or update when executed from the slave SQL thread by implementing index and range searching functions. It is necessary to only pretend this for the SQL thread, since a SELECT executed on the Blackhole engine will otherwise never return EOF, causing a livelock. mysql-test/extra/binlog_tests/blackhole.test: Blackhole now handles row-based replication. mysql-test/extra/rpl_tests/rpl_blackhole.test: Test helper file for testing that blackhole actually writes something to the binary log on the slave. mysql-test/suite/binlog/t/binlog_multi_engine.test: Replication now handles row-based replcation. mysql-test/suite/rpl/t/rpl_blackhole.test: Test that Blackhole works with primary key, index, or none. sql/log_event.cc: Correcting code to only touch filler bits and leave all other bits alone. It is necessary since there is no guarantee that the engine will be able to fill in the bits correctly (e.g., the blackhole engine). storage/blackhole/ha_blackhole.cc: Adding definitions for update_row() and delete_row() to return OK when executed from the slave SQL thread with thd->query == NULL (indicating that row-based replication events are being processed). Changing rnd_next(), index_read(), index_read_idx(), and index_read_last() to return OK when executed from the slave SQL thread (faking that the row has been found so that processing proceeds to update/delete the row). storage/blackhole/ha_blackhole.h: Enabling row capabilities for engine. Defining write_row(), update_row(), and delete_row(). Making write_row() private (as it should be). --- mysql-test/extra/binlog_tests/blackhole.test | 9 -- mysql-test/extra/rpl_tests/rpl_blackhole.test | 19 ++++ .../suite/binlog/r/binlog_multi_engine.result | 11 +- .../binlog/r/binlog_stm_blackhole.result | 1 - .../suite/binlog/t/binlog_multi_engine.test | 7 +- mysql-test/suite/rpl/r/rpl_blackhole.result | 100 ++++++++++++++++++ mysql-test/suite/rpl/t/rpl_blackhole.test | 66 ++++++++++++ sql/log_event.cc | 8 +- storage/blackhole/ha_blackhole.cc | 31 ++++++ storage/blackhole/ha_blackhole.h | 7 +- 10 files changed, 231 insertions(+), 28 deletions(-) create mode 100644 mysql-test/extra/rpl_tests/rpl_blackhole.test create mode 100644 mysql-test/suite/rpl/r/rpl_blackhole.result create mode 100644 mysql-test/suite/rpl/t/rpl_blackhole.test diff --git a/mysql-test/extra/binlog_tests/blackhole.test b/mysql-test/extra/binlog_tests/blackhole.test index 2d63ae27aa3..e8671ed2da0 100644 --- a/mysql-test/extra/binlog_tests/blackhole.test +++ b/mysql-test/extra/binlog_tests/blackhole.test @@ -139,15 +139,6 @@ drop table t1,t2,t3; # table # CREATE TABLE t1(a INT) ENGINE=BLACKHOLE; -# NOTE: After exchanging open_ltable() by open_and_lock_tables() in -# handle_delayed_insert() to fix problems with MERGE tables (Bug#26379), -# problems with INSERT DELAYED and BLACKHOLE popped up. open_ltable() -# does not check if the binlogging capabilities of the statement and the -# table match. So the below used to succeed. But since INSERT DELAYED -# switches to row-based logging in mixed-mode and BLACKHOLE cannot do -# row-based logging, it could not really work. Until this problem is -# correctly fixed, we have that error here. ---error ER_BINLOG_LOGGING_IMPOSSIBLE INSERT DELAYED INTO t1 VALUES(1); DROP TABLE t1; diff --git a/mysql-test/extra/rpl_tests/rpl_blackhole.test b/mysql-test/extra/rpl_tests/rpl_blackhole.test new file mode 100644 index 00000000000..594acbee020 --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_blackhole.test @@ -0,0 +1,19 @@ +connection slave; +let $before = query_get_value("SHOW MASTER STATUS", Position, 1); + +--echo [on master] +connection master; +eval $statement; + +--echo [on slave] +sync_slave_with_master; +--echo # Expect 0 +SELECT COUNT(*) FROM t1; +let $after = query_get_value("SHOW MASTER STATUS", Position, 1); +let $something_written = `select $after - $before != 0`; +if ($something_written) { + --echo >>> Something was written to binary log <<< +} +if (!$something_written) { + --echo >>> Nothing was written to binary log <<< +} diff --git a/mysql-test/suite/binlog/r/binlog_multi_engine.result b/mysql-test/suite/binlog/r/binlog_multi_engine.result index ffe7915f1f8..a295657b8f8 100644 --- a/mysql-test/suite/binlog/r/binlog_multi_engine.result +++ b/mysql-test/suite/binlog/r/binlog_multi_engine.result @@ -43,8 +43,6 @@ INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging -UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; -ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging TRUNCATE t1m; TRUNCATE t1b; TRUNCATE t1n; @@ -68,20 +66,21 @@ RESET MASTER; SET SESSION BINLOG_FORMAT=ROW; INSERT INTO t1m VALUES (1,1), (1,2), (2,1), (2,2); INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2); -ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); -UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; -ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; -ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines +ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query # # use `test`; BEGIN master-bin.000001 # Table_map # # table_id: # (test.t1m) master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # use `test`; COMMIT +master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Table_map # # table_id: # (test.t1b) +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # use `test`; COMMIT master-bin.000001 # Query # # BEGIN master-bin.000001 # Table_map # # table_id: # (test.t1n) master-bin.000001 # Table_map # # table_id: # (mysql.ndb_apply_status) diff --git a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result index 619fd39a2a9..30f298488fc 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_blackhole.result +++ b/mysql-test/suite/binlog/r/binlog_stm_blackhole.result @@ -141,7 +141,6 @@ master-bin.000001 # Query # # use `test`; COMMIT drop table t1,t2,t3; CREATE TABLE t1(a INT) ENGINE=BLACKHOLE; INSERT DELAYED INTO t1 VALUES(1); -ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines DROP TABLE t1; CREATE TABLE t1(a INT, b INT) ENGINE=BLACKHOLE; DELETE FROM t1 WHERE a=10; diff --git a/mysql-test/suite/binlog/t/binlog_multi_engine.test b/mysql-test/suite/binlog/t/binlog_multi_engine.test index bf84eed6ec1..c8136d669e4 100644 --- a/mysql-test/suite/binlog/t/binlog_multi_engine.test +++ b/mysql-test/suite/binlog/t/binlog_multi_engine.test @@ -69,9 +69,6 @@ UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; #UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; -error ER_BINLOG_LOGGING_IMPOSSIBLE; -UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; - TRUNCATE t1m; TRUNCATE t1b; TRUNCATE t1n; @@ -83,12 +80,10 @@ RESET MASTER; SET SESSION BINLOG_FORMAT=ROW; INSERT INTO t1m VALUES (1,1), (1,2), (2,1), (2,2); -error ER_BINLOG_LOGGING_IMPOSSIBLE; + INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2); INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); -error ER_BINLOG_LOGGING_IMPOSSIBLE; -UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; error ER_BINLOG_LOGGING_IMPOSSIBLE; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; diff --git a/mysql-test/suite/rpl/r/rpl_blackhole.result b/mysql-test/suite/rpl/r/rpl_blackhole.result new file mode 100644 index 00000000000..27094e761b0 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_blackhole.result @@ -0,0 +1,100 @@ +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; +CREATE TABLE t1 (a INT, b INT, c INT); +CREATE TABLE t2 (a INT, b INT, c INT); +ALTER TABLE t1 ENGINE=BLACKHOLE; +INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4); +[on master] +INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4); +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +DELETE FROM t1 WHERE a % 2 = 0 AND b = 1; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +INSERT INTO t1 SELECT * FROM t2; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +INSERT INTO t2 SELECT * FROM t1; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b); +[on master] +INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4); +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +DELETE FROM t1 WHERE a % 2 = 0 AND b = 2; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a); +[on master] +INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4); +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< +[on master] +DELETE FROM t1 WHERE a % 2 = 0 AND b = 3; +[on slave] +# Expect 0 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +>>> Something was written to binary log <<< diff --git a/mysql-test/suite/rpl/t/rpl_blackhole.test b/mysql-test/suite/rpl/t/rpl_blackhole.test new file mode 100644 index 00000000000..c25c4954dfa --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_blackhole.test @@ -0,0 +1,66 @@ +--source include/master-slave.inc + +# Test to test that blackhole works with replication, all three +# modes. We start by creating a table on the master and then change +# the engine to use blackhole on the slave. + +# We start with no primary key +CREATE TABLE t1 (a INT, b INT, c INT); +CREATE TABLE t2 (a INT, b INT, c INT); + +sync_slave_with_master; +ALTER TABLE t1 ENGINE=BLACKHOLE; + +connection master; +INSERT INTO t2 VALUES (1,9,1), (2,9,2), (3,9,3), (4,9,4); +sync_slave_with_master; + +# Test insert, no primary key +let $statement = INSERT INTO t1 VALUES (1,1,1),(2,1,2),(3,1,3),(4,1,4); +source extra/rpl_tests/rpl_blackhole.test; + +# Test update, no primary key +let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 1; +source extra/rpl_tests/rpl_blackhole.test; + +# Test delete, no primary key +let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 1; +source extra/rpl_tests/rpl_blackhole.test; + +# Test INSERT-SELECT into Blackhole, no primary key +let $statement = INSERT INTO t1 SELECT * FROM t2; +source extra/rpl_tests/rpl_blackhole.test; + +# Test INSERT-SELECT from Blackhole, no primary key +let $statement = INSERT INTO t2 SELECT * FROM t1; +source extra/rpl_tests/rpl_blackhole.test; + +connection master; +ALTER TABLE t1 ADD PRIMARY KEY pk_t1 (a,b); + +# Test insert, primary key +let $statement = INSERT INTO t1 VALUES (1,2,1),(2,2,2),(3,2,3),(4,2,4); +source extra/rpl_tests/rpl_blackhole.test; + +# Test update, primary key +let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 2; +source extra/rpl_tests/rpl_blackhole.test; + +# Test delete, primary key +let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 2; +source extra/rpl_tests/rpl_blackhole.test; + +connection master; +ALTER TABLE t1 DROP PRIMARY KEY, ADD KEY key_t1 (a); + +# Test insert, key +let $statement = INSERT INTO t1 VALUES (1,3,1),(2,3,2),(3,3,3),(4,3,4); +source extra/rpl_tests/rpl_blackhole.test; + +# Test update, key +let $statement = UPDATE t1 SET c = 2*c WHERE a % 2 = 0 AND b = 3; +source extra/rpl_tests/rpl_blackhole.test; + +# Test delete, key +let $statement = DELETE FROM t1 WHERE a % 2 = 0 AND b = 3; +source extra/rpl_tests/rpl_blackhole.test; diff --git a/sql/log_event.cc b/sql/log_event.cc index 27e81c8ecd3..1521de5b975 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -8605,10 +8605,10 @@ int Rows_log_event::find_row(const Relay_log_info *rli) the necessary bits on the bytes and don't set the filler bits correctly. */ - my_ptrdiff_t const pos= - table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; - table->record[0][pos]= 0xFF; - + if (table->s->null_bytes > 0) + table->record[0][table->s->null_bytes - 1]|= + 256U - (1U << table->s->last_null_bit_pos); + if ((error= table->file->index_read_map(table->record[0], m_key, HA_WHOLE_KEY, HA_READ_KEY_EXACT))) diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc index 85b8117e501..1a1a0d02375 100644 --- a/storage/blackhole/ha_blackhole.cc +++ b/storage/blackhole/ha_blackhole.cc @@ -18,6 +18,7 @@ #pragma implementation // gcc: Class implementation #endif +#define MYSQL_SERVER 1 #include "mysql_priv.h" #include "ha_blackhole.h" @@ -100,6 +101,24 @@ int ha_blackhole::write_row(uchar * buf) DBUG_RETURN(table->next_number_field ? update_auto_increment() : 0); } +int ha_blackhole::update_row(const uchar *old_data, uchar *new_data) +{ + DBUG_ENTER("ha_blackhole::update_row"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_blackhole::delete_row(const uchar *buf) +{ + DBUG_ENTER("ha_blackhole::delete_row"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + int ha_blackhole::rnd_init(bool scan) { DBUG_ENTER("ha_blackhole::rnd_init"); @@ -110,6 +129,9 @@ int ha_blackhole::rnd_init(bool scan) int ha_blackhole::rnd_next(uchar *buf) { DBUG_ENTER("ha_blackhole::rnd_next"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); DBUG_RETURN(HA_ERR_END_OF_FILE); } @@ -189,6 +211,9 @@ int ha_blackhole::index_read_map(uchar * buf, const uchar * key, enum ha_rkey_function find_flag) { DBUG_ENTER("ha_blackhole::index_read"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); DBUG_RETURN(HA_ERR_END_OF_FILE); } @@ -198,6 +223,9 @@ int ha_blackhole::index_read_idx_map(uchar * buf, uint idx, const uchar * key, enum ha_rkey_function find_flag) { DBUG_ENTER("ha_blackhole::index_read_idx"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); DBUG_RETURN(HA_ERR_END_OF_FILE); } @@ -206,6 +234,9 @@ int ha_blackhole::index_read_last_map(uchar * buf, const uchar * key, key_part_map keypart_map) { DBUG_ENTER("ha_blackhole::index_read_last"); + THD *thd= ha_thd(); + if (thd->system_thread == SYSTEM_THREAD_SLAVE_SQL && thd->query == NULL) + DBUG_RETURN(0); DBUG_RETURN(HA_ERR_END_OF_FILE); } diff --git a/storage/blackhole/ha_blackhole.h b/storage/blackhole/ha_blackhole.h index d5a0d08926c..085840cce43 100644 --- a/storage/blackhole/ha_blackhole.h +++ b/storage/blackhole/ha_blackhole.h @@ -53,7 +53,7 @@ public: ulonglong table_flags() const { return(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_BINLOG_STMT_CAPABLE | + HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | HA_FILE_BASED | HA_CAN_GEOMETRY | HA_CAN_INSERT_DELAYED); } @@ -72,7 +72,6 @@ public: uint max_supported_key_part_length() const { return BLACKHOLE_MAX_KEY_LENGTH; } int open(const char *name, int mode, uint test_if_locked); int close(void); - int write_row(uchar * buf); int rnd_init(bool scan); int rnd_next(uchar *buf); int rnd_pos(uchar * buf, uchar *pos); @@ -94,4 +93,8 @@ public: THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); +private: + virtual int write_row(uchar *buf); + virtual int update_row(const uchar *old_data, uchar *new_data); + virtual int delete_row(const uchar *buf); }; From a18639b63426092f6df98f6c67ab1139fe50e3c8 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 2 Oct 2008 17:44:49 +0300 Subject: [PATCH 03/11] Bug #37348: Crash in or immediately after JOIN::make_sum_func_list The optimizer pulls up aggregate functions which should be aggregated in an outer select. At some point it may substitute such a function for a field in the temporary table. The setup_copy_fields function doesn't take this into account and may overrun the copy_field buffer. Fixed by filtering out the fields referenced through the specialized reference for aggregates (Item_aggregate_ref). Added an assertion to make sure bugs that cause similar discrepancy don't go undetected. mysql-test/r/func_group.result: Bug #37348: test case mysql-test/t/func_group.test: Bug #37348: test case sql/item.cc: Bug #37348: Added a way to distinguish Item_aggregate_ref from the other types of refs sql/item.h: Bug #37348: Added a way to distinguish Item_aggregate_ref from the other types of refs sql/sql_select.cc: Bug #37348: - Don't consider copying field references seen through Item_aggregate_ref - check for discrepancies between the number of expected fields that need copying and the actual fields copied. --- mysql-test/r/func_group.result | 37 +++++++++++++++++++++++++++++++ mysql-test/t/func_group.test | 40 ++++++++++++++++++++++++++++++++++ sql/item.cc | 1 + sql/item.h | 2 +- sql/sql_select.cc | 11 +++++++++- 5 files changed, 89 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index c198176532f..fd9cf69907c 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -1416,4 +1416,41 @@ SELECT AVG(a), CAST(AVG(a) AS DECIMAL) FROM t1; AVG(a) CAST(AVG(a) AS DECIMAL) 15 15 DROP TABLE t1; +CREATE TABLE derived1 (a bigint(21)); +INSERT INTO derived1 VALUES (2); +CREATE TABLE D ( +pk int(11) NOT NULL AUTO_INCREMENT, +int_nokey int(11) DEFAULT NULL, +int_key int(11) DEFAULT NULL, +filler blob, +PRIMARY KEY (pk), +KEY int_key (int_key) +); +INSERT INTO D VALUES +(39,40,4,repeat(' X', 42)), +(43,56,4,repeat(' X', 42)), +(47,12,4,repeat(' X', 42)), +(71,28,4,repeat(' X', 42)), +(76,54,4,repeat(' X', 42)), +(83,45,4,repeat(' X', 42)), +(105,53,12,NULL); +SELECT +(SELECT COUNT( int_nokey ) +FROM derived1 AS X +WHERE +X.int_nokey < 61 +GROUP BY pk +LIMIT 1) +FROM D AS X +WHERE X.int_key < 13 +GROUP BY int_nokey LIMIT 1; +(SELECT COUNT( int_nokey ) +FROM derived1 AS X +WHERE +X.int_nokey < 61 +GROUP BY pk +LIMIT 1) +1 +DROP TABLE derived1; +DROP TABLE D; End of 5.0 tests diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index b6143bc0c78..4eedd433d34 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -933,5 +933,45 @@ SELECT AVG(a), CAST(AVG(a) AS DECIMAL) FROM t1; DROP TABLE t1; +# +# Bug #37348: Crash in or immediately after JOIN::make_sum_func_list +# + +CREATE TABLE derived1 (a bigint(21)); +INSERT INTO derived1 VALUES (2); + + +CREATE TABLE D ( + pk int(11) NOT NULL AUTO_INCREMENT, + int_nokey int(11) DEFAULT NULL, + int_key int(11) DEFAULT NULL, + filler blob, + PRIMARY KEY (pk), + KEY int_key (int_key) +); + +INSERT INTO D VALUES + (39,40,4,repeat(' X', 42)), + (43,56,4,repeat(' X', 42)), + (47,12,4,repeat(' X', 42)), + (71,28,4,repeat(' X', 42)), + (76,54,4,repeat(' X', 42)), + (83,45,4,repeat(' X', 42)), + (105,53,12,NULL); + +SELECT + (SELECT COUNT( int_nokey ) + FROM derived1 AS X + WHERE + X.int_nokey < 61 + GROUP BY pk + LIMIT 1) +FROM D AS X +WHERE X.int_key < 13 +GROUP BY int_nokey LIMIT 1; + +DROP TABLE derived1; +DROP TABLE D; + ### --echo End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index 66d5d55a21e..fc33f3d5cad 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1338,6 +1338,7 @@ public: else Item_ident::print(str, query_type); } + virtual Ref_Type ref_type() { return AGGREGATE_REF; } }; diff --git a/sql/item.h b/sql/item.h index be343e25d3f..00a2759a739 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2126,7 +2126,7 @@ class Item_ref :public Item_ident protected: void set_properties(); public: - enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF }; + enum Ref_Type { REF, DIRECT_REF, VIEW_REF, OUTER_REF, AGGREGATE_REF }; Field *result_field; /* Save result here */ Item **ref; Item_ref(Name_resolution_context *context_arg, diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6510d2428db..102809f9f64 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -14804,6 +14804,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, Item *pos; List_iterator_fast li(all_fields); Copy_field *copy= NULL; + IF_DBUG(Copy_field *copy_start); res_selected_fields.empty(); res_all_fields.empty(); List_iterator_fast itr(res_all_fields); @@ -14816,12 +14817,19 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, goto err2; param->copy_funcs.empty(); + IF_DBUG(copy_start= copy); for (i= 0; (pos= li++); i++) { Field *field; uchar *tmp; Item *real_pos= pos->real_item(); - if (real_pos->type() == Item::FIELD_ITEM) + /* + Aggregate functions can be substituted for fields (by e.g. temp tables). + We need to filter those substituted fields out. + */ + if (real_pos->type() == Item::FIELD_ITEM && + !(real_pos != pos && + ((Item_ref *)pos)->ref_type() == Item_ref::AGGREGATE_REF)) { Item_field *item; if (!(item= new Item_field(thd, ((Item_field*) real_pos)))) @@ -14868,6 +14876,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, goto err; if (copy) { + DBUG_ASSERT (param->field_count > (uint) (copy - copy_start)); copy->set(tmp, item->result_field); item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1); #ifdef HAVE_purify From 690fd28adf016032677b8a8131f156ff66886cc2 Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Thu, 2 Oct 2008 21:13:15 +0200 Subject: [PATCH 04/11] Bug #38360: BLACKHOLE replication with RBR is broken Incremental patch to add comments to test cases. --- mysql-test/extra/rpl_tests/rpl_blackhole.test | 13 +++++++++++++ mysql-test/suite/rpl/t/rpl_blackhole.test | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_blackhole.test b/mysql-test/extra/rpl_tests/rpl_blackhole.test index 594acbee020..1a0eeb3cf15 100644 --- a/mysql-test/extra/rpl_tests/rpl_blackhole.test +++ b/mysql-test/extra/rpl_tests/rpl_blackhole.test @@ -1,3 +1,16 @@ +# Check replication of one statement assuming that the engine on the +# slave is a blackhole engine. + +# Input: +# $statement Statement to evaluate, it is assumed to change t1 + +# 1. Evaluate statement on master, it is assumed to change t1 +# 2. Wait for statement to be processed on slave +# 3. SELECT from table t1 to see what was written +# 4. Compare position on slave before executing statement and after +# executing statement. If difference is >0, then something was +# written to the binary log on the slave. + connection slave; let $before = query_get_value("SHOW MASTER STATUS", Position, 1); diff --git a/mysql-test/suite/rpl/t/rpl_blackhole.test b/mysql-test/suite/rpl/t/rpl_blackhole.test index c25c4954dfa..0609a4c6048 100644 --- a/mysql-test/suite/rpl/t/rpl_blackhole.test +++ b/mysql-test/suite/rpl/t/rpl_blackhole.test @@ -1,8 +1,21 @@ --source include/master-slave.inc -# Test to test that blackhole works with replication, all three -# modes. We start by creating a table on the master and then change +# PURPOSE. Test that blackhole works with replication in all three +# modes: STATEMENT, MIXED, and ROW. +# +# METHOD. We start by creating a table on the master and then change # the engine to use blackhole on the slave. +# +# After insert/update/delete of one or more rows, the test the +# proceeds to check that replication is running after replicating an +# change, that the blackhole engine does not contain anything (which +# is just a check that the correct engine is used), and that something +# is written to the binary log. +# +# Whe check INSERT, UPDATE, and DELETE statement for tables with no +# key (forcing a range search on the slave), primary keys (using a +# primary key lookup), and index/key with multiple matches (forcing an +# index search). # We start with no primary key CREATE TABLE t1 (a INT, b INT, c INT); From 51cb9ac0e1654bee64609b694ce1df78f890a053 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Fri, 3 Oct 2008 11:30:54 +0200 Subject: [PATCH 05/11] post push fix for bug#38804 (back port of bug#33479) Removes the regression bug#38751. sql/ha_partition.cc: post push fix for bug#38804 (back port of bug#33479) Removes the regression bug#38751. archive relies on a ha_archive::info call to flush data before the copy takes place in alter table. This ensures that all partitions gets a info call, without having to always forward info(HA_STATUS_AUTO) to all partitions. --- sql/ha_partition.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b6303fc2f3c..ea42858356d 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1717,6 +1717,14 @@ error: void ha_partition::update_create_info(HA_CREATE_INFO *create_info) { + /* + Fix for bug#38751, some engines needs info-calls in ALTER. + Archive need this since it flushes in ::info. + HA_STATUS_AUTO is optimized so it will not always be forwarded + to all partitions, but HA_STATUS_VARIABLE will. + */ + info(HA_STATUS_VARIABLE); + info(HA_STATUS_AUTO); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) From 2ab558dec6b89f460cf6e84e58345a847388e992 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 3 Oct 2008 13:10:09 +0300 Subject: [PATCH 06/11] Bug #38370: The test ndb.ndb_index_ordered fails with the community features on The problem was caused by a wrong merge. Fixed by enabling the correct ndb variables initialization. mysql-test/suite/ndb/t/disabled.def: Bug #38370: remove disabled test case sql/mysqld.cc: Bug #38370: corrected a wrong merge to have all the NDB variables intiialized correctly --- mysql-test/suite/ndb/t/disabled.def | 1 - sql/mysqld.cc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mysql-test/suite/ndb/t/disabled.def b/mysql-test/suite/ndb/t/disabled.def index 6102d182684..c638c7b4774 100644 --- a/mysql-test/suite/ndb/t/disabled.def +++ b/mysql-test/suite/ndb/t/disabled.def @@ -12,6 +12,5 @@ partition_03ndb : BUG#16385 2006-03-24 mikael Partitions: crash when updating a range partitioned NDB table ndb_partition_error2 : HF is not sure if the test can work as internded on all the platforms -ndb_index_ordered : Bug#38370 The test ndb.ndb_index_ordered fails with the community features on # the below testcase have been reworked to avoid the bug, test contains comment, keep bug open diff --git a/sql/mysqld.cc b/sql/mysqld.cc index dcf1ee0188a..208ded65805 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7535,13 +7535,13 @@ static void mysql_init_variables(void) have_community_features = SHOW_OPTION_YES; #else have_community_features = SHOW_OPTION_NO; +#endif global_system_variables.ndb_index_stat_enable=FALSE; max_system_variables.ndb_index_stat_enable=TRUE; global_system_variables.ndb_index_stat_cache_entries=32; max_system_variables.ndb_index_stat_cache_entries=~0L; global_system_variables.ndb_index_stat_update_freq=20; max_system_variables.ndb_index_stat_update_freq=~0L; -#endif #ifdef HAVE_OPENSSL have_ssl=SHOW_OPTION_YES; #else From acfd0ff7de3ca8abb5722bbcf0235967a8a200fe Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Fri, 3 Oct 2008 12:52:01 +0200 Subject: [PATCH 07/11] The test rpl_blackhole was executed even when there were no blackhole installed. This patch adds a check for that. mysql-test/suite/rpl/t/rpl_blackhole.test: Adding include of have_blackhole. --- mysql-test/suite/rpl/t/rpl_blackhole.test | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/rpl/t/rpl_blackhole.test b/mysql-test/suite/rpl/t/rpl_blackhole.test index 0609a4c6048..e8dfbd51552 100644 --- a/mysql-test/suite/rpl/t/rpl_blackhole.test +++ b/mysql-test/suite/rpl/t/rpl_blackhole.test @@ -1,5 +1,3 @@ ---source include/master-slave.inc - # PURPOSE. Test that blackhole works with replication in all three # modes: STATEMENT, MIXED, and ROW. # @@ -17,6 +15,9 @@ # primary key lookup), and index/key with multiple matches (forcing an # index search). +source include/master-slave.inc; +source include/have_blackhole.inc; + # We start with no primary key CREATE TABLE t1 (a INT, b INT, c INT); CREATE TABLE t2 (a INT, b INT, c INT); From a66e58daee4c3b77a5ad02b7e6f72e597a70b688 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 6 Oct 2008 17:22:38 +0500 Subject: [PATCH 08/11] Bug#38083 Error-causing row inserted into partitioned table despite error --- sql/opt_range.cc | 8 +- sql/partition_info.h | 5 +- sql/sql_partition.cc | 275 ++++++++++++++++++++++++------------------- 3 files changed, 161 insertions(+), 127 deletions(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 28ee8af0699..3092bc5d408 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3145,10 +3145,12 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) ppar->subpart_fields);); /* Find the subpartition (it's HASH/KEY so we always have one) */ partition_info *part_info= ppar->part_info; - uint32 subpart_id= part_info->get_subpartition_id(part_info); - + uint32 part_id, subpart_id; + + if (part_info->get_subpartition_id(part_info, &subpart_id)) + return 0; + /* Mark this partition as used in each subpartition. */ - uint32 part_id; while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != NOT_A_PARTITION_ID) { diff --git a/sql/partition_info.h b/sql/partition_info.h index 2af7fa1717c..703b92305b1 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -25,8 +25,9 @@ class partition_info; typedef int (*get_part_id_func)(partition_info *part_info, uint32 *part_id, longlong *func_value); -typedef uint32 (*get_subpart_id_func)(partition_info *part_info); - +typedef int (*get_subpart_id_func)(partition_info *part_info, + uint32 *part_id); + struct st_ddl_log_memory_entry; class partition_info : public Sql_alloc diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 6419d336b9f..9b7e1b34af4 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -73,10 +73,8 @@ static int get_part_id_charset_func_subpart(partition_info *part_info, static int get_part_part_id_charset_func(partition_info *part_info, uint32 *part_id, longlong *func_value); -static uint32 get_subpart_id_charset_func(partition_info *part_info); -int get_partition_id_list(partition_info *part_info, - uint32 *part_id, - longlong *func_value); +static int get_subpart_id_charset_func(partition_info *part_info, + uint32 *part_id); int get_partition_id_list(partition_info *part_info, uint32 *part_id, longlong *func_value); @@ -119,10 +117,14 @@ int get_partition_id_list_sub_linear_hash(partition_info *part_info, int get_partition_id_list_sub_linear_key(partition_info *part_info, uint32 *part_id, longlong *func_value); -uint32 get_partition_id_hash_sub(partition_info *part_info); -uint32 get_partition_id_key_sub(partition_info *part_info); -uint32 get_partition_id_linear_hash_sub(partition_info *part_info); -uint32 get_partition_id_linear_key_sub(partition_info *part_info); +int get_partition_id_hash_sub(partition_info *part_info, + uint32 *part_id); +int get_partition_id_key_sub(partition_info *part_info, + uint32 *part_id); +int get_partition_id_linear_hash_sub(partition_info *part_info, + uint32 *part_id); +int get_partition_id_linear_key_sub(partition_info *part_info, + uint32 *part_id); static uint32 get_next_partition_via_walking(PARTITION_ITERATOR*); static void set_up_range_analysis_info(partition_info *part_info); static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR*); @@ -2232,17 +2234,24 @@ bool partition_key_modified(TABLE *table, const MY_BITMAP *fields) SYNOPSIS part_val_int() item_expr The item expression to evaluate + out:result The value of the partition function, + LONGLONG_MIN if any null value in function RETURN VALUES - The value of the partition function, LONGLONG_MIN if any null value - in function + TRUE Error in val_int() + FALSE ok */ -static inline longlong part_val_int(Item *item_expr) +static inline int part_val_int(Item *item_expr, longlong *result) { - longlong value= item_expr->val_int(); + *result= item_expr->val_int(); if (item_expr->null_value) - value= LONGLONG_MIN; - return value; + { + if (current_thd->is_error()) + return TRUE; + else + *result= LONGLONG_MIN; + } + return FALSE; } @@ -2319,24 +2328,29 @@ static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id, get_part_id_hash() no_parts Number of hash partitions part_expr Item tree of hash function - out:func_value Value of hash function + out:part_id The returned partition id + out:func_value Value of hash function RETURN VALUE - Calculated partition id + != 0 Error code + FALSE Success */ -inline -static uint32 get_part_id_hash(uint no_parts, - Item *part_expr, - longlong *func_value) +static int get_part_id_hash(uint no_parts, + Item *part_expr, + uint32 *part_id, + longlong *func_value) { longlong int_hash_id; DBUG_ENTER("get_part_id_hash"); - *func_value= part_val_int(part_expr); + if (part_val_int(part_expr, func_value)) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + int_hash_id= *func_value % no_parts; - DBUG_RETURN(int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id); + *part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id; + DBUG_RETURN(FALSE); } @@ -2349,24 +2363,29 @@ static uint32 get_part_id_hash(uint no_parts, desired information is given no_parts Number of hash partitions part_expr Item tree of hash function + out:part_id The returned partition id out:func_value Value of hash function RETURN VALUE - Calculated partition id + != 0 Error code + 0 OK */ -inline -static uint32 get_part_id_linear_hash(partition_info *part_info, - uint no_parts, - Item *part_expr, - longlong *func_value) +static int get_part_id_linear_hash(partition_info *part_info, + uint no_parts, + Item *part_expr, + uint32 *part_id, + longlong *func_value) { DBUG_ENTER("get_part_id_linear_hash"); - *func_value= part_val_int(part_expr); - DBUG_RETURN(get_part_id_from_linear_hash(*func_value, - part_info->linear_hash_mask, - no_parts)); + if (part_val_int(part_expr, func_value)) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + + *part_id= get_part_id_from_linear_hash(*func_value, + part_info->linear_hash_mask, + no_parts); + DBUG_RETURN(FALSE); } @@ -2503,49 +2522,7 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr) } return; } -/* - This function is used to calculate the partition id where all partition - fields have been prepared to point to a record where the partition field - values are bound. - SYNOPSIS - get_partition_id() - part_info A reference to the partition_info struct where all the - desired information is given - out:part_id The partition id is returned through this pointer - out: func_value Value of partition function (longlong) - - RETURN VALUE - part_id Partition id of partition that would contain - row with given values of PF-fields - HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't - fit into any partition and thus the values of - the PF-fields are not allowed. - - DESCRIPTION - A routine used from write_row, update_row and delete_row from any - handler supporting partitioning. It is also a support routine for - get_partition_set used to find the set of partitions needed to scan - for a certain index scan or full table scan. - - It is actually 14 different variants of this function which are called - through a function pointer. - - get_partition_id_list - get_partition_id_range - get_partition_id_hash_nosub - get_partition_id_key_nosub - get_partition_id_linear_hash_nosub - get_partition_id_linear_key_nosub - get_partition_id_range_sub_hash - get_partition_id_range_sub_key - get_partition_id_range_sub_linear_hash - get_partition_id_range_sub_linear_key - get_partition_id_list_sub_hash - get_partition_id_list_sub_key - get_partition_id_list_sub_linear_hash - get_partition_id_list_sub_linear_key -*/ /* This function is used to calculate the main partition to use in the case of @@ -2557,14 +2534,13 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr) part_info A reference to the partition_info struct where all the desired information is given out:part_id The partition id is returned through this pointer - out: func_value The value calculated by partition function + out:func_value The value calculated by partition function RETURN VALUE - part_id Partition id of partition that would contain - row with given values of PF-fields HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't fit into any partition and thus the values of the PF-fields are not allowed. + 0 OK DESCRIPTION @@ -2640,13 +2616,14 @@ static int get_part_part_id_charset_func(partition_info *part_info, } -static uint32 get_subpart_id_charset_func(partition_info *part_info) +static int get_subpart_id_charset_func(partition_info *part_info, + uint32 *part_id) { int res; copy_to_part_field_buffers(part_info->subpart_charset_field_array, part_info->subpart_field_buffers, part_info->restore_subpart_field_ptrs); - res= part_info->get_subpartition_id_charset(part_info); + res= part_info->get_subpartition_id_charset(part_info, part_id); restore_part_field_pointers(part_info->subpart_charset_field_array, part_info->restore_subpart_field_ptrs); return res; @@ -2661,11 +2638,15 @@ int get_partition_id_list(partition_info *part_info, int list_index; int min_list_index= 0; int max_list_index= part_info->no_list_values - 1; - longlong part_func_value= part_val_int(part_info->part_expr); + longlong part_func_value; + int error= part_val_int(part_info->part_expr, &part_func_value); longlong list_value; bool unsigned_flag= part_info->part_expr->unsigned_flag; DBUG_ENTER("get_partition_id_list"); + if (error) + goto notfound; + if (part_info->part_expr->null_value) { if (part_info->has_null_value) @@ -2809,10 +2790,14 @@ int get_partition_id_range(partition_info *part_info, uint min_part_id= 0; uint max_part_id= max_partition; uint loc_part_id; - longlong part_func_value= part_val_int(part_info->part_expr); + longlong part_func_value; + int error= part_val_int(part_info->part_expr, &part_func_value); bool unsigned_flag= part_info->part_expr->unsigned_flag; DBUG_ENTER("get_partition_id_range"); + if (error) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + if (part_info->part_expr->null_value) { *part_id= 0; @@ -2970,9 +2955,8 @@ int get_partition_id_hash_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value) { - *part_id= get_part_id_hash(part_info->no_parts, part_info->part_expr, - func_value); - return 0; + return get_part_id_hash(part_info->no_parts, part_info->part_expr, + part_id, func_value); } @@ -2980,9 +2964,8 @@ int get_partition_id_linear_hash_nosub(partition_info *part_info, uint32 *part_id, longlong *func_value) { - *part_id= get_part_id_linear_hash(part_info, part_info->no_parts, - part_info->part_expr, func_value); - return 0; + return get_part_id_linear_hash(part_info, part_info->no_parts, + part_info->part_expr, part_id, func_value); } @@ -3023,8 +3006,12 @@ int get_partition_id_range_sub_hash(partition_info *part_info, DBUG_RETURN(error); } no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_hash(no_subparts, part_info->subpart_expr, - &local_func_value); + if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr, + &sub_part_id, &local_func_value)))) + { + DBUG_RETURN(error); + } + *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); DBUG_RETURN(0); } @@ -3046,9 +3033,14 @@ int get_partition_id_range_sub_linear_hash(partition_info *part_info, DBUG_RETURN(error); } no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_linear_hash(part_info, no_subparts, - part_info->subpart_expr, - &local_func_value); + if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts, + part_info->subpart_expr, + &sub_part_id, + &local_func_value)))) + { + DBUG_RETURN(error); + } + *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); DBUG_RETURN(0); } @@ -3117,8 +3109,12 @@ int get_partition_id_list_sub_hash(partition_info *part_info, DBUG_RETURN(error); } no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_hash(no_subparts, part_info->subpart_expr, - &local_func_value); + if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr, + &sub_part_id, &local_func_value)))) + { + DBUG_RETURN(error); + } + *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); DBUG_RETURN(0); } @@ -3140,9 +3136,14 @@ int get_partition_id_list_sub_linear_hash(partition_info *part_info, DBUG_RETURN(error); } no_subparts= part_info->no_subparts; - sub_part_id= get_part_id_linear_hash(part_info, no_subparts, - part_info->subpart_expr, - &local_func_value); + if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts, + part_info->subpart_expr, + &sub_part_id, + &local_func_value)))) + { + DBUG_RETURN(error); + } + *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts); DBUG_RETURN(0); } @@ -3219,36 +3220,43 @@ int get_partition_id_list_sub_linear_key(partition_info *part_info, get_partition_id_linear_key_sub */ -uint32 get_partition_id_hash_sub(partition_info *part_info) +int get_partition_id_hash_sub(partition_info *part_info, + uint32 *part_id) { longlong func_value; return get_part_id_hash(part_info->no_subparts, part_info->subpart_expr, - &func_value); + part_id, &func_value); } -uint32 get_partition_id_linear_hash_sub(partition_info *part_info) +int get_partition_id_linear_hash_sub(partition_info *part_info, + uint32 *part_id) { longlong func_value; return get_part_id_linear_hash(part_info, part_info->no_subparts, - part_info->subpart_expr, &func_value); + part_info->subpart_expr, part_id, + &func_value); } -uint32 get_partition_id_key_sub(partition_info *part_info) +int get_partition_id_key_sub(partition_info *part_info, + uint32 *part_id) { longlong func_value; - return get_part_id_key(part_info->subpart_field_array, - part_info->no_subparts, &func_value); + *part_id= get_part_id_key(part_info->subpart_field_array, + part_info->no_subparts, &func_value); + return FALSE; } -uint32 get_partition_id_linear_key_sub(partition_info *part_info) +int get_partition_id_linear_key_sub(partition_info *part_info, + uint32 *part_id) { longlong func_value; - return get_part_id_linear_key(part_info, - part_info->subpart_field_array, - part_info->no_subparts, &func_value); + *part_id= get_part_id_linear_key(part_info, + part_info->subpart_field_array, + part_info->no_subparts, &func_value); + return FALSE; } @@ -3337,37 +3345,40 @@ static bool check_part_func_bound(Field **ptr) buf A buffer that can be used to evaluate the partition function key_info The index object key_spec A key_range containing key and key length + out:part_id The returned partition id RETURN VALUES - part_id Subpartition id to use + TRUE All fields in partition function are set + FALSE Not all fields in partition function are set DESCRIPTION Use key buffer to set-up record in buf, move field pointers and get the partition identity and restore field pointers afterwards. */ -static uint32 get_sub_part_id_from_key(const TABLE *table,uchar *buf, - KEY *key_info, - const key_range *key_spec) +static int get_sub_part_id_from_key(const TABLE *table,uchar *buf, + KEY *key_info, + const key_range *key_spec, + uint32 *part_id) { uchar *rec0= table->record[0]; partition_info *part_info= table->part_info; - uint32 part_id; + int res; DBUG_ENTER("get_sub_part_id_from_key"); key_restore(buf, (uchar*)key_spec->key, key_info, key_spec->length); if (likely(rec0 == buf)) { - part_id= part_info->get_subpartition_id(part_info); + res= part_info->get_subpartition_id(part_info, part_id); } else { Field **part_field_array= part_info->subpart_field_array; set_field_ptr(part_field_array, buf, rec0); - part_id= part_info->get_subpartition_id(part_info); + res= part_info->get_subpartition_id(part_info, part_id); set_field_ptr(part_field_array, rec0, buf); } - DBUG_RETURN(part_id); + DBUG_RETURN(res); } /* @@ -3586,7 +3597,13 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, else if (part_info->is_sub_partitioned()) { if (part_info->all_fields_in_SPF.is_set(index)) - sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec); + { + if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part)) + { + part_spec->start_part= no_parts; + DBUG_VOID_RETURN; + } + } else if (part_info->all_fields_in_PPF.is_set(index)) { if (get_part_id_from_key(table,buf,key_info, @@ -3632,7 +3649,14 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, else if (part_info->is_sub_partitioned()) { if (check_part_func_bound(part_info->subpart_field_array)) - sub_part= get_sub_part_id_from_key(table, buf, key_info, key_spec); + { + if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part)) + { + part_spec->start_part= no_parts; + clear_indicator_in_key_fields(key_info); + DBUG_VOID_RETURN; + } + } else if (check_part_func_bound(part_info->part_field_array)) { if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part)) @@ -6836,9 +6860,11 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info, field->set_null(); if (is_subpart) { - part_id= part_info->get_subpartition_id(part_info); - init_single_partition_iterator(part_id, part_iter); - return 1; /* Ok, iterator initialized */ + if (!part_info->get_subpartition_id(part_info, &part_id)) + { + init_single_partition_iterator(part_id, part_iter); + return 1; /* Ok, iterator initialized */ + } } else { @@ -7007,13 +7033,18 @@ static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter) static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter) { Field *field= part_iter->part_info->subpart_field_array[0]; + uint32 res; if (part_iter->field_vals.cur == part_iter->field_vals.end) { part_iter->field_vals.cur= part_iter->field_vals.start; return NOT_A_PARTITION_ID; } field->store(part_iter->field_vals.cur++, FALSE); - return part_iter->part_info->get_subpartition_id(part_iter->part_info); + if (part_iter->part_info->get_subpartition_id(part_iter->part_info, + &res)) + return NOT_A_PARTITION_ID; + return res; + } From afbb52c43c0180917db6f7718232d40031f47fa4 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 6 Oct 2008 18:14:20 +0500 Subject: [PATCH 09/11] Bug#38005 Partitions: error with insert select. MyISAM blocks index usage for bulk insert into zero-records tables. See ha_myisam::start_bulk_insert() lines from ... if (file->state->records == 0 ... ... That causes problems for partition engine when some partitions have records some not as the engine uses same access method for all partitions. Now partition engine doesn't call index_first/index_last for empty tables. per-file comments: mysql-test/r/partition.result Bug#38005 Partitions: error with insert select. test result mysql-test/t/partition.test Bug#38005 Partitions: error with insert select. test case sql/ha_partition.cc Bug#38005 Partitions: error with insert select. ha_engine::index_first and ha_engine::index_last not called for empty tables. --- mysql-test/r/partition.result | 70 +++++++++++++++++++++++++++++++++++ mysql-test/t/partition.test | 49 ++++++++++++++++++++++++ sql/ha_partition.cc | 33 +++++++++++++++++ 3 files changed, 152 insertions(+) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index a441a841a07..321139c9ea1 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1637,4 +1637,74 @@ select count(*) from t1, t2 where t1.createdDate = t2.createdDate; count(*) 1 drop table t1, t2; +create table t1 (s1 int) partition by hash(s1) partitions 2; +create index i on t1 (s1); +insert into t1 values (1); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +s1 +1 +1 +1 +1 +1 +1 +1 +1 +drop table t1; +create table t1 (s1 int) partition by range(s1) +(partition pa1 values less than (10), +partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (1); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +s1 +1 +1 +1 +1 +1 +1 +1 +1 +drop table t1; +create table t1 (s1 int) partition by range(s1) +(partition pa1 values less than (10), +partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (20); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +s1 +20 +20 +20 +20 +20 +20 +20 +20 +drop table t1; +create table t1 (s1 int) partition by range(s1) +(partition pa1 values less than (10), +partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (1), (2), (3), (4), (5), (6), (7), (8); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +insert into t1 select s1 from t1 where s1=3; +select count(*) from t1; +count(*) +288 +drop table t1; End of 5.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 5270eced05f..970eb3e6f35 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -1791,4 +1791,53 @@ select count(*) from t1, t2 where t1.createdDate = t2.createdDate; drop table t1, t2; +# +# Bug #38005 Partitions: error with insert select +# + +create table t1 (s1 int) partition by hash(s1) partitions 2; +create index i on t1 (s1); +insert into t1 values (1); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +drop table t1; + +create table t1 (s1 int) partition by range(s1) + (partition pa1 values less than (10), + partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (1); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +drop table t1; + +create table t1 (s1 int) partition by range(s1) + (partition pa1 values less than (10), + partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (20); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +select * from t1; +drop table t1; + +create table t1 (s1 int) partition by range(s1) + (partition pa1 values less than (10), + partition pa2 values less than MAXVALUE); +create index i on t1 (s1); +insert into t1 values (1), (2), (3), (4), (5), (6), (7), (8); +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1; +insert into t1 select s1 from t1 order by s1 desc; +insert into t1 select s1 from t1 where s1=3; +select count(*) from t1; +drop table t1; + --echo End of 5.1 tests diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index ea42858356d..14e321218ca 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4290,6 +4290,17 @@ 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: @@ -4377,10 +4388,32 @@ 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; From 449fe80fe69490130c638ccafb63c50156d81400 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 6 Oct 2008 18:40:53 +0500 Subject: [PATCH 10/11] keep compiler happy --- sql/sql_partition.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 9b7e1b34af4..a45664a9767 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2999,6 +2999,8 @@ int get_partition_id_range_sub_hash(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_range_sub_hash"); + LINT_INIT(loc_part_id); + LINT_INIT(sub_part_id); if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, func_value)))) @@ -3026,6 +3028,8 @@ int get_partition_id_range_sub_linear_hash(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_range_sub_linear_hash"); + LINT_INIT(loc_part_id); + LINT_INIT(sub_part_id); if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, func_value)))) @@ -3055,6 +3059,7 @@ int get_partition_id_range_sub_key(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_range_sub_key"); + LINT_INIT(loc_part_id); if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, func_value)))) @@ -3078,6 +3083,7 @@ int get_partition_id_range_sub_linear_key(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_range_sub_linear_key"); + LINT_INIT(loc_part_id); if (unlikely((error= get_partition_id_range(part_info, &loc_part_id, func_value)))) @@ -3102,6 +3108,7 @@ int get_partition_id_list_sub_hash(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_list_sub_hash"); + LINT_INIT(sub_part_id); if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, func_value)))) @@ -3129,6 +3136,7 @@ int get_partition_id_list_sub_linear_hash(partition_info *part_info, longlong local_func_value; int error; DBUG_ENTER("get_partition_id_list_sub_linear_hash"); + LINT_INIT(sub_part_id); if (unlikely((error= get_partition_id_list(part_info, &loc_part_id, func_value)))) From 84c1fffabbf89a7c1ed3a768c81dc9060516255c Mon Sep 17 00:00:00 2001 From: Guilhem Bichot Date: Mon, 6 Oct 2008 16:06:59 +0200 Subject: [PATCH 11/11] Fix for BUG#31612 "Trigger fired multiple times leads to gaps in auto_increment sequence". The bug was that if a trigger fired multiple times inside a top statement (for example top-statement is a multi-row INSERT, and trigger is ON INSERT), and that trigger inserted into an auto_increment column, then gaps could be observed in the auto_increment sequence, even if there were no other users of the database (no concurrency). It was wrong usage of THD::auto_inc_intervals_in_cur_stmt_for_binlog. Note that the fix changes "class handler", I'll tell the Storage Engine API team. mysql-test/r/trigger-trans.result: result; before the bugfix, the sequence was 1,2,4,6,8,10,12... mysql-test/t/trigger-trans.test: test for BUG#31612 sql/handler.cc: See revision comment of handler.h. As THD::auto_inc_intervals_in_cur_stmt_for_binlog is cumulative over all trigger invokations by the top statement, the second invokation of the trigger arrived in handler::update_auto_increment() with already one interval in THD::auto_inc_intervals_in_cur_stmt_for_binlog. The method thus believed it had already reserved one interval for that invokation, thus reserved a twice larger interval (heuristic when we don't know how large the interval should be: we grow by powers of two). InnoDB thus increased its internal per-table auto_increment counter by 2 while only one row was to be inserted. Hence a gap in the sequence. The fix is to use the new handler::auto_inc_intervals_count. Note that the trigger's statement knows how many rows it is going to insert, but provides estimation_rows_to_insert == 0 (see comments in sql_insert.cc why triggers don't call handler::ha_start_bulk_insert()). * removing white space at end of line * we don't need to maintain THD::auto_inc_intervals_in_cur_stmt_for_binlog if no binlogging or if row-based binlogging. Using auto_inc_intervals_count in the heuristic makes the heuristic independent of binary logging, which is good. sql/handler.h: THD::auto_inc_intervals_in_cur_stmt_for_binlog served - for binlogging - as a heuristic when we have no estimation of how many records the statement will insert. But the first goal needs to be cumulative over all statements which form a binlog event, while the second one needs to be attached to each statement. THD::auto_inc_intervals_in_cur_stmt_for_binlog is cumulative, leading to BUG#31612. So we introduce handler::auto_inc_intervals_count for the second goal. See the revision comment of handler.cc. A smaller issue was that, even when the binlog event was only one statement (no triggers, no stored functions), THD::auto_inc_intervals_in_cur_stmt.nb_elements() could be lower than the number of reserved intervals (fooling the heuristic), because its append() method collapses two contiguous intervals in one. Note that as auto_inc_intervals_count is in class 'handler' and not in class 'THD', it does not need to be handled in THD::reset|restore_sub_statement_state(). sql/log.cc: Comment is wrong: if auto_increment is second, in handler::update_auto_increment() 'append' is false and so auto_inc_intervals_in_cur_stmt_for_binlog is empty, we do not come here. sql/sql_class.h: comment --- mysql-test/r/trigger-trans.result | 27 +++++++++++++++++++++++++++ mysql-test/t/trigger-trans.test | 13 +++++++++++++ sql/handler.cc | 27 ++++++++++++++++----------- sql/handler.h | 10 +++++++++- sql/log.cc | 5 ----- sql/sql_class.h | 3 +++ 6 files changed, 68 insertions(+), 17 deletions(-) diff --git a/mysql-test/r/trigger-trans.result b/mysql-test/r/trigger-trans.result index 9e0f1e2c351..29df8cbe06d 100644 --- a/mysql-test/r/trigger-trans.result +++ b/mysql-test/r/trigger-trans.result @@ -161,3 +161,30 @@ SELECT @a, @b; 1 1 DROP TABLE t2, t1; End of 5.0 tests +BUG#31612 +Trigger fired multiple times leads to gaps in auto_increment sequence +create table t1 (a int, val char(1)) engine=InnoDB; +create table t2 (b int auto_increment primary key, +val char(1)) engine=InnoDB; +create trigger t1_after_insert after +insert on t1 for each row insert into t2 set val=NEW.val; +insert into t1 values ( 123, 'a'), ( 123, 'b'), ( 123, 'c'), +(123, 'd'), (123, 'e'), (123, 'f'), (123, 'g'); +insert into t1 values ( 654, 'a'), ( 654, 'b'), ( 654, 'c'), +(654, 'd'), (654, 'e'), (654, 'f'), (654, 'g'); +select * from t2 order by b; +b val +1 a +2 b +3 c +4 d +5 e +6 f +7 g +8 a +9 b +10 c +11 d +12 e +13 f +14 g diff --git a/mysql-test/t/trigger-trans.test b/mysql-test/t/trigger-trans.test index 5db5b982773..ae223b2666e 100644 --- a/mysql-test/t/trigger-trans.test +++ b/mysql-test/t/trigger-trans.test @@ -162,3 +162,16 @@ DROP TABLE t2, t1; --echo End of 5.0 tests + +--echo BUG#31612 +--echo Trigger fired multiple times leads to gaps in auto_increment sequence +create table t1 (a int, val char(1)) engine=InnoDB; +create table t2 (b int auto_increment primary key, + val char(1)) engine=InnoDB; +create trigger t1_after_insert after + insert on t1 for each row insert into t2 set val=NEW.val; +insert into t1 values ( 123, 'a'), ( 123, 'b'), ( 123, 'c'), + (123, 'd'), (123, 'e'), (123, 'f'), (123, 'g'); +insert into t1 values ( 654, 'a'), ( 654, 'b'), ( 654, 'c'), + (654, 'd'), (654, 'e'), (654, 'f'), (654, 'g'); +select * from t2 order by b; diff --git a/sql/handler.cc b/sql/handler.cc index f26e84bdfdf..600991aeec8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2165,7 +2165,12 @@ prev_insert_id(ulonglong nr, struct system_variables *variables) - In both cases, the reserved intervals are remembered in thd->auto_inc_intervals_in_cur_stmt_for_binlog if statement-based binlogging; the last reserved interval is remembered in - auto_inc_interval_for_cur_row. + auto_inc_interval_for_cur_row. The number of reserved intervals is + remembered in auto_inc_intervals_count. It differs from the number of + elements in thd->auto_inc_intervals_in_cur_stmt_for_binlog() because the + latter list is cumulative over all statements forming one binlog event + (when stored functions and triggers are used), and collapses two + contiguous intervals in one (see its append() method). The idea is that generated auto_increment values are predictable and independent of the column values in the table. This is needed to be @@ -2249,8 +2254,6 @@ int handler::update_auto_increment() handler::estimation_rows_to_insert was set by handler::ha_start_bulk_insert(); if 0 it means "unknown". */ - uint nb_already_reserved_intervals= - thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements(); ulonglong nb_desired_values; /* If an estimation was given to the engine: @@ -2262,17 +2265,17 @@ int handler::update_auto_increment() start, starting from AUTO_INC_DEFAULT_NB_ROWS. Don't go beyond a max to not reserve "way too much" (because reservation means potentially losing unused values). + Note that in prelocked mode no estimation is given. */ - if (nb_already_reserved_intervals == 0 && - (estimation_rows_to_insert > 0)) + if ((auto_inc_intervals_count == 0) && (estimation_rows_to_insert > 0)) nb_desired_values= estimation_rows_to_insert; else /* go with the increasing defaults */ { /* avoid overflow in formula, with this if() */ - if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS) + if (auto_inc_intervals_count <= AUTO_INC_DEFAULT_NB_MAX_BITS) { - nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS * - (1 << nb_already_reserved_intervals); + nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS * + (1 << auto_inc_intervals_count); set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX); } else @@ -2285,7 +2288,7 @@ int handler::update_auto_increment() &nb_reserved_values); if (nr == ~(ulonglong) 0) DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED); // Mark failure - + /* That rounding below should not be needed when all engines actually respect offset and increment in get_auto_increment(). But they don't @@ -2296,7 +2299,7 @@ int handler::update_auto_increment() */ nr= compute_next_insert_id(nr-1, variables); } - + if (table->s->next_number_keypart == 0) { /* We must defer the appending until "nr" has been possibly truncated */ @@ -2340,8 +2343,9 @@ int handler::update_auto_increment() { auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values, variables->auto_increment_increment); + auto_inc_intervals_count++; /* Row-based replication does not need to store intervals in binlog */ - if (!thd->current_stmt_binlog_row_based) + if (mysql_bin_log.is_open() && !thd->current_stmt_binlog_row_based) thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(), auto_inc_interval_for_cur_row.values(), variables->auto_increment_increment); @@ -2461,6 +2465,7 @@ void handler::ha_release_auto_increment() release_auto_increment(); insert_id_for_cur_row= 0; auto_inc_interval_for_cur_row.replace(0, 0, 0); + auto_inc_intervals_count= 0; if (next_insert_id > 0) { next_insert_id= 0; diff --git a/sql/handler.h b/sql/handler.h index 3c5db8a0dff..b943e188962 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1129,6 +1129,13 @@ public: inserter. */ Discrete_interval auto_inc_interval_for_cur_row; + /** + Number of reserved auto-increment intervals. Serves as a heuristic + when we have no estimation of how many records the statement will insert: + the more intervals we have reserved, the bigger the next one. Reset in + handler::ha_release_auto_increment(). + */ + uint auto_inc_intervals_count; handler(handlerton *ht_arg, TABLE_SHARE *share_arg) :table_share(share_arg), table(0), @@ -1137,7 +1144,8 @@ public: ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), locked(FALSE), implicit_emptied(0), - pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0) + pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), + auto_inc_intervals_count(0) {} virtual ~handler(void) { diff --git a/sql/log.cc b/sql/log.cc index 7775fb44b65..b5539525ea6 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4011,11 +4011,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) DBUG_PRINT("info",("number of auto_inc intervals: %u", thd->auto_inc_intervals_in_cur_stmt_for_binlog. nb_elements())); - /* - If the auto_increment was second in a table's index (possible with - MyISAM or BDB) (table->next_number_keypart != 0), such event is - in fact not necessary. We could avoid logging it. - */ Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT, thd->auto_inc_intervals_in_cur_stmt_for_binlog. minimum()); diff --git a/sql/sql_class.h b/sql/sql_class.h index 60051ed02cc..82c162f923f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1524,6 +1524,9 @@ public: then the latter INSERT will insert no rows (first_successful_insert_id_in_cur_stmt == 0), but storing "INSERT_ID=3" in the binlog is still needed; the list's minimum will contain 3. + This variable is cumulative: if several statements are written to binlog + as one (stored functions or triggers are used) this list is the + concatenation of all intervals reserved by all statements. */ Discrete_intervals_list auto_inc_intervals_in_cur_stmt_for_binlog; /* Used by replication and SET INSERT_ID */