From 3bd52b0b7b4cddc973b74af64c097033cb75e149 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Jul 2007 15:17:06 +0500 Subject: [PATCH 1/8] BUG#29464 - load data infile into table with big5 chinese fulltext index hangs 100% cpu Fulltext parser may fall into infinite loop when it gets illegal multibyte sequence (or a sequence that doesn't have mapping to unicode). Affects 5.1 only. mysql-test/r/fulltext.result: A test case for BUG#29464. mysql-test/t/fulltext.test: A test case for BUG#29464. storage/myisam/ft_parser.c: ctype() may return negative value, which was stored in unsigned variable. Also ctype() may return negative length for correct multibyte sequence that doesn't have a mapping to unicode. These characters are skipped correctly with this patch. --- mysql-test/r/fulltext.result | 4 ++++ mysql-test/t/fulltext.test | 9 +++++++++ storage/myisam/ft_parser.c | 16 ++++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index d8e3d53f7b1..ef8cf304743 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -476,3 +476,7 @@ ALTER TABLE t1 DISABLE KEYS; SELECT * FROM t1 WHERE MATCH(a) AGAINST('test'); ERROR HY000: Can't find FULLTEXT index matching the column list DROP TABLE t1; +CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, +FULLTEXT(a)); +INSERT INTO t1 VALUES(0xA3C2); +DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index 537ab5888bd..749e54d218a 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -399,4 +399,13 @@ ALTER TABLE t1 DISABLE KEYS; SELECT * FROM t1 WHERE MATCH(a) AGAINST('test'); DROP TABLE t1; +# +# BUG#29464 - load data infile into table with big5 chinese fulltext index +# hangs 100% cpu +# +CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, +FULLTEXT(a)); +INSERT INTO t1 VALUES(0xA3C2); +DROP TABLE t1; + # End of 4.1 tests diff --git a/storage/myisam/ft_parser.c b/storage/myisam/ft_parser.c index ba858c37aee..befe2bab066 100644 --- a/storage/myisam/ft_parser.c +++ b/storage/myisam/ft_parser.c @@ -111,7 +111,8 @@ uchar ft_get_word(CHARSET_INFO *cs, uchar **start, uchar *end, { uchar *doc=*start; int ctype; - uint mwc, length, mbl; + uint mwc, length; + int mbl; param->yesno=(FTB_YES==' ') ? 1 : (param->quot != 0); param->weight_adjust= param->wasign= 0; @@ -119,7 +120,7 @@ uchar ft_get_word(CHARSET_INFO *cs, uchar **start, uchar *end, while (doc 0 ? mbl : 1)) + for (; doc < end; doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1))) { mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end); if (true_word_char(ctype, *doc)) @@ -157,7 +158,8 @@ uchar ft_get_word(CHARSET_INFO *cs, uchar **start, uchar *end, } mwc=length=0; - for (word->pos= doc; doc < end; length++, doc+= (mbl > 0 ? mbl : 1)) + for (word->pos= doc; doc < end; length++, + doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1))) { mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end); if (true_word_char(ctype, *doc)) @@ -200,13 +202,14 @@ uchar ft_simple_get_word(CHARSET_INFO *cs, uchar **start, const uchar *end, FT_WORD *word, my_bool skip_stopwords) { uchar *doc= *start; - uint mwc, length, mbl; + uint mwc, length; + int mbl; int ctype; DBUG_ENTER("ft_simple_get_word"); do { - for (;; doc+= (mbl > 0 ? mbl : 1)) + for (;; doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1))) { if (doc >= end) DBUG_RETURN(0); @@ -216,7 +219,8 @@ uchar ft_simple_get_word(CHARSET_INFO *cs, uchar **start, const uchar *end, } mwc= length= 0; - for (word->pos= doc; doc < end; length++, doc+= (mbl > 0 ? mbl : 1)) + for (word->pos= doc; doc < end; length++, + doc+= (mbl > 0 ? mbl : (mbl < 0 ? -mbl : 1))) { mbl= cs->cset->ctype(cs, &ctype, (uchar*)doc, (uchar*)end); if (true_word_char(ctype, *doc)) From ea2ec3d96f3b615813e82a8ba691891ce44b52f6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 5 Jul 2007 19:20:14 +0200 Subject: [PATCH 2/8] Post-merge fixes. Fixed typos and comment styles in tests. mysql-test/r/rpl_partition.result: Post-merge fixes. Removed warnings after fixing --disable_warnings command. mysql-test/t/ctype_uca.test: Post-merge fixes. Fixed comment style. mysql-test/t/rpl_partition.test: Post-merge fixes. Fixed typo in --disable_warnings and --enable_warnings. mysql-test/t/subselect.test: Post-merge fixes. Fixed comment style. --- mysql-test/r/rpl_partition.result | 22 ---------------------- mysql-test/t/ctype_uca.test | 2 +- mysql-test/t/rpl_partition.test | 4 ++-- mysql-test/t/subselect.test | 2 +- 4 files changed, 4 insertions(+), 26 deletions(-) diff --git a/mysql-test/r/rpl_partition.result b/mysql-test/r/rpl_partition.result index cdd29919c48..79a95fd613b 100644 --- a/mysql-test/r/rpl_partition.result +++ b/mysql-test/r/rpl_partition.result @@ -10,31 +10,9 @@ select @@global.binlog_format, @@session.binlog_format; @@global.binlog_format ROW @@session.binlog_format ROW DROP TABLE IF EXISTS t1, t2, t3; -Warnings: -Level Note -Code 1051 -Message Unknown table 't1' -Level Note -Code 1051 -Message Unknown table 't2' -Level Note -Code 1051 -Message Unknown table 't3' DROP PROCEDURE IF EXISTS p1; -Warnings: -Level Note -Code 1305 -Message PROCEDURE p1 does not exist DROP PROCEDURE IF EXISTS p2; -Warnings: -Level Note -Code 1305 -Message PROCEDURE p2 does not exist DROP PROCEDURE IF EXISTS p3; -Warnings: -Level Note -Code 1305 -Message PROCEDURE p3 does not exist CREATE TABLE t1(id MEDIUMINT NOT NULL AUTO_INCREMENT, dt TIMESTAMP, user CHAR(255), uuidf LONGBLOB, fkid MEDIUMINT, filler VARCHAR(255), diff --git a/mysql-test/t/ctype_uca.test b/mysql-test/t/ctype_uca.test index 17632e7c8fc..695a21adbf5 100644 --- a/mysql-test/t/ctype_uca.test +++ b/mysql-test/t/ctype_uca.test @@ -530,7 +530,7 @@ create table t1 ( a varchar(255), key a(a) ) character set utf8 collate utf8_czech_ci; --- In Czech 'ch' is a single letter between 'h' and 'i' +# In Czech 'ch' is a single letter between 'h' and 'i' insert into t1 values ('b'),('c'),('d'),('e'),('f'),('g'),('h'),('ch'),('i'),('j'); select * from t1 where a like 'c%'; diff --git a/mysql-test/t/rpl_partition.test b/mysql-test/t/rpl_partition.test index 7f6e05db20e..ffd6d17ec6f 100644 --- a/mysql-test/t/rpl_partition.test +++ b/mysql-test/t/rpl_partition.test @@ -9,12 +9,12 @@ SET GLOBAL binlog_format = 'ROW'; SET SESSION binlog_format = 'ROW'; select @@global.binlog_format, @@session.binlog_format; ---disable-warnings +--disable_warnings DROP TABLE IF EXISTS t1, t2, t3; DROP PROCEDURE IF EXISTS p1; DROP PROCEDURE IF EXISTS p2; DROP PROCEDURE IF EXISTS p3; ---enable-warnings +--enable_warnings eval CREATE TABLE t1(id MEDIUMINT NOT NULL AUTO_INCREMENT, dt TIMESTAMP, user CHAR(255), uuidf LONGBLOB, diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 4c046dfd3c6..95514fd773e 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2970,7 +2970,7 @@ DROP TABLE t1,t2; CREATE TABLE t1 (a INT, b INT); INSERT INTO t1 VALUES (1, 2), (1,3), (1,4), (2,1), (2,2); --- returns no rows, when it should +# returns no rows, when it should SELECT a1.a, COUNT(*) FROM t1 a1 WHERE a1.a = 1 AND EXISTS( SELECT a2.a FROM t1 a2 WHERE a2.a = a1.a) GROUP BY a1.a; From 3e99fbbb7f736c17e5d15a210f6726befba41330 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 10 Jul 2007 15:37:39 +0500 Subject: [PATCH 3/8] BUG#29445 - match ... against () never returns Part 1: Do not perform index search for a plus-word with truncation operator in case there are other plus-words in expression. Specifically this optimization was enforced for expression like "+word1 +word2*", but wasn't enforced for expression like "+word1 word2*". mysql-test/r/fulltext.result: A test case for BUG#29445. mysql-test/t/fulltext.test: A test case for BUG#29445. storage/myisam/ft_boolean_search.c: Do not perform index search for a plus-word with truncation operator in case there are other plus-words in expression. Specifically this optimization was enforced for expression like "+word1 +word2*", but wasn't enforced for expression like "+word1 word2*". --- mysql-test/r/fulltext.result | 6 ++++++ mysql-test/t/fulltext.test | 8 ++++++++ storage/myisam/ft_boolean_search.c | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index ef8cf304743..6f6760e31bc 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -480,3 +480,9 @@ CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, FULLTEXT(a)); INSERT INTO t1 VALUES(0xA3C2); DROP TABLE t1; +CREATE TABLE t1(a VARCHAR(20), FULLTEXT(a)); +INSERT INTO t1 VALUES('Offside'),('City Of God'); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); +a +City Of God +DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index 749e54d218a..4e0c65267c4 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -408,4 +408,12 @@ FULLTEXT(a)); INSERT INTO t1 VALUES(0xA3C2); DROP TABLE t1; +# +# BUG#29445 - match ... against () never returns +# +CREATE TABLE t1(a VARCHAR(20), FULLTEXT(a)); +INSERT INTO t1 VALUES('Offside'),('City Of God'); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); +DROP TABLE t1; + # End of 4.1 tests diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c index f38561a76dd..ce93ba28da9 100644 --- a/storage/myisam/ft_boolean_search.c +++ b/storage/myisam/ft_boolean_search.c @@ -474,7 +474,8 @@ static void _ftb_init_index_search(FT_INFO *ftb) ftbe->up->flags|= FTB_FLAG_TRUNC, ftbe=ftbe->up) { if (ftbe->flags & FTB_FLAG_NO || /* 2 */ - ftbe->up->ythresh - ftbe->up->yweaks >1) /* 1 */ + ftbe->up->ythresh - ftbe->up->yweaks > + test(ftbe->flags & FTB_FLAG_YES)) /* 1 */ { FTB_EXPR *top_ftbe=ftbe->up; ftbw->docid[0]=HA_OFFSET_ERROR; From 847d6ed75bf396eeccef04205822c6314ff55065 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 11 Jul 2007 15:26:19 +0200 Subject: [PATCH 4/8] fix for smarter index mergein fulltext to work for queries like "+a (b)" mysql-test/r/fulltext.result: second test for BUG#29445 mysql-test/t/fulltext.test: second test for BUG#29445 storage/myisam/ft_boolean_search.c: fix for smarter index merge, to work for queries like "+a (b)" --- mysql-test/r/fulltext.result | 3 +++ mysql-test/t/fulltext.test | 1 + storage/myisam/ft_boolean_search.c | 25 +++++++++++++++++++------ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index 6f6760e31bc..02b115cb6dc 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -485,4 +485,7 @@ INSERT INTO t1 VALUES('Offside'),('City Of God'); SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); a City Of God +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of)*' IN BOOLEAN MODE); +a +City Of God DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index 4e0c65267c4..b1bf0036c70 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -414,6 +414,7 @@ DROP TABLE t1; CREATE TABLE t1(a VARCHAR(20), FULLTEXT(a)); INSERT INTO t1 VALUES('Offside'),('City Of God'); SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of)*' IN BOOLEAN MODE); DROP TABLE t1; # End of 4.1 tests diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c index ce93ba28da9..5c421d3688b 100644 --- a/storage/myisam/ft_boolean_search.c +++ b/storage/myisam/ft_boolean_search.c @@ -24,7 +24,7 @@ subtree, but it could be updated by plus-word only. The idea is: there is no need to search for docid smaller than - biggest docid inside current plus subtree. + biggest docid inside current plus subtree or any upper plus subtree. Examples: +word1 word2 @@ -36,6 +36,13 @@ +(word1 -word2) +(+word3 word4) share same max_docid max_docid updated by word3 + +word1 word2 (+word3 word4 (+word5 word6)) + three subexpressions (including the top-level one), + every one has its own max_docid, updated by its plus word. + but for the search word6 uses + max(word1.max_docid, word3.max_docid, word5.max_docid), + while word4 uses, accordingly, + max(word1.max_docid, word3.max_docid). */ #define FT_CORE @@ -104,7 +111,7 @@ typedef struct st_ftb_word /* ^^^^^^^^^^^^^^^^^^ FTB_{EXPR,WORD} common section */ my_off_t docid[2]; /* for index search and for scan */ my_off_t key_root; - my_off_t *max_docid; + FTB_EXPR *max_docid_expr; MI_KEYDEF *keyinfo; struct st_ftb_word *prev; float weight; @@ -208,7 +215,7 @@ static int ftb_query_add_word(MYSQL_FTPARSER_PARAM *param, for (tmp_expr= ftb_param->ftbe; tmp_expr->up; tmp_expr= tmp_expr->up) if (! (tmp_expr->flags & FTB_FLAG_YES)) break; - ftbw->max_docid= &tmp_expr->max_docid; + ftbw->max_docid_expr= tmp_expr; /* fall through */ case FT_TOKEN_STOPWORD: if (! ftb_param->up_quot) break; @@ -347,11 +354,17 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) else { uint sflag= SEARCH_BIGGER; - if (ftbw->docid[0] < *ftbw->max_docid) + my_off_t max_docid=0; + FTB_EXPR *tmp; + + for (tmp= ftbw->max_docid_expr; tmp; tmp= tmp->up) + set_if_bigger(max_docid, tmp->max_docid); + + if (ftbw->docid[0] < max_docid) { sflag|= SEARCH_SAME; _mi_dpointer(info, (uchar *)(ftbw->word + ftbw->len + HA_FT_WLEN), - *ftbw->max_docid); + max_docid); } r=_mi_search(info, ftbw->keyinfo, (uchar*) lastkey_buf, USE_WHOLE_KEY, sflag, ftbw->key_root); @@ -431,7 +444,7 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) } ftbw->docid[0]=info->lastpos; if (ftbw->flags & FTB_FLAG_YES) - *ftbw->max_docid= info->lastpos; + ftbw->max_docid_expr->max_docid= info->lastpos; return 0; } From afbac0070f7dd7e5f37d5b99375568bfe09463cc Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 Jul 2007 20:21:17 +0200 Subject: [PATCH 5/8] Bug#28158 - table->read_set is set incorrectly, causing wrong error message in Falcon An error message about a duplicate key could show a wrong key value when not all columns of the key were used to select the rows for update. Some storage engines return a record with only the selected columns filled. This is fixed by re-reading the record with a read_set which includes all columns of the duplicate key after a duplicate key error happens and before the error message is printed. mysql-test/r/ndb_update.result: Bug#28158 - table->read_set is set incorrectly, causing wrong error message in Falcon Added test result. mysql-test/t/ndb_update.test: Bug#28158 - table->read_set is set incorrectly, causing wrong error message in Falcon Added test from falcon_bug_28158. sql/sql_update.cc: Bug#28158 - table->read_set is set incorrectly, causing wrong error message in Falcon Added a function for re-reading a record with a read_set that contains all fields used by a duplicate key. Called the function before every call to handler::print_error(), which could print a duplicate key value. --- mysql-test/r/ndb_update.result | 46 +++++++++++++++++ mysql-test/t/ndb_update.test | 50 +++++++++++++++++- sql/sql_update.cc | 94 ++++++++++++++++++++++++++++++++-- 3 files changed, 186 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/ndb_update.result b/mysql-test/r/ndb_update.result index 919b8c44a40..daea0e27a6a 100644 --- a/mysql-test/r/ndb_update.result +++ b/mysql-test/r/ndb_update.result @@ -1,4 +1,6 @@ DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; CREATE TABLE t1 ( pk1 INT NOT NULL PRIMARY KEY, b INT NOT NULL, @@ -40,3 +42,47 @@ pk1 b c 12 2 2 14 1 1 DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a int, b int, KEY (a, b)) ENGINE=ndbcluster; +CREATE TABLE t2 (a int, b int, UNIQUE KEY (a, b)) ENGINE=ndbcluster; +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a, b)) ENGINE=ndbcluster; +INSERT INTO t1 VALUES (1, 2); +INSERT INTO t1 VALUES (2, 2); +INSERT INTO t2 VALUES (1, 2); +INSERT INTO t2 VALUES (2, 2); +INSERT INTO t3 VALUES (1, 2); +INSERT INTO t3 VALUES (2, 2); +UPDATE t1 SET a = 1; +UPDATE t1 SET a = 1 ORDER BY a; +UPDATE t2 SET a = 1; +ERROR 23000: Duplicate entry '' for key '*UNKNOWN*' +UPDATE t2 SET a = 1 ORDER BY a; +ERROR 23000: Duplicate entry '' for key '*UNKNOWN*' +UPDATE t3 SET a = 1; +ERROR 23000: Duplicate entry '1-2' for key 'PRIMARY' +UPDATE t3 SET a = 1 ORDER BY a; +ERROR 23000: Duplicate entry '1-2' for key 'PRIMARY' +SELECT count(*) FROM t1; +count(*) +2 +SELECT count(*) FROM t2; +count(*) +2 +SELECT count(*) FROM t3; +count(*) +2 +SELECT * FROM t1 ORDER by a; +a b +1 2 +1 2 +SELECT * FROM t2 ORDER by a; +a b +1 2 +2 2 +SELECT * FROM t3 ORDER by a; +a b +1 2 +2 2 +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +End of 5.1 tests diff --git a/mysql-test/t/ndb_update.test b/mysql-test/t/ndb_update.test index 73a0ebc69cb..c45f990edcc 100644 --- a/mysql-test/t/ndb_update.test +++ b/mysql-test/t/ndb_update.test @@ -3,10 +3,12 @@ --disable_warnings DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; --enable_warnings # -# Basic test of INSERT in NDB +# Basic test of UPDATE in NDB # # @@ -39,3 +41,49 @@ DROP TABLE IF EXISTS t1; --enable_warnings # End of 4.1 tests + +# +# Bug#28158: table->read_set is set incorrectly, +# causing wrong error message in Falcon +# +CREATE TABLE t1 (a int, b int, KEY (a, b)) ENGINE=ndbcluster; +CREATE TABLE t2 (a int, b int, UNIQUE KEY (a, b)) ENGINE=ndbcluster; +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a, b)) ENGINE=ndbcluster; +# +INSERT INTO t1 VALUES (1, 2); +INSERT INTO t1 VALUES (2, 2); +# +INSERT INTO t2 VALUES (1, 2); +INSERT INTO t2 VALUES (2, 2); +# +INSERT INTO t3 VALUES (1, 2); +INSERT INTO t3 VALUES (2, 2); +# +UPDATE t1 SET a = 1; +UPDATE t1 SET a = 1 ORDER BY a; +# +--error ER_DUP_ENTRY +UPDATE t2 SET a = 1; +--error ER_DUP_ENTRY +UPDATE t2 SET a = 1 ORDER BY a; +# +--error ER_DUP_ENTRY +UPDATE t3 SET a = 1; +--error ER_DUP_ENTRY +UPDATE t3 SET a = 1 ORDER BY a; +# +SELECT count(*) FROM t1; +SELECT count(*) FROM t2; +SELECT count(*) FROM t3; +SELECT * FROM t1 ORDER by a; +SELECT * FROM t2 ORDER by a; +SELECT * FROM t3 ORDER by a; +# +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +--enable_warnings + +--echo End of 5.1 tests + diff --git a/sql/sql_update.cc b/sql/sql_update.cc index ef1f46bfdd2..873aa6cfbeb 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -83,6 +83,75 @@ static bool check_fields(THD *thd, List &items) } +/** + @brief Re-read record if more columns are needed for error message. + + @detail If we got a duplicate key error, we want to write an error + message containing the value of the duplicate key. If we do not have + all fields of the key value in record[0], we need to re-read the + record with a proper read_set. + + @param[in] error error number + @param[in] table table +*/ + +static void prepare_record_for_error_message(int error, TABLE *table) +{ + Field **field_p; + Field *field; + uint keynr; + MY_BITMAP unique_map; /* Fields in offended unique. */ + my_bitmap_map unique_map_buf[bitmap_buffer_size(MAX_FIELDS)]; + DBUG_ENTER("prepare_record_for_error_message"); + + /* + Only duplicate key errors print the key value. + If storage engine does always read all columns, we have the value alraedy. + */ + if ((error != HA_ERR_FOUND_DUPP_KEY) || + !(table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)) + DBUG_VOID_RETURN; + + /* + Get the number of the offended index. + We will see MAX_KEY if the engine cannot determine the affected index. + */ + if ((keynr= table->file->get_dup_key(error)) >= MAX_KEY) + DBUG_VOID_RETURN; + + /* Create unique_map with all fields used by that index. */ + bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); + table->mark_columns_used_by_index_no_reset(keynr, &unique_map); + + /* Subtract read_set and write_set. */ + bitmap_subtract(&unique_map, table->read_set); + bitmap_subtract(&unique_map, table->write_set); + + /* + If the unique index uses columns that are neither in read_set + nor in write_set, we must re-read the record. + Otherwise no need to do anything. + */ + if (bitmap_is_clear_all(&unique_map)) + DBUG_VOID_RETURN; + + /* Get identifier of last read record into table->file->ref. */ + table->file->position(table->record[0]); + /* Add all fields used by unique index to read_set. */ + bitmap_union(table->read_set, &unique_map); + /* Tell the engine about the new set. */ + table->file->column_bitmaps_signal(); + /* Read record that is identified by table->file->ref. */ + (void) table->file->rnd_pos(table->record[1], table->file->ref); + /* Copy the newly read columns into the new record. */ + for (field_p= table->field; (field= *field_p); field_p++) + if (bitmap_is_set(&unique_map, field->field_index)) + field->copy_from_tmp(table->s->rec_buff_length); + + DBUG_VOID_RETURN; +} + + /* Process usual UPDATE @@ -470,6 +539,13 @@ int mysql_update(THD *thd, else will_batch= !table->file->start_bulk_update(); + /* + Assure that we can use position() + if we need to create an error message. + */ + if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) + table->prepare_for_position(); + /* We can use compare_record() to optimize away updates if the table handler is returning all columns OR if @@ -573,6 +649,8 @@ int mysql_update(THD *thd, */ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) thd->fatal_error(); /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); error= 1; break; @@ -596,13 +674,16 @@ int mysql_update(THD *thd, { if (error) { + /* purecov: begin inspected */ /* The handler should not report error of duplicate keys if they are ignored. This is a requirement on batching handlers. */ + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); error= 1; break; + /* purecov: end */ } /* Either an error was found and we are ignoring errors or there @@ -668,9 +749,12 @@ int mysql_update(THD *thd, in the batched update. */ { + /* purecov: begin inspected */ thd->fatal_error(); + prepare_record_for_error_message(loc_error, table); table->file->print_error(loc_error,MYF(0)); error= 1; + /* purecov: end */ } else updated-= dup_key_found; @@ -1540,6 +1624,8 @@ bool multi_update::send_data(List ¬_used_values) */ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)) thd->fatal_error(); /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); table->file->print_error(error,MYF(0)); DBUG_RETURN(1); } @@ -1676,7 +1762,7 @@ int multi_update::do_updates(bool from_send_error) ha_rows org_updated; TABLE *table, *tmp_table; List_iterator_fast check_opt_it(unupdated_check_opt_tables); - DBUG_ENTER("do_updates"); + DBUG_ENTER("multi_update::do_updates"); do_update= 0; // Don't retry this function if (!found) @@ -1819,6 +1905,7 @@ err: if (!from_send_error) { thd->fatal_error(); + prepare_record_for_error_message(local_error, table); table->file->print_error(local_error,MYF(0)); } @@ -1849,6 +1936,7 @@ bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; ulonglong id; + DBUG_ENTER("multi_update::send_eof"); thd->proc_info="updating reference tables"; /* Does updates for the last n - 1 tables, returns 0 if ok */ @@ -1904,7 +1992,7 @@ bool multi_update::send_eof() /* Safety: If we haven't got an error before (can happen in do_updates) */ my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update", MYF(0)); - return TRUE; + DBUG_RETURN(TRUE); } id= thd->arg_of_last_insert_id_function ? @@ -1914,5 +2002,5 @@ 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, id, buff); - return FALSE; + DBUG_RETURN(FALSE); } From 62738bf97e7a653030361f0dcedf0f4a5c97388d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 13 Jul 2007 03:25:27 +0500 Subject: [PATCH 6/8] BUG#29445 - match ... against () never returns Part 2: Searching fulltext index for a word with boolean mode truncation operator may cause infinite loop. The problem was that "smarter index merge" was used with "trunc-words", which must never happen. Affects 5.1 only. mysql-test/r/fulltext.result: Addition to a test case for BUG#29445. mysql-test/t/fulltext.test: Addition to a test case for BUG#29445. storage/myisam/ft_boolean_search.c: Fulltext "smarter index merge" optimization assumes that rows it gets are ordered by doc_id. That is not the case when we search for a word with truncation operator. It may return rows in random order. Thus we may not use "smarter index merge" optimization with "trunc-words". Also fixed compiler warning introduced by Part 1 patch. --- mysql-test/r/fulltext.result | 5 ++++- mysql-test/t/fulltext.test | 3 ++- storage/myisam/ft_boolean_search.c | 10 ++++++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index 02b115cb6dc..a649f70b6f2 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -485,7 +485,10 @@ INSERT INTO t1 VALUES('Offside'),('City Of God'); SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); a City Of God -SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of)*' IN BOOLEAN MODE); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of*)' IN BOOLEAN MODE); +a +City Of God +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city* of*' IN BOOLEAN MODE); a City Of God DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index b1bf0036c70..ecb4a0e5691 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -414,7 +414,8 @@ DROP TABLE t1; CREATE TABLE t1(a VARCHAR(20), FULLTEXT(a)); INSERT INTO t1 VALUES('Offside'),('City Of God'); SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); -SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of)*' IN BOOLEAN MODE); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city (of*)' IN BOOLEAN MODE); +SELECT a FROM t1 WHERE MATCH a AGAINST ('+city* of*' IN BOOLEAN MODE); DROP TABLE t1; # End of 4.1 tests diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c index 5c421d3688b..c881f7a7480 100644 --- a/storage/myisam/ft_boolean_search.c +++ b/storage/myisam/ft_boolean_search.c @@ -23,6 +23,12 @@ inside plus subtree. max_docid could be used by any word in plus subtree, but it could be updated by plus-word only. + Fulltext "smarter index merge" optimization assumes that rows + it gets are ordered by doc_id. That is not the case when we + search for a word with truncation operator. It may return + rows in random order. Thus we may not use "smarter index merge" + optimization with "trunc-words". + The idea is: there is no need to search for docid smaller than biggest docid inside current plus subtree or any upper plus subtree. @@ -443,7 +449,7 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) memcpy(lastkey_buf+off, info->lastkey, info->lastkey_length); } ftbw->docid[0]=info->lastpos; - if (ftbw->flags & FTB_FLAG_YES) + if (ftbw->flags & FTB_FLAG_YES && !(ftbw->flags & FTB_FLAG_TRUNC)) ftbw->max_docid_expr->max_docid= info->lastpos; return 0; } @@ -488,7 +494,7 @@ static void _ftb_init_index_search(FT_INFO *ftb) { if (ftbe->flags & FTB_FLAG_NO || /* 2 */ ftbe->up->ythresh - ftbe->up->yweaks > - test(ftbe->flags & FTB_FLAG_YES)) /* 1 */ + (uint) test(ftbe->flags & FTB_FLAG_YES)) /* 1 */ { FTB_EXPR *top_ftbe=ftbe->up; ftbw->docid[0]=HA_OFFSET_ERROR; From bae6562762e8a35793faf5c2708b1401f81f81fe Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 13 Jul 2007 03:29:25 +0500 Subject: [PATCH 7/8] BUG#29464 - load data infile into table with big5 chinese fulltext index hangs 100% cpu Moved a test case for BUG#29464 into fulltext3.test, since it requires big5 character set. mysql-test/r/fulltext.result: Moved a test case for BUG#29464 into fulltext3.test, since it requires big5 character set. mysql-test/r/fulltext3.result: Moved a test case for BUG#29464 into fulltext3.test, since it requires big5 character set. mysql-test/t/fulltext.test: Moved a test case for BUG#29464 into fulltext3.test, since it requires big5 character set. mysql-test/t/fulltext3.test: Moved a test case for BUG#29464 into fulltext3.test, since it requires big5 character set. --- mysql-test/r/fulltext.result | 4 ---- mysql-test/r/fulltext3.result | 4 ++++ mysql-test/t/fulltext.test | 9 --------- mysql-test/t/fulltext3.test | 10 ++++++++++ 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index a649f70b6f2..96ebb9bf254 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -476,10 +476,6 @@ ALTER TABLE t1 DISABLE KEYS; SELECT * FROM t1 WHERE MATCH(a) AGAINST('test'); ERROR HY000: Can't find FULLTEXT index matching the column list DROP TABLE t1; -CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, -FULLTEXT(a)); -INSERT INTO t1 VALUES(0xA3C2); -DROP TABLE t1; CREATE TABLE t1(a VARCHAR(20), FULLTEXT(a)); INSERT INTO t1 VALUES('Offside'),('City Of God'); SELECT a FROM t1 WHERE MATCH a AGAINST ('+city of*' IN BOOLEAN MODE); diff --git a/mysql-test/r/fulltext3.result b/mysql-test/r/fulltext3.result index 019d5f472ed..4ec48369ad1 100644 --- a/mysql-test/r/fulltext3.result +++ b/mysql-test/r/fulltext3.result @@ -11,3 +11,7 @@ Table Op Msg_type Msg_text test.t1 check status OK SET NAMES latin1; DROP TABLE t1; +CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, +FULLTEXT(a)); +INSERT INTO t1 VALUES(0xA3C2); +DROP TABLE t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index ecb4a0e5691..1f8a3b82cfd 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -399,15 +399,6 @@ ALTER TABLE t1 DISABLE KEYS; SELECT * FROM t1 WHERE MATCH(a) AGAINST('test'); DROP TABLE t1; -# -# BUG#29464 - load data infile into table with big5 chinese fulltext index -# hangs 100% cpu -# -CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, -FULLTEXT(a)); -INSERT INTO t1 VALUES(0xA3C2); -DROP TABLE t1; - # # BUG#29445 - match ... against () never returns # diff --git a/mysql-test/t/fulltext3.test b/mysql-test/t/fulltext3.test index a57fd48daaa..1b6a07c540f 100644 --- a/mysql-test/t/fulltext3.test +++ b/mysql-test/t/fulltext3.test @@ -22,3 +22,13 @@ DROP TABLE t1; # End of 5.0 tests +# +# BUG#29464 - load data infile into table with big5 chinese fulltext index +# hangs 100% cpu +# +CREATE TABLE t1(a VARCHAR(2) CHARACTER SET big5 COLLATE big5_chinese_ci, +FULLTEXT(a)); +INSERT INTO t1 VALUES(0xA3C2); +DROP TABLE t1; + +# End of 5.1 tests From a9c6a0bbbc3f56e859848f2f4ac21412995dde2c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 14 Jul 2007 17:58:39 +0500 Subject: [PATCH 8/8] BUG#29734 - thread_id=0 in binary log which leads to temporary table conflicts pseudo_thread_id was reset to zero via mysql_change_user() handling whereas there is no reason to do that. Moreover, having two concurrent threads that change user and create a namesake temp tables leads to recording the dup pair of queries: set @@session.pseudo_thread_id = 0; CREATE temporary table `the namesake`; which will stall the slave as the second instance can not be created. And that is the bug case. Fixed by correcting pseudo_thread_id value after mysql_change_user(). sql/sql_class.cc: Fixed that pseudo_thread_id was set to 0 after mysql_change_user(). --- sql/sql_class.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 036ba217a9b..ee4e1ea149c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -211,7 +211,7 @@ THD::THD() time_after_lock=(time_t) 0; current_linfo = 0; slave_thread = 0; - variables.pseudo_thread_id= 0; + thread_id= 0; one_shot_set= 0; file_id = 0; query_id= 0; @@ -328,6 +328,12 @@ void THD::init(void) variables.date_format); variables.datetime_format= date_time_format_copy((THD*) 0, variables.datetime_format); + /* + variables= global_system_variables above has reset + variables.pseudo_thread_id to 0. We need to correct it here to + avoid temporary tables replication failure. + */ + variables.pseudo_thread_id= thread_id; pthread_mutex_unlock(&LOCK_global_system_variables); server_status= SERVER_STATUS_AUTOCOMMIT; if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) @@ -579,6 +585,9 @@ bool THD::store_globals() By default 'slave_proxy_id' is 'thread_id'. They may later become different if this is the slave SQL thread. */ + /** @todo we already do it in init(), see if we still need to do it here. + add DBUG_ASSERT(variables.pseudo_thread_id == thread_id) + */ variables.pseudo_thread_id= thread_id; /* We have to call thr_lock_info_init() again here as THD may have been