diff options
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | myisam/mi_delete.c | 2 | ||||
-rw-r--r-- | myisam/myisam_ftdump.c | 3 | ||||
-rw-r--r-- | myisammrg/myrg_open.c | 10 | ||||
-rw-r--r-- | myisammrg/myrg_queue.c | 2 | ||||
-rw-r--r-- | mysql-test/r/delayed.result | 174 | ||||
-rw-r--r-- | mysql-test/r/merge.result | 19 | ||||
-rw-r--r-- | mysql-test/r/rpl_insert_id.result | 129 | ||||
-rw-r--r-- | mysql-test/t/delayed.test | 148 | ||||
-rw-r--r-- | mysql-test/t/merge.test | 25 | ||||
-rw-r--r-- | mysql-test/t/rpl_insert_id.test | 112 | ||||
-rw-r--r-- | mysys/queues.c | 48 | ||||
-rw-r--r-- | sql/ha_archive.cc | 34 | ||||
-rw-r--r-- | sql/ha_archive.h | 1 | ||||
-rw-r--r-- | sql/item_func.cc | 35 | ||||
-rw-r--r-- | sql/item_func.h | 1 | ||||
-rw-r--r-- | sql/log_event.cc | 1 | ||||
-rw-r--r-- | sql/set_var.cc | 13 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 2 | ||||
-rw-r--r-- | sql/sql_class.cc | 16 | ||||
-rw-r--r-- | sql/sql_class.h | 48 | ||||
-rw-r--r-- | sql/sql_insert.cc | 54 | ||||
-rw-r--r-- | sql/sql_load.cc | 12 | ||||
-rw-r--r-- | sql/sql_parse.cc | 16 | ||||
-rw-r--r-- | sql/sql_select.cc | 11 | ||||
-rw-r--r-- | sql/sql_update.cc | 4 | ||||
-rw-r--r-- | tests/mysql_client_test.c | 40 |
27 files changed, 874 insertions, 90 deletions
diff --git a/configure.in b/configure.in index 7bc4fe77675..55da1dfb241 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.26) +AM_INIT_AUTOMAKE(mysql, 5.0.27) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -19,7 +19,7 @@ SHARED_LIB_VERSION=$SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=26 +NDB_VERSION_BUILD=27 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index 2bc99d65dd2..85cc60bdd9d 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -446,7 +446,7 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key, else { DBUG_PRINT("test",("Inserting of key when deleting")); - if (_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos, + if (!_mi_get_last_key(info,keyinfo,leaf_buff,keybuff,endpos, &tmp)) goto err; ret_value=_mi_insert(info,keyinfo,key,leaf_buff,endpos,keybuff, diff --git a/myisam/myisam_ftdump.c b/myisam/myisam_ftdump.c index 809d7bcca89..2be95d11714 100644 --- a/myisam/myisam_ftdump.c +++ b/myisam/myisam_ftdump.c @@ -126,7 +126,6 @@ int main(int argc,char *argv[]) if (count || stats) { - doc_cnt++; if (strcmp(buf, buf2)) { if (*buf2) @@ -151,6 +150,7 @@ int main(int argc,char *argv[]) keylen2=keylen; doc_cnt=0; } + doc_cnt+= (subkeys >= 0 ? 1 : -subkeys); } if (dump) { @@ -166,7 +166,6 @@ int main(int argc,char *argv[]) if (count || stats) { - doc_cnt++; if (*buf2) { uniq++; diff --git a/myisammrg/myrg_open.c b/myisammrg/myrg_open.c index f9cdc2bb205..af10f0da90b 100644 --- a/myisammrg/myrg_open.c +++ b/myisammrg/myrg_open.c @@ -33,7 +33,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { int save_errno,errpos=0; - uint files=0,i,dir_length,length,key_parts; + uint files= 0, i, dir_length, length, key_parts, min_keys= 0; ulonglong file_offset=0; char name_buff[FN_REFLEN*2],buff[FN_REFLEN],*end; MYRG_INFO *m_info=0; @@ -89,7 +89,10 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) else fn_format(buff, buff, "", "", 0); if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0)))) + { + my_errno= HA_ERR_WRONG_MRG_TABLE_DEF; goto err; + } if (!m_info) /* First file */ { key_parts=isam->s->base.key_parts; @@ -106,6 +109,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) files= 0; } m_info->reclength=isam->s->base.reclength; + min_keys= isam->s->base.keys; errpos=3; } m_info->open_tables[files].table= isam; @@ -121,6 +125,8 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) m_info->records+= isam->state->records; m_info->del+= isam->state->del; m_info->data_file_length+= isam->state->data_file_length; + if (min_keys > isam->s->base.keys) + min_keys= isam->s->base.keys; for (i=0; i < key_parts; i++) m_info->rec_per_key_part[i]+= (isam->s->state.rec_per_key_part[i] / m_info->tables); @@ -138,7 +144,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) my_errno=HA_ERR_RECORD_FILE_FULL; goto err; } - m_info->keys= files ? isam->s->base.keys : 0; + m_info->keys= min_keys; bzero((char*) &m_info->by_key,sizeof(m_info->by_key)); /* this works ok if the table list is empty */ diff --git a/myisammrg/myrg_queue.c b/myisammrg/myrg_queue.c index 2e600a526c0..74fdddc7748 100644 --- a/myisammrg/myrg_queue.c +++ b/myisammrg/myrg_queue.c @@ -65,6 +65,8 @@ int _myrg_init_queue(MYRG_INFO *info,int inx,enum ha_rkey_function search_flag) error=my_errno; } } + else + my_errno= error= HA_ERR_WRONG_INDEX; return error; } diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index a336f3b4108..6295fceec2b 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -7,6 +7,7 @@ insert delayed into t1 set a = 4; insert delayed into t1 set a = 5, tmsp = 19711006010203; insert delayed into t1 (a, tmsp) values (6, 19711006010203); insert delayed into t1 (a, tmsp) values (7, NULL); +FLUSH TABLE t1; insert into t1 set a = 8,tmsp=19711006010203; select * from t1 where tmsp=0; a tmsp @@ -22,6 +23,7 @@ insert delayed into t1 values (null,"c"); insert delayed into t1 values (3,"d"),(null,"e"); insert delayed into t1 values (3,"this will give an","error"); ERROR 21S01: Column count doesn't match value count at row 1 +FLUSH TABLE t1; show status like 'not_flushed_delayed_rows'; Variable_name Value Not_flushed_delayed_rows 0 @@ -54,6 +56,7 @@ insert delayed into t1 values(null); insert delayed into t1 values(null); insert delayed into t1 values(null); insert delayed into t1 values(null); +FLUSH TABLE t1; select * from t1 order by a; a 1 @@ -69,3 +72,174 @@ a 12 13 DROP TABLE t1; +SET @bug20627_old_auto_increment_offset= +@@auto_increment_offset= 2; +SET @bug20627_old_auto_increment_increment= +@@auto_increment_increment= 3; +SET @bug20627_old_session_auto_increment_offset= +@@session.auto_increment_offset= 4; +SET @bug20627_old_session_auto_increment_increment= +@@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +CREATE TABLE t1 ( +c1 INT NOT NULL AUTO_INCREMENT, +PRIMARY KEY (c1) +); +INSERT INTO t1 VALUES (NULL),(NULL),(NULL); +SELECT * FROM t1; +c1 +4 +9 +14 +DROP TABLE t1; +CREATE TABLE t1 ( +c1 INT NOT NULL AUTO_INCREMENT, +PRIMARY KEY (c1) +); +INSERT DELAYED INTO t1 VALUES (NULL),(NULL),(NULL); +FLUSH TABLE t1; +SELECT * FROM t1; +c1 +4 +9 +14 +DROP TABLE t1; +SET @@auto_increment_offset= +@bug20627_old_auto_increment_offset; +SET @@auto_increment_increment= +@bug20627_old_auto_increment_increment; +SET @@session.auto_increment_offset= +@bug20627_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= +@bug20627_old_session_auto_increment_increment; +SET @bug20830_old_auto_increment_offset= +@@auto_increment_offset= 2; +SET @bug20830_old_auto_increment_increment= +@@auto_increment_increment= 3; +SET @bug20830_old_session_auto_increment_offset= +@@session.auto_increment_offset= 4; +SET @bug20830_old_session_auto_increment_increment= +@@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +CREATE TABLE t1 ( +c1 INT(11) NOT NULL AUTO_INCREMENT, +c2 INT(11) DEFAULT NULL, +PRIMARY KEY (c1) +); +SET insert_id= 14; +INSERT INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +INSERT INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 91); +ERROR 23000: Duplicate entry '114' for key 1 +INSERT INTO t1 VALUES (NULL, 92), (NULL, 93); +SELECT * FROM t1; +c1 c2 +14 11 +19 12 +24 13 +29 21 +34 22 +39 23 +69 31 +74 32 +79 33 +84 41 +89 42 +94 43 +114 51 +119 52 +124 53 +129 61 +134 62 +139 63 +49 71 +144 72 +149 73 +154 81 +159 82 +164 83 +169 92 +174 93 +SELECT COUNT(*) FROM t1; +COUNT(*) +26 +SELECT SUM(c1) FROM t1; +SUM(c1) +2569 +DROP TABLE t1; +CREATE TABLE t1 ( +c1 INT(11) NOT NULL AUTO_INCREMENT, +c2 INT(11) DEFAULT NULL, +PRIMARY KEY (c1) +); +SET insert_id= 14; +INSERT DELAYED INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT DELAYED INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +INSERT DELAYED INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT DELAYED INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT DELAYED INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +INSERT DELAYED INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT DELAYED INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 91); +INSERT DELAYED INTO t1 VALUES (NULL, 92), (NULL, 93); +FLUSH TABLE t1; +SELECT * FROM t1; +c1 c2 +14 11 +19 12 +24 13 +29 21 +34 22 +39 23 +69 31 +74 32 +79 33 +84 41 +89 42 +94 43 +114 51 +119 52 +124 53 +129 61 +134 62 +139 63 +49 71 +144 72 +149 73 +154 81 +159 82 +164 83 +169 92 +174 93 +SELECT COUNT(*) FROM t1; +COUNT(*) +26 +SELECT SUM(c1) FROM t1; +SUM(c1) +2569 +DROP TABLE t1; +SET @@auto_increment_offset= +@bug20830_old_auto_increment_offset; +SET @@auto_increment_increment= +@bug20830_old_auto_increment_increment; +SET @@session.auto_increment_offset= +@bug20830_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= +@bug20830_old_session_auto_increment_increment; diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index b8fdd24be74..d7eb7da939d 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -178,9 +178,9 @@ t3 CREATE TABLE `t3` ( ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t1`,`t2`) create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); select * from t4; -ERROR HY000: All tables in the MERGE table are not identically defined +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists alter table t4 add column c int; -ERROR HY000: All tables in the MERGE table are not identically defined +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists create database mysqltest; create table mysqltest.t6 (a int not null primary key auto_increment, message char(20)); create table t5 (a int not null, b char(20), key(a)) engine=MERGE UNION=(test.t1,mysqltest.t6); @@ -768,6 +768,21 @@ Table Op Msg_type Msg_text test.t1 check status OK test.t2 check status OK drop table t1, t2, t3; +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(2),(1); +CREATE TABLE t2(a INT, KEY(a)) ENGINE=MERGE UNION=(t1); +SELECT * FROM t2 WHERE a=2; +ERROR HY000: Got error 124 from storage engine +DROP TABLE t1, t2; +CREATE TABLE t1(a INT) ENGINE=MEMORY; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t1, t2; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); +SELECT * FROM t2; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists +DROP TABLE t2; create table t1 (b bit(1)); create table t2 (b bit(1)); create table tm (b bit(1)) engine = merge union = (t1,t2); diff --git a/mysql-test/r/rpl_insert_id.result b/mysql-test/r/rpl_insert_id.result index e425cce9508..d0360c8b9a3 100644 --- a/mysql-test/r/rpl_insert_id.result +++ b/mysql-test/r/rpl_insert_id.result @@ -234,6 +234,135 @@ n b 2 100 3 350 drop table t1; +DROP PROCEDURE IF EXISTS p1; +DROP TABLE IF EXISTS t1, t2; +SELECT LAST_INSERT_ID(0); +LAST_INSERT_ID(0) +0 +CREATE TABLE t1 ( +id INT NOT NULL DEFAULT 0, +last_id INT, +PRIMARY KEY (id) +); +CREATE TABLE t2 ( +id INT NOT NULL AUTO_INCREMENT, +last_id INT, +PRIMARY KEY (id) +); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t2 (last_id) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (last_id) VALUES (LAST_INSERT_ID()); +END| +CALL p1(); +SELECT * FROM t1; +id last_id +0 1 +SELECT * FROM t2; +id last_id +1 0 +SELECT * FROM t1; +id last_id +0 1 +SELECT * FROM t2; +id last_id +1 0 +DROP PROCEDURE p1; +DROP TABLE t1, t2; +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 ( +i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +j INT DEFAULT 0 +); +CREATE TABLE t2 (i INT); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (i) VALUES (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (i) VALUES (NULL), (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +END | +CREATE FUNCTION f1() RETURNS INT MODIFIES SQL DATA +BEGIN +INSERT INTO t1 (i) VALUES (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +INSERT INTO t1 (i) VALUES (NULL), (NULL); +INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +RETURN 0; +END | +CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC +RETURN LAST_INSERT_ID() | +INSERT INTO t1 VALUES (NULL, -1); +CALL p1(); +SELECT f1(); +f1() +0 +INSERT INTO t1 VALUES (NULL, f2()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, f2()), (NULL, f2()); +INSERT INTO t1 VALUES (NULL, f2()); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), +(NULL, @@LAST_INSERT_ID); +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; +SELECT * FROM t1; +i j +1 -1 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 3 +9 3 +10 3 +11 3 +12 3 +13 8 +14 13 +15 5 +16 13 +17 -1 +18 14 +SELECT * FROM t2; +i +2 +3 +5 +6 +SELECT * FROM t1; +i j +1 -1 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 3 +9 3 +10 3 +11 3 +12 3 +13 8 +14 13 +15 5 +16 13 +17 -1 +18 14 +SELECT * FROM t2; +i +2 +3 +5 +6 +DROP PROCEDURE p1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1, t2; # End of 5.0 tests diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 55e8f81f763..03d8e20dd8f 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -17,7 +17,8 @@ insert delayed into t1 set a = 4; insert delayed into t1 set a = 5, tmsp = 19711006010203; insert delayed into t1 (a, tmsp) values (6, 19711006010203); insert delayed into t1 (a, tmsp) values (7, NULL); ---sleep 2 +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; insert into t1 set a = 8,tmsp=19711006010203; select * from t1 where tmsp=0; select * from t1 where tmsp=19711006010203; @@ -34,8 +35,8 @@ insert delayed into t1 values (null,"c"); insert delayed into t1 values (3,"d"),(null,"e"); --error 1136 insert delayed into t1 values (3,"this will give an","error"); -# 2 was not enough for --ps-protocol ---sleep 4 +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; show status like 'not_flushed_delayed_rows'; select * from t1; drop table t1; @@ -92,10 +93,145 @@ insert delayed into t1 values(null); # Works, since the delayed-counter is 8, which is unused insert delayed into t1 values(null); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; # Check what we have now -# must wait so that the delayed thread finishes -# Note: this must be increased if the test fails ---sleep 1 select * from t1 order by a; DROP TABLE t1; + +# +# Bug#20627 - INSERT DELAYED does not honour auto_increment_* variables +# +SET @bug20627_old_auto_increment_offset= + @@auto_increment_offset= 2; +SET @bug20627_old_auto_increment_increment= + @@auto_increment_increment= 3; +SET @bug20627_old_session_auto_increment_offset= + @@session.auto_increment_offset= 4; +SET @bug20627_old_session_auto_increment_increment= + @@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +# +# Normal insert as reference. +CREATE TABLE t1 ( + c1 INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (c1) + ); +INSERT INTO t1 VALUES (NULL),(NULL),(NULL); +# Check what we have now +SELECT * FROM t1; +DROP TABLE t1; +# +# Delayed insert. +CREATE TABLE t1 ( + c1 INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY (c1) + ); +INSERT DELAYED INTO t1 VALUES (NULL),(NULL),(NULL); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; +# Check what we have now +SELECT * FROM t1; +DROP TABLE t1; +# +# Cleanup +SET @@auto_increment_offset= + @bug20627_old_auto_increment_offset; +SET @@auto_increment_increment= + @bug20627_old_auto_increment_increment; +SET @@session.auto_increment_offset= + @bug20627_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= + @bug20627_old_session_auto_increment_increment; + +# +# Bug#20830 - INSERT DELAYED does not honour SET INSERT_ID +# +SET @bug20830_old_auto_increment_offset= + @@auto_increment_offset= 2; +SET @bug20830_old_auto_increment_increment= + @@auto_increment_increment= 3; +SET @bug20830_old_session_auto_increment_offset= + @@session.auto_increment_offset= 4; +SET @bug20830_old_session_auto_increment_increment= + @@session.auto_increment_increment= 5; +SET @@auto_increment_offset= 2; +SET @@auto_increment_increment= 3; +SET @@session.auto_increment_offset= 4; +SET @@session.auto_increment_increment= 5; +# +# Normal insert as reference. +CREATE TABLE t1 ( + c1 INT(11) NOT NULL AUTO_INCREMENT, + c2 INT(11) DEFAULT NULL, + PRIMARY KEY (c1) + ); +SET insert_id= 14; +INSERT INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +# Restart sequence at a different value. +INSERT INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +# Restart sequence at a different value. +SET insert_id= 114; +INSERT INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +# Set one value below the maximum value. +INSERT INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +# Create a duplicate value. +SET insert_id= 114; +--error 1062 +INSERT INTO t1 VALUES(NULL, 91); +INSERT INTO t1 VALUES (NULL, 92), (NULL, 93); +# Check what we have now +SELECT * FROM t1; +SELECT COUNT(*) FROM t1; +SELECT SUM(c1) FROM t1; +DROP TABLE t1; +# +# Delayed insert. +CREATE TABLE t1 ( + c1 INT(11) NOT NULL AUTO_INCREMENT, + c2 INT(11) DEFAULT NULL, + PRIMARY KEY (c1) + ); +SET insert_id= 14; +INSERT DELAYED INTO t1 VALUES(NULL, 11), (NULL, 12), (NULL, 13); +INSERT DELAYED INTO t1 VALUES(NULL, 21), (NULL, 22), (NULL, 23); +# Restart sequence at a different value. +INSERT DELAYED INTO t1 VALUES( 69, 31), (NULL, 32), (NULL, 33); +INSERT DELAYED INTO t1 VALUES(NULL, 41), (NULL, 42), (NULL, 43); +# Restart sequence at a different value. +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 51), (NULL, 52), (NULL, 53); +INSERT DELAYED INTO t1 VALUES(NULL, 61), (NULL, 62), (NULL, 63); +# Set one value below the maximum value. +INSERT DELAYED INTO t1 VALUES( 49, 71), (NULL, 72), (NULL, 73); +INSERT DELAYED INTO t1 VALUES(NULL, 81), (NULL, 82), (NULL, 83); +# Create a duplicate value. +SET insert_id= 114; +INSERT DELAYED INTO t1 VALUES(NULL, 91); +INSERT DELAYED INTO t1 VALUES (NULL, 92), (NULL, 93); +# Wait until the rows are flushed to the table files. +FLUSH TABLE t1; +# Check what we have now +SELECT * FROM t1; +SELECT COUNT(*) FROM t1; +SELECT SUM(c1) FROM t1; +DROP TABLE t1; +# +# Cleanup +SET @@auto_increment_offset= + @bug20830_old_auto_increment_offset; +SET @@auto_increment_increment= + @bug20830_old_auto_increment_increment; +SET @@session.auto_increment_offset= + @bug20830_old_session_auto_increment_offset; +SET @@session.auto_increment_increment= + @bug20830_old_session_auto_increment_increment; + diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 93fbc631680..1308b0b83a4 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -378,6 +378,31 @@ select * from t3; check table t1, t2; drop table t1, t2, t3; +# +# BUG#21617 - crash when selecting from merge table with inconsistent +# indexes +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES(2),(1); +CREATE TABLE t2(a INT, KEY(a)) ENGINE=MERGE UNION=(t1); +--error 1030 +SELECT * FROM t2 WHERE a=2; +DROP TABLE t1, t2; + +# +# BUG#10974 - No error message if merge table based on union of innodb, +# memory +# +CREATE TABLE t1(a INT) ENGINE=MEMORY; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1); +--error 1168 +SELECT * FROM t2; +DROP TABLE t1, t2; +CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3); +--error 1168 +SELECT * FROM t2; +DROP TABLE t2; + # End of 4.1 tests # diff --git a/mysql-test/t/rpl_insert_id.test b/mysql-test/t/rpl_insert_id.test index 3c46a5b4ca1..126aad68df4 100644 --- a/mysql-test/t/rpl_insert_id.test +++ b/mysql-test/t/rpl_insert_id.test @@ -244,6 +244,118 @@ select * from t1 order by n; connection master; drop table t1; + +# +# BUG#20339: stored procedure using LAST_INSERT_ID() does not +# replicate statement-based +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +# Reset result of LAST_INSERT_ID(). +SELECT LAST_INSERT_ID(0); + +CREATE TABLE t1 ( + id INT NOT NULL DEFAULT 0, + last_id INT, + PRIMARY KEY (id) +); + +CREATE TABLE t2 ( + id INT NOT NULL AUTO_INCREMENT, + last_id INT, + PRIMARY KEY (id) +); + +delimiter |; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t2 (last_id) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (last_id) VALUES (LAST_INSERT_ID()); +END| +delimiter ;| + +CALL p1(); +SELECT * FROM t1; +SELECT * FROM t2; + +sync_slave_with_master; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; + +DROP PROCEDURE p1; +DROP TABLE t1, t2; + + +# +# BUG#21726: Incorrect result with multiple invocations of +# LAST_INSERT_ID +# +--disable_warnings +DROP PROCEDURE IF EXISTS p1; +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP TABLE IF EXISTS t1, t2; +--enable_warnings + +CREATE TABLE t1 ( + i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + j INT DEFAULT 0 +); +CREATE TABLE t2 (i INT); + +delimiter |; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 (i) VALUES (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (i) VALUES (NULL), (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); +END | + +CREATE FUNCTION f1() RETURNS INT MODIFIES SQL DATA +BEGIN + INSERT INTO t1 (i) VALUES (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + INSERT INTO t1 (i) VALUES (NULL), (NULL); + INSERT INTO t2 (i) VALUES (LAST_INSERT_ID()); + RETURN 0; +END | + +CREATE FUNCTION f2() RETURNS INT NOT DETERMINISTIC + RETURN LAST_INSERT_ID() | +delimiter ;| + +INSERT INTO t1 VALUES (NULL, -1); +CALL p1(); +SELECT f1(); +INSERT INTO t1 VALUES (NULL, f2()), (NULL, LAST_INSERT_ID()), + (NULL, LAST_INSERT_ID()), (NULL, f2()), (NULL, f2()); +INSERT INTO t1 VALUES (NULL, f2()); +INSERT INTO t1 VALUES (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID(5)), + (NULL, @@LAST_INSERT_ID); +# Test replication of substitution "IS NULL" -> "= LAST_INSERT_ID". +INSERT INTO t1 VALUES (NULL, 0), (NULL, LAST_INSERT_ID()); +UPDATE t1 SET j= -1 WHERE i IS NULL; + +SELECT * FROM t1; +SELECT * FROM t2; + +sync_slave_with_master; +SELECT * FROM t1; +SELECT * FROM t2; + +connection master; +DROP PROCEDURE p1; +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1, t2; + + sync_slave_with_master; --echo diff --git a/mysys/queues.c b/mysys/queues.c index ecf1058af41..6a285ce7417 100644 --- a/mysys/queues.c +++ b/mysys/queues.c @@ -164,28 +164,22 @@ void delete_queue(QUEUE *queue) void queue_insert(register QUEUE *queue, byte *element) { - reg2 uint idx,next; + reg2 uint idx, next; int cmp; - -#ifndef DBUG_OFF - if (queue->elements < queue->max_elements) -#endif + DBUG_ASSERT(queue->elements < queue->max_elements); + queue->root[0]= element; + idx= ++queue->elements; + /* max_at_top swaps the comparison if we want to order by desc */ + while ((cmp= queue->compare(queue->first_cmp_arg, + element + queue->offset_to_key, + queue->root[(next= idx >> 1)] + + queue->offset_to_key)) && + (cmp ^ queue->max_at_top) < 0) { - queue->root[0]=element; - idx= ++queue->elements; - - /* max_at_top swaps the comparison if we want to order by desc */ - while ((cmp=queue->compare(queue->first_cmp_arg, - element+queue->offset_to_key, - queue->root[(next=idx >> 1)] + - queue->offset_to_key)) && - (cmp ^ queue->max_at_top) < 0) - { - queue->root[idx]=queue->root[next]; - idx=next; - } - queue->root[idx]=element; + queue->root[idx]= queue->root[next]; + idx= next; } + queue->root[idx]= element; } /* Remove item from queue */ @@ -193,16 +187,12 @@ void queue_insert(register QUEUE *queue, byte *element) byte *queue_remove(register QUEUE *queue, uint idx) { -#ifndef DBUG_OFF - if (idx >= queue->max_elements) - return 0; -#endif - { - byte *element=queue->root[++idx]; /* Intern index starts from 1 */ - queue->root[idx]=queue->root[queue->elements--]; - _downheap(queue,idx); - return element; - } + byte *element; + DBUG_ASSERT(idx < queue->max_elements); + element= queue->root[++idx]; /* Intern index starts from 1 */ + queue->root[idx]= queue->root[queue->elements--]; + _downheap(queue, idx); + return element; } /* Fix when element on top has been replaced */ diff --git a/sql/ha_archive.cc b/sql/ha_archive.cc index bb94a99e700..bc3c819c4ed 100644 --- a/sql/ha_archive.cc +++ b/sql/ha_archive.cc @@ -120,6 +120,8 @@ static bool archive_inited= FALSE; /* Variables for archive share methods */ pthread_mutex_t archive_mutex; static HASH archive_open_tables; +static z_off_t max_zfile_size; +static int zoffset_size; /* The file extension */ #define ARZ ".ARZ" // The data file @@ -203,6 +205,18 @@ bool archive_db_init() } else { + zoffset_size= 2 << ((zlibCompileFlags() >> 6) & 3); + switch (sizeof(z_off_t)) { + case 2: + max_zfile_size= INT_MAX16; + break; + case 8: + max_zfile_size= LONGLONG_MAX; + break; + case 4: + default: + max_zfile_size= INT_MAX32; + } archive_inited= TRUE; DBUG_RETURN(FALSE); } @@ -240,7 +254,7 @@ ha_archive::ha_archive(TABLE *table_arg) buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info); /* The size of the offset value we will use for position() */ - ref_length = 2 << ((zlibCompileFlags() >> 6) & 3); + ref_length = zoffset_size; DBUG_ASSERT(ref_length <= sizeof(z_off_t)); } @@ -480,7 +494,8 @@ int ha_archive::init_archive_writer() DBUG_RETURN(1); } share->archive_write_open= TRUE; - + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; DBUG_RETURN(0); } @@ -651,10 +666,21 @@ error: */ int ha_archive::real_write_row(byte *buf, gzFile writer) { - z_off_t written; + z_off_t written, total_row_length; uint *ptr, *end; DBUG_ENTER("ha_archive::real_write_row"); - + total_row_length= table->s->reclength; + for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields; + ptr != end; ptr++) + total_row_length+= ((Field_blob*) table->field[*ptr])->get_length(); + if (share->approx_file_size > max_zfile_size - total_row_length) + { + info(HA_STATUS_TIME); + share->approx_file_size= data_file_length; + if (share->approx_file_size > max_zfile_size - total_row_length) + DBUG_RETURN(HA_ERR_RECORD_FILE_FULL); + } + share->approx_file_size+= total_row_length; written= gzwrite(writer, buf, table->s->reclength); DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength)); if (!delayed_insert || !bulk_insert) diff --git a/sql/ha_archive.h b/sql/ha_archive.h index 2bac9fa605e..564b9f03bf5 100644 --- a/sql/ha_archive.h +++ b/sql/ha_archive.h @@ -38,6 +38,7 @@ typedef struct st_archive_share { bool dirty; /* Flag for if a flush should occur */ bool crashed; /* Meta file is crashed */ ha_rows rows_recorded; /* Number of rows in tables */ + z_off_t approx_file_size; /* Approximate archive data file size */ } ARCHIVE_SHARE; /* diff --git a/sql/item_func.cc b/sql/item_func.cc index 2e594c74031..e395a7a3af5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3345,6 +3345,34 @@ longlong Item_func_release_lock::val_int() } +bool Item_func_last_insert_id::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + + if (Item_int_func::fix_fields(thd, ref)) + return TRUE; + + if (arg_count == 0) + { + if (!thd->last_insert_id_used) + { + /* + As this statement calls LAST_INSERT_ID(), set + THD::last_insert_id_used and remember first generated insert + id of the previous statement in THD::current_insert_id. + */ + thd->last_insert_id_used= TRUE; + thd->current_insert_id= thd->last_insert_id; + } + null_value= FALSE; + } + + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + return FALSE; +} + + longlong Item_func_last_insert_id::val_int() { THD *thd= current_thd; @@ -3354,12 +3382,13 @@ longlong Item_func_last_insert_id::val_int() longlong value= args[0]->val_int(); thd->insert_id(value); null_value= args[0]->null_value; - return value; // Avoid side effect of insert_id() + return value; } - thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); + + return thd->current_insert_id; } + /* This function is just used to test speed of different functions */ longlong Item_func_benchmark::val_int() diff --git a/sql/item_func.h b/sql/item_func.h index 177daf0311f..31adc033034 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -891,6 +891,7 @@ public: if (arg_count) max_length= args[0]->max_length; } + bool fix_fields(THD *thd, Item **ref); }; diff --git a/sql/log_event.cc b/sql/log_event.cc index 219434ab218..271658d8054 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3365,7 +3365,6 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli) { switch (type) { case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; thd->last_insert_id = val; break; case INSERT_ID_EVENT: diff --git a/sql/set_var.cc b/sql/set_var.cc index d5beac0e971..3dadc632911 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2571,8 +2571,17 @@ bool sys_var_last_insert_id::update(THD *thd, set_var *var) byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { - thd->sys_var_tmp.long_value= (long) thd->insert_id(); - return (byte*) &thd->last_insert_id; + if (!thd->last_insert_id_used) + { + /* + As this statement reads @@LAST_INSERT_ID, set + THD::last_insert_id_used and remember first generated insert id + of the previous statement in THD::current_insert_id. + */ + thd->last_insert_id_used= TRUE; + thd->current_insert_id= thd->last_insert_id; + } + return (byte*) &thd->current_insert_id; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index c3657e8246d..5ed7466b0df 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -3813,7 +3813,7 @@ ER_WRONG_MRG_TABLE cze "V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì" dan "Tabellerne i MERGE er ikke defineret ens" nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities" - eng "All tables in the MERGE table are not identically defined" + eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exists" est "Kõik tabelid MERGE tabeli määratluses ei ole identsed" fre "Toutes les tables de la table de type MERGE n'ont pas la même définition" ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 093173ab949..4d47ec338c0 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -553,10 +553,24 @@ bool THD::store_globals() } -/* Cleanup after a query */ +/* + Cleanup after query. + + SYNOPSIS + THD::cleanup_after_query() + DESCRIPTION + This function is used to reset thread data to it's default state. + + NOTE + This function is not suitable for setting thread data to some + non-default values, as there is only one replication thread, so + different master threads may overwrite data of each other on + slave. +*/ void THD::cleanup_after_query() { + last_insert_id_used= FALSE; if (clear_next_insert_id) { clear_next_insert_id= 0; diff --git a/sql/sql_class.h b/sql/sql_class.h index 00648a9872d..6e1a061f279 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1252,17 +1252,29 @@ public: ulonglong next_insert_id; /* Remember last next_insert_id to reset it if something went wrong */ ulonglong prev_insert_id; + /* - The insert_id used for the last statement or set by SET LAST_INSERT_ID=# - or SELECT LAST_INSERT_ID(#). Used for binary log and returned by - LAST_INSERT_ID() + At the beginning of the statement last_insert_id holds the first + generated value of the previous statement. During statement + execution it is updated to the value just generated, but then + restored to the value that was generated first, so for the next + statement it will again be "the first generated value of the + previous statement". + + It may also be set with "LAST_INSERT_ID(expr)" or + "@@LAST_INSERT_ID= expr", but the effect of such setting will be + seen only in the next statement. */ ulonglong last_insert_id; + /* - Set to the first value that LAST_INSERT_ID() returned for the last - statement. When this is set, last_insert_id_used is set to true. + current_insert_id remembers the first generated value of the + previous statement, and does not change during statement + execution. Its value returned from LAST_INSERT_ID() and + @@LAST_INSERT_ID. */ ulonglong current_insert_id; + ulonglong limit_found_rows; ulonglong options; /* Bitmap of states */ longlong row_count_func; /* For the ROW_COUNT() function */ @@ -1325,7 +1337,22 @@ public: bool last_cuted_field; bool no_errors, password, is_fatal_error; bool query_start_used, rand_used, time_zone_used; - bool last_insert_id_used,insert_id_used, clear_next_insert_id; + + /* + last_insert_id_used is set when current statement calls + LAST_INSERT_ID() or reads @@LAST_INSERT_ID, so that binary log + LAST_INSERT_ID_EVENT be generated. + */ + bool last_insert_id_used; + + /* + insert_id_used is set when current statement updates + THD::last_insert_id, so that binary log INSERT_ID_EVENT be + generated. + */ + bool insert_id_used; + + bool clear_next_insert_id; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; bool in_lock_tables; @@ -1461,15 +1488,6 @@ public: insert_id_used=1; substitute_null_with_insert_id= TRUE; } - inline ulonglong insert_id(void) - { - if (!last_insert_id_used) - { - last_insert_id_used=1; - current_insert_id=last_insert_id; - } - return last_insert_id; - } inline ulonglong found_rows(void) { return limit_found_rows; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3db6eae8695..2ce83caa369 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -590,10 +590,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, #endif error=write_record(thd, table ,&info); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the update log. */ if (! id && thd->insert_id_used) { // Get auto increment value @@ -1303,6 +1301,9 @@ public: time_t start_time; bool query_start_used,last_insert_id_used,insert_id_used, ignore, log_query; ulonglong last_insert_id; + ulonglong next_insert_id; + ulong auto_increment_increment; + ulong auto_increment_offset; timestamp_auto_set_type timestamp_field_type; uint query_length; @@ -1684,6 +1685,22 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool igno row->last_insert_id= thd->last_insert_id; row->timestamp_field_type= table->timestamp_field_type; + /* The session variable settings can always be copied. */ + row->auto_increment_increment= thd->variables.auto_increment_increment; + row->auto_increment_offset= thd->variables.auto_increment_offset; + /* + Next insert id must be set for the first value in a multi-row insert + only. So clear it after the first use. Assume a multi-row insert. + Since the user thread doesn't really execute the insert, + thd->next_insert_id is left untouched between the rows. If we copy + the same insert id to every row of the multi-row insert, the delayed + insert thread would copy this before inserting every row. Thus it + tries to insert all rows with the same insert id. This fails on the + unique constraint. So just the first row would be really inserted. + */ + row->next_insert_id= thd->next_insert_id; + thd->next_insert_id= 0; + di->rows.push_back(row); di->stacked_inserts++; di->status=1; @@ -2055,6 +2072,14 @@ bool delayed_insert::handle_inserts(void) thd.insert_id_used=row->insert_id_used; table->timestamp_field_type= row->timestamp_field_type; + /* The session variable settings can always be copied. */ + thd.variables.auto_increment_increment= row->auto_increment_increment; + thd.variables.auto_increment_offset= row->auto_increment_offset; + /* Next insert id must be used only if non-zero. */ + if (row->next_insert_id) + thd.next_insert_id= row->next_insert_id; + DBUG_PRINT("loop", ("next_insert_id: %lu", (ulong) thd.next_insert_id)); + info.ignore= row->ignore; info.handle_duplicates= row->dup; if (info.ignore || @@ -2076,6 +2101,20 @@ bool delayed_insert::handle_inserts(void) info.error_count++; // Ignore errors thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); row->log_query = 0; + /* + We must reset next_insert_id. Otherwise all following rows may + become duplicates. If write_record() failed on a duplicate and + next_insert_id would be left unchanged, the next rows would also + be tried with the same insert id and would fail. Since the end + of a multi-row statement is unknown here, all following rows in + the queue would be dropped, regardless which thread added them. + After the queue is used up, next_insert_id is cleared and the + next run will succeed. This could even happen if these come from + the same multi-row statement as the current queue contents. That + way it would look somewhat random which rows are rejected after + a duplicate. + */ + thd.next_insert_id= 0; } if (using_ignore) { @@ -2121,6 +2160,7 @@ bool delayed_insert::handle_inserts(void) /* This should never happen */ table->file->print_error(error,MYF(0)); sql_print_error("%s",thd.net.last_error); + DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop")); goto err; } query_cache_invalidate3(&thd, table, 1); @@ -2146,6 +2186,7 @@ bool delayed_insert::handle_inserts(void) { // This shouldn't happen table->file->print_error(error,MYF(0)); sql_print_error("%s",thd.net.last_error); + DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop")); goto err; } query_cache_invalidate3(&thd, table, 1); @@ -2153,13 +2194,16 @@ bool delayed_insert::handle_inserts(void) DBUG_RETURN(0); err: + DBUG_EXECUTE("error", max_rows= 0;); /* Remove all not used rows */ while ((row=rows.get())) { delete row; thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); stacked_inserts--; + DBUG_EXECUTE("error", max_rows++;); } + DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows)); thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status); pthread_mutex_lock(&mutex); DBUG_RETURN(1); @@ -2447,7 +2491,7 @@ bool select_insert::send_data(List<Item> &values) */ table->next_number_field->reset(); if (!last_insert_id && thd->insert_id_used) - last_insert_id= thd->insert_id(); + last_insert_id= thd->last_insert_id; } } DBUG_RETURN(error); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index d5faf6ee7e9..bdc08b7bd2d 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -616,10 +616,8 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, thd->no_trans_update= no_trans_update; /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; @@ -784,10 +782,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (write_record(thd, table, &info)) DBUG_RETURN(1); /* - If auto_increment values are used, save the first one - for LAST_INSERT_ID() and for the binary/update log. - We can't use insert_id() as we don't want to touch the - last_insert_id_used flag. + If auto_increment values are used, save the first one for + LAST_INSERT_ID() and for the binary/update log. */ if (!id && thd->insert_id_used) id= thd->last_insert_id; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 18d048df393..1b69e266442 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2422,6 +2422,20 @@ mysql_execute_command(THD *thd) thd->net.no_send_error= 0; /* + Remember first generated insert id value of the previous + statement. We remember it here at the beginning of the statement, + and also in Item_func_last_insert_id::fix_fields() and + sys_var_last_insert_id::value_ptr(). Last two places are required + because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in + expression that is not executed with mysql_execute_command(). + + And we remember it here because some statements read + @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS + NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>". + */ + thd->current_insert_id= thd->last_insert_id; + + /* In many cases first table of main SELECT_LEX have special meaning => check that it is first table in global list and relink it first in queries_tables list if it is necessary (we need such relinking only @@ -5636,7 +5650,7 @@ void mysql_reset_thd_for_next_command(THD *thd) DBUG_ENTER("mysql_reset_thd_for_next_command"); thd->free_list= 0; thd->select_number= 1; - thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->query_start_used= thd->insert_id_used=0; thd->is_fatal_error= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b328d9162d5..87cbdef0522 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8142,7 +8142,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) Field *field=((Item_field*) args[0])->field; if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null && (thd->options & OPTION_AUTO_IS_NULL) && - thd->insert_id() && thd->substitute_null_with_insert_id) + thd->current_insert_id && thd->substitute_null_with_insert_id) { #ifdef HAVE_QUERY_CACHE query_cache_abort(&thd->net); @@ -8150,9 +8150,16 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", - thd->insert_id(), + thd->current_insert_id, 21)))) { + /* + Set THD::last_insert_id_used manually, as this statement + uses LAST_INSERT_ID() in a sense, and should issue + LAST_INSERT_ID_EVENT. + */ + thd->last_insert_id_used= TRUE; + cond=new_cond; /* Item_func_eq can't be fixed after creation so we do not check diff --git a/sql/sql_update.cc b/sql/sql_update.cc index a5a767b6ebc..50914fd3408 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -568,7 +568,7 @@ int mysql_update(THD *thd, thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ @@ -1567,6 +1567,6 @@ bool multi_update::send_eof() thd->row_count_func= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; ::send_ok(thd, (ulong) thd->row_count_func, - thd->insert_id_used ? thd->insert_id() : 0L,buff); + thd->insert_id_used ? thd->last_insert_id : 0L,buff); return FALSE; } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 8a444590301..d3f39296445 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -15237,6 +15237,43 @@ static void test_bug21206() DBUG_VOID_RETURN; } +/* + Bug#21726: Incorrect result with multiple invocations of + LAST_INSERT_ID + + Test that client gets updated value of insert_id on UPDATE that uses + LAST_INSERT_ID(expr). +*/ +static void test_bug21726() +{ + const char *create_table[]= + { + "DROP TABLE IF EXISTS t1", + "CREATE TABLE t1 (i INT)", + "INSERT INTO t1 VALUES (1)", + }; + const char *update_query= "UPDATE t1 SET i= LAST_INSERT_ID(i + 1)"; + int rc; + my_ulonglong insert_id; + + DBUG_ENTER("test_bug21726"); + myheader("test_bug21726"); + + fill_tables(create_table, sizeof(create_table) / sizeof(*create_table)); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 2); + + rc= mysql_query(mysql, update_query); + myquery(rc); + insert_id= mysql_insert_id(mysql); + DIE_UNLESS(insert_id == 3); + + DBUG_VOID_RETURN; +} + /* Read and parse arguments and MySQL options from my.cnf @@ -15511,7 +15548,8 @@ static struct my_tests_st my_tests[]= { { "test_bug17667", test_bug17667 }, { "test_bug19671", test_bug19671 }, { "test_bug15752", test_bug15752 }, - { "test_bug21206", test_bug21206}, + { "test_bug21206", test_bug21206 }, + { "test_bug21726", test_bug21726 }, { 0, 0 } }; |