From 4c31c41fdc3e105197f3e76476243e6c3210ba93 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Nov 2007 11:21:00 +0100 Subject: [PATCH 01/15] Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentation fault Problem was for LINEAR HASH/KEY. Crashes because of wrong partition id returned when creating the new altered partitions. (because of wrong linear hash mask) Solution: Update the linear hash mask before using it for the new altered table. mysql-test/r/partition_hash.result: Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentation fault test result mysql-test/t/partition_hash.test: Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentatition fault test case sql/ha_partition.cc: Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentation fault Updating the linear hash mask before using it. sql/sql_partition.cc: Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentation fault exporting the set_linear_hash_mask function (static -> non static) sql/sql_partition.h: Bug#30822: ALTER TABLE COALESCE PARTITION causes segmentation fault exporting the set_linear_hash_mask function (static -> non static) --- mysql-test/r/partition_hash.result | 12 ++++++++++++ mysql-test/t/partition_hash.test | 16 ++++++++++++++++ sql/ha_partition.cc | 8 ++++++++ sql/sql_partition.cc | 2 +- sql/sql_partition.h | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/partition_hash.result b/mysql-test/r/partition_hash.result index 72f036be099..94fefe77a77 100644 --- a/mysql-test/r/partition_hash.result +++ b/mysql-test/r/partition_hash.result @@ -1,4 +1,16 @@ drop table if exists t1; +CREATE TABLE t1 (c1 INT) +PARTITION BY HASH (c1) +PARTITIONS 15; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +ALTER TABLE t1 COALESCE PARTITION 13; +DROP TABLE t1; +CREATE TABLE t1 (c1 INT) +PARTITION BY LINEAR HASH (c1) +PARTITIONS 5; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +ALTER TABLE t1 COALESCE PARTITION 3; +DROP TABLE t1; create table t1 (a int unsigned) partition by hash(a div 2) partitions 4; diff --git a/mysql-test/t/partition_hash.test b/mysql-test/t/partition_hash.test index 52caaa8c8e9..362d5f747e9 100644 --- a/mysql-test/t/partition_hash.test +++ b/mysql-test/t/partition_hash.test @@ -9,6 +9,22 @@ drop table if exists t1; --enable_warnings +# +# Bug#30822: crash when COALESCE +# +CREATE TABLE t1 (c1 INT) + PARTITION BY HASH (c1) + PARTITIONS 15; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +ALTER TABLE t1 COALESCE PARTITION 13; +DROP TABLE t1; +CREATE TABLE t1 (c1 INT) + PARTITION BY LINEAR HASH (c1) + PARTITIONS 5; +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +ALTER TABLE t1 COALESCE PARTITION 3; +DROP TABLE t1; + # # More partition pruning tests, especially on interval walking # diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b53a5e3da97..d1b20896d9a 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1531,6 +1531,14 @@ int ha_partition::copy_partitions(ulonglong *copied, ulonglong *deleted) longlong func_value; DBUG_ENTER("ha_partition::copy_partitions"); + if (m_part_info->linear_hash_ind) + { + if (m_part_info->part_type == HASH_PARTITION) + set_linear_hash_mask(m_part_info, m_part_info->no_parts); + else + set_linear_hash_mask(m_part_info, m_part_info->no_subparts); + } + while (reorg_part < m_reorged_parts) { handler *file= m_reorged_file[reorg_part]; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 94b58bf913f..8d5732dfd94 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1402,7 +1402,7 @@ static void set_up_partition_func_pointers(partition_info *part_info) NONE */ -static void set_linear_hash_mask(partition_info *part_info, uint no_parts) +void set_linear_hash_mask(partition_info *part_info, uint no_parts) { uint mask; diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 56f24181b93..282e24f1853 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -65,6 +65,7 @@ int get_part_for_delete(const uchar *buf, const uchar *rec0, void prune_partition_set(const TABLE *table, part_id_range *part_spec); bool check_partition_info(partition_info *part_info,handlerton **eng_type, TABLE *table, handler *file, HA_CREATE_INFO *info); +void set_linear_hash_mask(partition_info *part_info, uint no_parts); bool fix_partition_func(THD *thd, TABLE *table, bool create_table_ind); char *generate_partition_syntax(partition_info *part_info, uint *buf_length, bool use_sql_alloc, From 13ce2d7778d42a55a51350517c11b87fef90d9cd Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 23 Nov 2007 17:51:12 +0400 Subject: [PATCH 02/15] BUG#31833 - ORDER BY leads to wrong result when ARCHIVE, BLOB and table cache is full After reading last record from freshly opened archive table (e.g. after flush table, or if there is no room in table cache), the table is reported as crashed. The problem was that azio wrongly invalidated azio_stream when it meets EOF. mysql-test/r/archive.result: A test case for BUG#31833. mysql-test/t/archive.test: A test case for BUG#31833. storage/archive/azio.c: After azread() successfuly read and inflated data, it calls check_header() function. According to the comment it is done to detect concatenated .az files. When we read last record, there are no more bytes left at the current offset, all further my_read() calls will return 0. In this case check_header() wrongly sets s->z_err to Z_ERRNO, indicating that azio_stream is broken. Following is original condition from gzio: len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; As fread() returns 0 on both EOF and error, the condition states: Invalidate gzio_stream if we got an error from last fread(). Applied the same logic to azio. Note that a test case contains FLUSH TABLE t1 prior to SELECT. It is needed because azio doesn't flush buffers immediately. Thus we may azread() last record from in-memory buffer. When we read from in-memory buffer, EOF is detected by different branch of code in azread() and we never enter check_header() in this case. --- mysql-test/r/archive.result | 7 +++++++ mysql-test/t/archive.test | 9 +++++++++ storage/archive/azio.c | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 77d3cba63d5..ed2553910a2 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -12687,3 +12687,10 @@ CREATE TABLE t1(a VARCHAR(510)) ENGINE = ARCHIVE; INSERT INTO t1(a) VALUES (''); SELECT * FROM t1 ORDER BY a; DROP TABLE t1; +CREATE TABLE t1(a INT NOT NULL AUTO_INCREMENT, b BLOB, KEY(a)) ENGINE=archive; +INSERT INTO t1 VALUES (NULL, NULL),(NULL, NULL); +FLUSH TABLE t1; +SELECT * FROM t1 ORDER BY a; +a b +1 NULL +2 NULL diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index f8eff10e30a..9595576cbce 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1589,3 +1589,12 @@ SELECT * FROM t1 ORDER BY a; --enable_result_log DROP TABLE t1; + +# +# BUG#31833 - ORDER BY leads to wrong result when ARCHIVE, BLOB and table +# cache is full +# +CREATE TABLE t1(a INT NOT NULL AUTO_INCREMENT, b BLOB, KEY(a)) ENGINE=archive; +INSERT INTO t1 VALUES (NULL, NULL),(NULL, NULL); +FLUSH TABLE t1; +SELECT * FROM t1 ORDER BY a; diff --git a/storage/archive/azio.c b/storage/archive/azio.c index 2cf0fe114d3..cada6c57918 100644 --- a/storage/archive/azio.c +++ b/storage/archive/azio.c @@ -262,7 +262,7 @@ void check_header(azio_stream *s) if (len) s->inbuf[0] = s->stream.next_in[0]; errno = 0; len = (uInt)my_read(s->file, (uchar *)s->inbuf + len, AZ_BUFSIZE_READ >> len, MYF(0)); - if (len == 0) s->z_err = Z_ERRNO; + if (len == (uInt)-1) s->z_err = Z_ERRNO; s->stream.avail_in += len; s->stream.next_in = s->inbuf; if (s->stream.avail_in < 2) { From bc3e1ff5e0df561e554c44fa64d0d71fa0bab645 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Nov 2007 10:28:25 +0400 Subject: [PATCH 03/15] Fix for bug #29258: Partitions: search fails for maximum unsigned bigint Problems: 1. looking for a matching partition we miss the fact that the maximum allowed value is in the PARTITION p LESS THAN MAXVALUE. 2. one can insert maximum value if numeric maximum value is the last range. (should only work if LESS THAN MAXVALUE). 3. one cannot have both numeric maximum value and MAXVALUE string as ranges (the same value, but different meanings). Fix: consider the maximum value as a supremum. mysql-test/r/partition.result: Fix for bug #29258: Partitions: search fails for maximum unsigned bigint - test result. mysql-test/t/partition.test: Fix for bug #29258: Partitions: search fails for maximum unsigned bigint - test case. sql/partition_info.cc: Fix for bug #29258: Partitions: search fails for maximum unsigned bigint - In case of PARTITION p VALUES LESS THAN MAXVALUE consider the maximium value as a supremum. sql/sql_partition.cc: Fix for bug #29258: Partitions: search fails for maximum unsigned bigint - In case of PARTITION p VALUES LESS THAN MAXVALUE consider the maximium value as a supremum. --- mysql-test/r/partition.result | 47 +++++++++++++++++++++++++++++++++++ mysql-test/t/partition.test | 39 +++++++++++++++++++++++++++++ sql/partition_info.cc | 7 ++++++ sql/sql_partition.cc | 12 ++++++--- 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 2b8c79b4563..22dca8888ef 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -1297,4 +1297,51 @@ create table t1 partition by key(s1) partitions 3; insert into t1 values (null,null); drop table t1; +CREATE TABLE t1 (s1 BIGINT UNSIGNED) +PARTITION BY RANGE (s1) ( +PARTITION p0 VALUES LESS THAN (0), +PARTITION p1 VALUES LESS THAN (1), +PARTITION p2 VALUES LESS THAN (18446744073709551615) +); +INSERT INTO t1 VALUES (0), (18446744073709551614); +INSERT INTO t1 VALUES (18446744073709551615); +ERROR HY000: Table has no partition for value 18446744073709551615 +DROP TABLE t1; +CREATE TABLE t1 (s1 BIGINT UNSIGNED) +PARTITION BY RANGE (s1) ( +PARTITION p0 VALUES LESS THAN (0), +PARTITION p1 VALUES LESS THAN (1), +PARTITION p2 VALUES LESS THAN (18446744073709551614), +PARTITION p3 VALUES LESS THAN MAXVALUE +); +INSERT INTO t1 VALUES (-1), (0), (18446744073709551613), +(18446744073709551614), (18446744073709551615); +Warnings: +Warning 1264 Out of range value for column 's1' at row 1 +SELECT * FROM t1; +s1 +0 +0 +18446744073709551613 +18446744073709551614 +18446744073709551615 +SELECT * FROM t1 WHERE s1 = 0; +s1 +0 +0 +SELECT * FROM t1 WHERE s1 = 18446744073709551614; +s1 +18446744073709551614 +SELECT * FROM t1 WHERE s1 = 18446744073709551615; +s1 +18446744073709551615 +DROP TABLE t1; +CREATE TABLE t1 (s1 BIGINT UNSIGNED) +PARTITION BY RANGE (s1) ( +PARTITION p0 VALUES LESS THAN (0), +PARTITION p1 VALUES LESS THAN (1), +PARTITION p2 VALUES LESS THAN (18446744073709551615), +PARTITION p3 VALUES LESS THAN MAXVALUE +); +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 68eff608879..91ec91d4b10 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -1541,4 +1541,43 @@ while ($cnt) --enable_query_log drop table t1; + +# +# Bug #29258: Partitions: search fails for maximum unsigned bigint +# +CREATE TABLE t1 (s1 BIGINT UNSIGNED) + PARTITION BY RANGE (s1) ( + PARTITION p0 VALUES LESS THAN (0), + PARTITION p1 VALUES LESS THAN (1), + PARTITION p2 VALUES LESS THAN (18446744073709551615) +); +INSERT INTO t1 VALUES (0), (18446744073709551614); +--error ER_NO_PARTITION_FOR_GIVEN_VALUE +INSERT INTO t1 VALUES (18446744073709551615); +DROP TABLE t1; + +CREATE TABLE t1 (s1 BIGINT UNSIGNED) + PARTITION BY RANGE (s1) ( + PARTITION p0 VALUES LESS THAN (0), + PARTITION p1 VALUES LESS THAN (1), + PARTITION p2 VALUES LESS THAN (18446744073709551614), + PARTITION p3 VALUES LESS THAN MAXVALUE +); +INSERT INTO t1 VALUES (-1), (0), (18446744073709551613), + (18446744073709551614), (18446744073709551615); +SELECT * FROM t1; +SELECT * FROM t1 WHERE s1 = 0; +SELECT * FROM t1 WHERE s1 = 18446744073709551614; +SELECT * FROM t1 WHERE s1 = 18446744073709551615; +DROP TABLE t1; + +CREATE TABLE t1 (s1 BIGINT UNSIGNED) + PARTITION BY RANGE (s1) ( + PARTITION p0 VALUES LESS THAN (0), + PARTITION p1 VALUES LESS THAN (1), + PARTITION p2 VALUES LESS THAN (18446744073709551615), + PARTITION p3 VALUES LESS THAN MAXVALUE +); +DROP TABLE t1; + --echo End of 5.1 tests diff --git a/sql/partition_info.cc b/sql/partition_info.cc index ab887d5dda0..67f5f48556b 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -524,6 +524,13 @@ bool partition_info::check_range_constants() current_largest= part_range_value; range_int_array[i]= part_range_value; } + else if (defined_max_value && + current_largest == part_range_value && + part_range_value == LONGLONG_MAX && + i == (no_parts - 1)) + { + range_int_array[i]= part_range_value; + } else { my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0)); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 64f96f342df..06b45e3e62a 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -2834,8 +2834,8 @@ int get_partition_id_range(partition_info *part_info, loc_part_id++; *part_id= (uint32)loc_part_id; if (loc_part_id == max_partition && - range_array[loc_part_id] != LONGLONG_MAX && - part_func_value >= range_array[loc_part_id]) + part_func_value >= range_array[loc_part_id] && + !part_info->defined_max_value) DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); DBUG_PRINT("exit",("partition: %d", *part_id)); @@ -2941,7 +2941,13 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info, } if (left_endpoint) { - if (part_func_value >= range_array[loc_part_id]) + longlong bound= range_array[loc_part_id]; + /* + In case of PARTITION p VALUES LESS THAN MAXVALUE + the maximum value is in the current partition. + */ + if (part_func_value > bound || + (part_func_value == bound && !part_info->defined_max_value)) loc_part_id++; } else From 503755256d9bb4c07a4f0fd62cec5de64c7ae21a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 26 Nov 2007 13:29:26 +0400 Subject: [PATCH 04/15] Fix for bug #32676: insert delayed crash with wrong column and function specified Problem: using wrong local lock type value in the mysql_insert() results in a crash. Fix: use a proper value. mysql-test/r/delayed.result: Fix for bug #32676: insert delayed crash with wrong column and function specified - test result. mysql-test/t/delayed.test: Fix for bug #32676: insert delayed crash with wrong column and function specified - test case. sql/sql_insert.cc: Fix for bug #32676: insert delayed crash with wrong column and function specified - the local lock_type var assigment displaced just after the line where the table_list->lock_type is filnally defined in the mysql_insert() to avoid using its old value. --- mysql-test/r/delayed.result | 5 +++++ mysql-test/t/delayed.test | 9 +++++++++ sql/sql_insert.cc | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index b37679847be..5bcedbf107f 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -255,3 +255,8 @@ CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1); INSERT DELAYED INTO t2 VALUES(1); ERROR HY000: Table storage engine for 't2' doesn't have this option DROP TABLE t1, t2; +CREATE TABLE t1 (a INT); +INSERT DELAYED INTO t1 SET b= b(); +ERROR 42S22: Unknown column 'b' in 'field list' +DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 13615c8c269..5a05301010e 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -252,3 +252,12 @@ CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1); INSERT DELAYED INTO t2 VALUES(1); DROP TABLE t1, t2; +# +# Bug #32676: insert delayed crash with wrong column and function specified +# +CREATE TABLE t1 (a INT); +--error ER_BAD_FIELD_ERROR +INSERT DELAYED INTO t1 SET b= b(); +DROP TABLE t1; + +--echo End of 5.0 tests diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 770bbd1349d..bb648d2eb83 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -585,7 +585,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->security_ctx->master_access & SUPER_ACL)); #endif - thr_lock_type lock_type = table_list->lock_type; + thr_lock_type lock_type; Item *unused_conds= 0; DBUG_ENTER("mysql_insert"); @@ -620,6 +620,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); } + lock_type= table_list->lock_type; thd->proc_info="init"; thd->used_tables=0; @@ -637,7 +638,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, /* mysql_prepare_insert set table_list->table if it was not set */ table= table_list->table; - lock_type= table_list->lock_type; context= &thd->lex->select_lex.context; /* From ba974f83712e458275ac2a3d2a803c36871cf4f0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Nov 2007 09:36:43 +0400 Subject: [PATCH 05/15] Fix for bug #32559: connection hangs on query with name_const Problem: passing a non-constant name to the NAME_CONST function results in a crash. Fix: check the NAME_CONST name argument; return fake item type if we got non-constant argument(s). mysql-test/r/func_misc.result: Fix for bug #32559: connection hangs on query with name_const - test result. mysql-test/t/func_misc.test: Fix for bug #32559: connection hangs on query with name_const - test case. sql/item.cc: Fix for bug #32559: connection hangs on query with name_const - Item_name_const::type() now returns NULL_ITEM if non-constant arguments were used to create the item to avoid wrong type casting. sql/item.h: Fix for bug #32559: connection hangs on query with name_const - NAME_CONST name argument checked for invariability. --- mysql-test/r/func_misc.result | 7 +++++++ mysql-test/t/func_misc.test | 12 +++++++++++- sql/item.cc | 12 +++++++++++- sql/item.h | 4 +++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index c941790c35b..889dfb7e078 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -207,4 +207,11 @@ test SELECT NAME_CONST('test', 'test'); test test +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (), (), (); +SELECT NAME_CONST(a, '1') FROM t1; +ERROR HY000: Incorrect arguments to NAME_CONST +SET INSERT_ID= NAME_CONST(a, a); +ERROR HY000: Incorrect arguments to NAME_CONST +DROP TABLE t1; End of 5.0 tests diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index 2c34f77b1ff..4870379d69e 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -204,5 +204,15 @@ SELECT NAME_CONST('test', 1.0); SELECT NAME_CONST('test', -1.0); SELECT NAME_CONST('test', 'test'); ---echo End of 5.0 tests +# +# Bug #32559: connection hangs on query with name_const +# +CREATE TABLE t1(a INT); +INSERT INTO t1 VALUES (), (), (); +--error ER_WRONG_ARGUMENTS +SELECT NAME_CONST(a, '1') FROM t1; +--error ER_WRONG_ARGUMENTS +SET INSERT_ID= NAME_CONST(a, a); +DROP TABLE t1; +--echo End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index 431d82af331..a75d0159848 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1209,7 +1209,17 @@ bool Item_name_const::is_null() Item::Type Item_name_const::type() const { - return value_item->type(); + /* + As + 1. one can try to create the Item_name_const passing non-constant + arguments, although it's incorrect and + 2. the type() method can be called before the fix_fields() to get + type information for a further type cast, e.g. + if (item->type() == FIELD_ITEM) + ((Item_field *) item)->... + we return NULL_ITEM in the case to avoid wrong casting. + */ + return valid_args ? value_item->type() : NULL_ITEM; } diff --git a/sql/item.h b/sql/item.h index a1135c2c725..fbf4b94b276 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1111,11 +1111,13 @@ class Item_name_const : public Item { Item *value_item; Item *name_item; + bool valid_args; public: Item_name_const(Item *name_arg, Item *val): value_item(val), name_item(name_arg) { - if(!value_item->basic_const_item()) + if (!(valid_args= name_item->basic_const_item() & + value_item->basic_const_item())) my_error(ER_WRONG_ARGUMENTS, MYF(0), "NAME_CONST"); Item::maybe_null= TRUE; } From f3bb583c3e4ea71a342f21c6e01014a05c89fd98 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 27 Nov 2007 14:01:11 +0400 Subject: [PATCH 06/15] BUG#32050 - table logging gone wrong. INSERT/UPDATE against CSV table created by MySQL earlier than 5.1.23 with NULL-able column results in server crash in debug builds. Starting with 5.1.23 CSV doesn't permit creation of NULL-able columns, but it is still possible to get such table from older MySQL versions. Fixed by removing excessive DBUG_ASSERT(). scripts/mysql_system_tables_fix.sql: Starting with 5.1.23 CSV doesn't permit creation of NULL-able columns. Alter system CSV tables structure so that all columns are NOT NULL. storage/csv/ha_tina.cc: Starting with 5.1.23 CSV doesn't permit creation of NULL-able columns, but it is still possible to get such table from older MySQL versions. Removed excessive DBUG_ASSERT(). --- scripts/mysql_system_tables_fix.sql | 16 ++++++++++++++++ storage/csv/ha_tina.cc | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index a43964bf09a..fccd1737e31 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -513,6 +513,22 @@ ALTER TABLE tables_priv MODIFY Table_priv set('Select','Insert','Update','Delete UPDATE user SET Trigger_priv=Super_priv WHERE @hadTriggerPriv = 0; +# +# Log Table +# + +ALTER TABLE general_log + MODIFY COLUMN user_host MEDIUMTEXT NOT NULL, + MODIFY COLUMN thread_id INTEGER NOT NULL, + MODIFY COLUMN server_id INTEGER NOT NULL, + MODIFY COLUMN command_type VARCHAR(64) NOT NULL, + MODIFY COLUMN argument MEDIUMTEXT NOT NULL; +ALTER TABLE slow_log + MODIFY COLUMN db VARCHAR(512) NOT NULL, + MODIFY COLUMN last_insert_id INTEGER NOT NULL, + MODIFY COLUMN insert_id INTEGER NOT NULL, + MODIFY COLUMN server_id INTEGER NOT NULL; + # Activate the new, possible modified privilege tables # This should not be needed, but gives us some extra testing that the above # changes was correct diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 6a87b8ecc72..75c3f70dec4 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -472,14 +472,6 @@ int ha_tina::encode_quote(uchar *buf) const char *ptr; const char *end_ptr; const bool was_null= (*field)->is_null(); - - /* - CSV does not support nulls. ::create() prevents creation of a table - with nullable columns so if we encounter them here, there is a bug. - This may only occur if the frm was created by an older version of - mysqld which permitted table creation with nullable columns. - */ - DBUG_ASSERT(!(*field)->maybe_null()); /* assistance for backwards compatibility in production builds. From 3f42e6ff9b98ebefdb684c0370872cb98f3a5aa2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Nov 2007 10:55:13 +0400 Subject: [PATCH 07/15] Fix for bug #32726: crash with cast in order by clause and cp932 charset - fix for #31070 (missed during merging) applied for cp932 charset. - tests/results adjusted. mysql-test/include/ctype_common.inc: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_big5.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_cp932.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_euckr.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_gb2312.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_gbk.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/r/ctype_uca.result: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. mysql-test/t/ctype_cp932.test: Fix for bug #32726: crash with cast in order by clause and cp932 charset - tests/results adjusted. strings/ctype-cp932.c: Fix for bug #32726: crash with cast in order by clause and cp932 charset - fix for #31070 (missed during merging) applied for cp932. --- mysql-test/include/ctype_common.inc | 2 + mysql-test/r/ctype_big5.result | 4 ++ mysql-test/r/ctype_cp932.result | 74 +++++++++++++++++++++++++++++ mysql-test/r/ctype_euckr.result | 4 ++ mysql-test/r/ctype_gb2312.result | 4 ++ mysql-test/r/ctype_gbk.result | 4 ++ mysql-test/r/ctype_uca.result | 4 ++ mysql-test/t/ctype_cp932.test | 4 ++ strings/ctype-cp932.c | 4 +- 9 files changed, 102 insertions(+), 2 deletions(-) diff --git a/mysql-test/include/ctype_common.inc b/mysql-test/include/ctype_common.inc index 9ee0a40c8ce..89b7ceb0c72 100644 --- a/mysql-test/include/ctype_common.inc +++ b/mysql-test/include/ctype_common.inc @@ -53,11 +53,13 @@ DROP TABLE t1; # # Bug #31070: crash during conversion of charsets +# Bug #32726: crash with cast in order by clause and cp932 charset # create table t1 (a set('a') not null); insert into t1 values (),(); select cast(a as char(1)) from t1; select a sounds like a from t1; +select 1 from t1 order by cast(a as char(1)); drop table t1; DROP DATABASE d1; diff --git a/mysql-test/r/ctype_big5.result b/mysql-test/r/ctype_big5.result index b190273cc64..8103e9b856f 100644 --- a/mysql-test/r/ctype_big5.result +++ b/mysql-test/r/ctype_big5.result @@ -64,6 +64,10 @@ select a sounds like a from t1; a sounds like a 1 1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 drop table t1; DROP DATABASE d1; USE test; diff --git a/mysql-test/r/ctype_cp932.result b/mysql-test/r/ctype_cp932.result index e3598f00777..8974a6a8594 100755 --- a/mysql-test/r/ctype_cp932.result +++ b/mysql-test/r/ctype_cp932.result @@ -2,6 +2,80 @@ drop table if exists t1; drop table if exists t2; drop table if exists t3; drop table if exists t4; +SET @test_character_set= 'cp932'; +SET @test_collation= 'cp932_japanese_ci'; +SET @safe_character_set_server= @@character_set_server; +SET @safe_collation_server= @@collation_server; +SET character_set_server= @test_character_set; +SET collation_server= @test_collation; +CREATE DATABASE d1; +USE d1; +CREATE TABLE t1 (c CHAR(10), KEY(c)); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c char(10) cp932_japanese_ci YES MUL NULL +INSERT INTO t1 VALUES ('aaa'),('aaaa'),('aaaaa'); +SELECT c as want3results FROM t1 WHERE c LIKE 'aaa%'; +want3results +aaa +aaaa +aaaaa +DROP TABLE t1; +CREATE TABLE t1 (c1 varchar(15), KEY c1 (c1(2))); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c1 varchar(15) cp932_japanese_ci YES MUL NULL +INSERT INTO t1 VALUES ('location'),('loberge'),('lotre'),('boabab'); +SELECT c1 as want3results from t1 where c1 like 'l%'; +want3results +location +loberge +lotre +SELECT c1 as want3results from t1 where c1 like 'lo%'; +want3results +location +loberge +lotre +SELECT c1 as want1result from t1 where c1 like 'loc%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'loca%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locat%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locati%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locatio%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'location%'; +want1result +location +DROP TABLE t1; +create table t1 (a set('a') not null); +insert into t1 values (),(); +Warnings: +Warning 1364 Field 'a' doesn't have a default value +select cast(a as char(1)) from t1; +cast(a as char(1)) + + +select a sounds like a from t1; +a sounds like a +1 +1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 +drop table t1; +DROP DATABASE d1; +USE test; +SET character_set_server= @safe_character_set_server; +SET collation_server= @safe_collation_server; set names cp932; set character_set_database = cp932; CREATE TABLE t1(c1 CHAR(1)) DEFAULT CHARACTER SET = cp932; diff --git a/mysql-test/r/ctype_euckr.result b/mysql-test/r/ctype_euckr.result index b9619370d4c..08fa9bd059a 100644 --- a/mysql-test/r/ctype_euckr.result +++ b/mysql-test/r/ctype_euckr.result @@ -64,6 +64,10 @@ select a sounds like a from t1; a sounds like a 1 1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 drop table t1; DROP DATABASE d1; USE test; diff --git a/mysql-test/r/ctype_gb2312.result b/mysql-test/r/ctype_gb2312.result index 90c94c3b299..95246525368 100644 --- a/mysql-test/r/ctype_gb2312.result +++ b/mysql-test/r/ctype_gb2312.result @@ -64,6 +64,10 @@ select a sounds like a from t1; a sounds like a 1 1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 drop table t1; DROP DATABASE d1; USE test; diff --git a/mysql-test/r/ctype_gbk.result b/mysql-test/r/ctype_gbk.result index fe90c7bff29..8437e34be1e 100644 --- a/mysql-test/r/ctype_gbk.result +++ b/mysql-test/r/ctype_gbk.result @@ -64,6 +64,10 @@ select a sounds like a from t1; a sounds like a 1 1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 drop table t1; DROP DATABASE d1; USE test; diff --git a/mysql-test/r/ctype_uca.result b/mysql-test/r/ctype_uca.result index e676d5a5ca0..7d6bca7e7d0 100644 --- a/mysql-test/r/ctype_uca.result +++ b/mysql-test/r/ctype_uca.result @@ -2599,6 +2599,10 @@ select a sounds like a from t1; a sounds like a 1 1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 drop table t1; DROP DATABASE d1; USE test; diff --git a/mysql-test/t/ctype_cp932.test b/mysql-test/t/ctype_cp932.test index 633f3af0d2b..3521a6fc91e 100644 --- a/mysql-test/t/ctype_cp932.test +++ b/mysql-test/t/ctype_cp932.test @@ -8,6 +8,10 @@ drop table if exists t3; drop table if exists t4; --enable_warnings +SET @test_character_set= 'cp932'; +SET @test_collation= 'cp932_japanese_ci'; +-- source include/ctype_common.inc + set names cp932; set character_set_database = cp932; diff --git a/strings/ctype-cp932.c b/strings/ctype-cp932.c index 42325648037..48c217921b1 100644 --- a/strings/ctype-cp932.c +++ b/strings/ctype-cp932.c @@ -5359,12 +5359,12 @@ my_wc_mb_cp932(CHARSET_INFO *cs __attribute__((unused)), static int my_mb_wc_cp932(CHARSET_INFO *cs __attribute__((unused)), my_wc_t *pwc, const uchar *s, const uchar *e){ - int hi=s[0]; + int hi; if (s >= e) return MY_CS_TOOSMALL; - if (hi < 0x80) + if ((hi= s[0]) < 0x80) { pwc[0]=hi; return 1; From 4ebdf739ce173dcc6821dd71e0b8d4f815c6acd0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 29 Nov 2007 22:32:34 +0400 Subject: [PATCH 08/15] after-merge fix: - result adjusted. --- .../binlog/r/binlog_row_ctype_cp932.result | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/mysql-test/suite/binlog/r/binlog_row_ctype_cp932.result b/mysql-test/suite/binlog/r/binlog_row_ctype_cp932.result index ef9f76b7d9c..655100203b5 100644 --- a/mysql-test/suite/binlog/r/binlog_row_ctype_cp932.result +++ b/mysql-test/suite/binlog/r/binlog_row_ctype_cp932.result @@ -2,6 +2,80 @@ drop table if exists t1; drop table if exists t2; drop table if exists t3; drop table if exists t4; +SET @test_character_set= 'cp932'; +SET @test_collation= 'cp932_japanese_ci'; +SET @safe_character_set_server= @@character_set_server; +SET @safe_collation_server= @@collation_server; +SET character_set_server= @test_character_set; +SET collation_server= @test_collation; +CREATE DATABASE d1; +USE d1; +CREATE TABLE t1 (c CHAR(10), KEY(c)); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c char(10) cp932_japanese_ci YES MUL NULL +INSERT INTO t1 VALUES ('aaa'),('aaaa'),('aaaaa'); +SELECT c as want3results FROM t1 WHERE c LIKE 'aaa%'; +want3results +aaa +aaaa +aaaaa +DROP TABLE t1; +CREATE TABLE t1 (c1 varchar(15), KEY c1 (c1(2))); +SHOW FULL COLUMNS FROM t1; +Field Type Collation Null Key Default Extra Privileges Comment +c1 varchar(15) cp932_japanese_ci YES MUL NULL +INSERT INTO t1 VALUES ('location'),('loberge'),('lotre'),('boabab'); +SELECT c1 as want3results from t1 where c1 like 'l%'; +want3results +location +loberge +lotre +SELECT c1 as want3results from t1 where c1 like 'lo%'; +want3results +location +loberge +lotre +SELECT c1 as want1result from t1 where c1 like 'loc%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'loca%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locat%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locati%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'locatio%'; +want1result +location +SELECT c1 as want1result from t1 where c1 like 'location%'; +want1result +location +DROP TABLE t1; +create table t1 (a set('a') not null); +insert into t1 values (),(); +Warnings: +Warning 1364 Field 'a' doesn't have a default value +select cast(a as char(1)) from t1; +cast(a as char(1)) + + +select a sounds like a from t1; +a sounds like a +1 +1 +select 1 from t1 order by cast(a as char(1)); +1 +1 +1 +drop table t1; +DROP DATABASE d1; +USE test; +SET character_set_server= @safe_character_set_server; +SET collation_server= @safe_collation_server; set names cp932; set character_set_database = cp932; CREATE TABLE t1(c1 CHAR(1)) DEFAULT CHARACTER SET = cp932; From 909142828ed0db7eb48bfc40b8e145067dd9e080 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Nov 2007 15:00:15 +0400 Subject: [PATCH 09/15] BUG#32050 - table logging gone wrong. Reverted log tables alteration. scripts/mysql_system_tables_fix.sql: Altering log table is wrong, because - it is not possible to alter log table when it is in use; - log table may use MyISAM engine, which perfectly supports NULLs. Reverted log tables alteration. --- scripts/mysql_system_tables_fix.sql | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index fccd1737e31..a43964bf09a 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -513,22 +513,6 @@ ALTER TABLE tables_priv MODIFY Table_priv set('Select','Insert','Update','Delete UPDATE user SET Trigger_priv=Super_priv WHERE @hadTriggerPriv = 0; -# -# Log Table -# - -ALTER TABLE general_log - MODIFY COLUMN user_host MEDIUMTEXT NOT NULL, - MODIFY COLUMN thread_id INTEGER NOT NULL, - MODIFY COLUMN server_id INTEGER NOT NULL, - MODIFY COLUMN command_type VARCHAR(64) NOT NULL, - MODIFY COLUMN argument MEDIUMTEXT NOT NULL; -ALTER TABLE slow_log - MODIFY COLUMN db VARCHAR(512) NOT NULL, - MODIFY COLUMN last_insert_id INTEGER NOT NULL, - MODIFY COLUMN insert_id INTEGER NOT NULL, - MODIFY COLUMN server_id INTEGER NOT NULL; - # Activate the new, possible modified privilege tables # This should not be needed, but gives us some extra testing that the above # changes was correct From dc362dae41ae345b05039eed113e3751d0a7e7f6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Nov 2007 15:16:31 +0100 Subject: [PATCH 10/15] Bug#30491 - MERGE doesn't report error when one table is Innodb 1. A bad error message was given when a MERGE table with an InnoDB child table was tried to use. 2. After selecting from a correct MERGE table and then altering one of the children to InnoDB, incorrect results were returned. These bugs have been fixed with the patch for bug 26379 (Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table). For verification, I added the test case from the bug report. mysql-test/r/merge_innodb.result: Bug#30491 - MERGE doesn't report error when one table is Innodb Added test result. mysql-test/t/merge_innodb.test: Bug#30491 - MERGE doesn't report error when one table is Innodb Added test case. --- mysql-test/r/merge_innodb.result | 37 ++++++++++++++++++++++++++++ mysql-test/t/merge_innodb.test | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 mysql-test/r/merge_innodb.result create mode 100644 mysql-test/t/merge_innodb.test diff --git a/mysql-test/r/merge_innodb.result b/mysql-test/r/merge_innodb.result new file mode 100644 index 00000000000..f6057d279b1 --- /dev/null +++ b/mysql-test/r/merge_innodb.result @@ -0,0 +1,37 @@ +DROP TABLE IF EXISTS t1, t2, t3, t4, t5; +CREATE TABLE t1 (c1 varchar(100)) ENGINE=MyISAM; +CREATE TABLE t2 (c1 varchar(100)) ENGINE=MyISAM; +CREATE TABLE t3 (c1 varchar(100)) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('Ann'), ('Alice'); +INSERT INTO t2 VALUES ('Bob'), ('Brian'); +INSERT INTO t3 VALUES ('Chris'), ('Charlie'); +CREATE TABLE t4 (c1 varchar(100)) ENGINE=MRG_MYISAM UNION=(t1,t2) +INSERT_METHOD=LAST; +CREATE TABLE t5 (c1 varchar(100)) ENGINE=MRG_MYISAM UNION=(t1,t3) +INSERT_METHOD=LAST; +SELECT * FROM t5; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM t4; +c1 +Ann +Alice +Bob +Brian +ALTER TABLE t2 ENGINE=InnoDB; +SELECT * FROM t4; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +DELETE FROM t2 LIMIT 1; +SELECT * FROM t4; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +INSERT INTO t4 VALUES ('Beware'); +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM t4; +ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist +SELECT * FROM t2; +c1 +Brian +SELECT * FROM t1; +c1 +Ann +Alice +DROP TABLE t1, t2, t3, t4, t5; diff --git a/mysql-test/t/merge_innodb.test b/mysql-test/t/merge_innodb.test new file mode 100644 index 00000000000..7f0b1a0c36e --- /dev/null +++ b/mysql-test/t/merge_innodb.test @@ -0,0 +1,41 @@ +# t/merge_innodb.test +# +# Tests with MERGE tables over InnoDB tables +# + +--source include/have_innodb.inc + +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3, t4, t5; +--enable_warnings + +# +# Bug#30491 - MERGE doesn't report error when one table is Innodb +# +CREATE TABLE t1 (c1 varchar(100)) ENGINE=MyISAM; +CREATE TABLE t2 (c1 varchar(100)) ENGINE=MyISAM; +CREATE TABLE t3 (c1 varchar(100)) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('Ann'), ('Alice'); +INSERT INTO t2 VALUES ('Bob'), ('Brian'); +INSERT INTO t3 VALUES ('Chris'), ('Charlie'); +CREATE TABLE t4 (c1 varchar(100)) ENGINE=MRG_MYISAM UNION=(t1,t2) + INSERT_METHOD=LAST; +CREATE TABLE t5 (c1 varchar(100)) ENGINE=MRG_MYISAM UNION=(t1,t3) + INSERT_METHOD=LAST; +--error ER_WRONG_MRG_TABLE +SELECT * FROM t5; +SELECT * FROM t4; +ALTER TABLE t2 ENGINE=InnoDB; +--error ER_WRONG_MRG_TABLE +SELECT * FROM t4; +DELETE FROM t2 LIMIT 1; +--error ER_WRONG_MRG_TABLE +SELECT * FROM t4; +--error ER_WRONG_MRG_TABLE +INSERT INTO t4 VALUES ('Beware'); +--error ER_WRONG_MRG_TABLE +SELECT * FROM t4; +SELECT * FROM t2; +SELECT * FROM t1; +DROP TABLE t1, t2, t3, t4, t5; + From c8969f71565e323022f9013f1ff066304cf84987 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Dec 2007 16:12:25 +0100 Subject: [PATCH 11/15] Bug#30480: Falcon: searches fail if LIKE and key partition (also fixes the bugs: Bug#29320, Bug#29493 and Bug#30536) Problem: Partitioning did not handle unordered scans correctly for engines with unordered read order. Solution: do not stop scanning fi a recored is out of range, since there can be more records within the range afterwards. Note: this is the patch that fixes the bug, but since there are no storage engines shipped with mysql 5.1 (falcon comes in 6.0) there are no test cases (it is a separate patch that only goes into 6.0) sql/ha_partition.cc: Bug#30480: Falcon: searches fail if LIKE and key partition Problem was that partitioning did not handle unordered scans correctly for engines with unordered read order. Solution: do not stop if a recored is out of range, since it can come more records within the range afterwards --- sql/ha_partition.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 1400d9da753..5abdf873c81 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -3983,7 +3983,8 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) } else if (!(error= file->index_next(buf))) { - if (compare_key(end_range) <= 0) + if (!(file->table_flags() & HA_READ_ORDER) || + compare_key(end_range) <= 0) { m_last_part= m_part_spec.start_part; DBUG_RETURN(0); // Row was in range @@ -4060,7 +4061,8 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) } if (!error) { - if (compare_key(end_range) <= 0) + if (!(file->table_flags() & HA_READ_ORDER) || + compare_key(end_range) <= 0) { m_last_part= i; DBUG_RETURN(0); From c3fceb409b9d0ae3dfc405e75b1523f970aa6477 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 6 Dec 2007 14:43:06 +0100 Subject: [PATCH 12/15] Bug#32948: FKs allowed to reference partitioned table Problem: when alter to partitioned table, it does not see it as change of engine. Solution: If alter includes partitioning, check if it is possible to change engines (eg. is the table referenced by a FK) mysql-test/r/partition_innodb.result: Bug#32948: FKs allowed to reference partitioned table test result mysql-test/t/partition_innodb.test: Bug#32948: FKs allowed to reference partitioned table test case sql/sql_table.cc: Bug#32948: FKs allowed to reference partitioned table if alter to partitioning, it is the same as changing engine. --- mysql-test/r/partition_innodb.result | 12 ++++++++++++ mysql-test/t/partition_innodb.test | 15 +++++++++++++++ sql/sql_table.cc | 3 ++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index f80e0001ea8..5cbe34c94ca 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -1,3 +1,15 @@ +# Bug#32948 +CREATE TABLE t1 (c1 INT, PRIMARY KEY (c1)) ENGINE=INNODB; +CREATE TABLE t2 (c1 INT, PRIMARY KEY (c1), +FOREIGN KEY (c1) REFERENCES t1 (c1) +ON DELETE CASCADE) +ENGINE=INNODB; +ALTER TABLE t1 PARTITION BY HASH(c1) PARTITIONS 5; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails +ALTER TABLE t1 ENGINE=MyISAM; +ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails +DROP TABLE t2; +DROP TABLE t1; create table t1 (a int) engine=innodb partition by hash(a) ; show table status like 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test index 6b73d0b2c5e..58010f85ecf 100644 --- a/mysql-test/t/partition_innodb.test +++ b/mysql-test/t/partition_innodb.test @@ -1,6 +1,21 @@ --source include/have_partition.inc --source include/have_innodb.inc +# Bug#32948 - FKs allowed to reference partitioned table +# +-- echo # Bug#32948 +CREATE TABLE t1 (c1 INT, PRIMARY KEY (c1)) ENGINE=INNODB; +CREATE TABLE t2 (c1 INT, PRIMARY KEY (c1), + FOREIGN KEY (c1) REFERENCES t1 (c1) + ON DELETE CASCADE) +ENGINE=INNODB; +--error ER_ROW_IS_REFERENCED +ALTER TABLE t1 PARTITION BY HASH(c1) PARTITIONS 5; +--error ER_ROW_IS_REFERENCED +ALTER TABLE t1 ENGINE=MyISAM; +DROP TABLE t2; +DROP TABLE t1; + # # Bug #14673: Wrong InnoDB default row format # diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 91acd8d20a3..896b681fc08 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5974,7 +5974,8 @@ view_err: goto err; new_db_type= create_info->db_type; - if (new_db_type != old_db_type && + if ((new_db_type != old_db_type || + alter_info->flags & ALTER_PARTITION) && !table->file->can_switch_engines()) { my_error(ER_ROW_IS_REFERENCED, MYF(0)); From 0fdc16bd13bca09abe4026d892d71eccb99fd121 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 7 Dec 2007 14:44:03 +0400 Subject: [PATCH 13/15] BUG#32817 - though CSV is marked as supported create table is rejected with error 1005. CSV doesn't support nullable fields. Report descriptive error if create table with nullable field is requested. mysql-test/r/csv.result: A test case for BUG#32817. mysql-test/t/csv.test: A test case for BUG#32817. storage/csv/ha_tina.cc: CSV doesn't support nullable fields. Report descriptive error if create table with nullable field is requested. --- mysql-test/r/csv.result | 10 ++++++++-- mysql-test/t/csv.test | 11 +++++++++-- storage/csv/ha_tina.cc | 5 ++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index 69f77dc3cd8..b0033383f00 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -5364,13 +5364,19 @@ BIN(a) 0 drop table t1; create table t1(a enum('foo','bar') default null) engine=csv; -ERROR HY000: Can't create table 'test.t1' (errno: -1) +ERROR 42000: The storage engine for the table doesn't support nullable columns create table t1(a enum('foo','bar') default 'foo') engine=csv; -ERROR HY000: Can't create table 'test.t1' (errno: -1) +ERROR 42000: The storage engine for the table doesn't support nullable columns create table t1(a enum('foo','bar') default 'foo' not null) engine=csv; insert into t1 values(); select * from t1; a foo drop table t1; +CREATE TABLE t1(a INT) ENGINE=CSV; +ERROR 42000: The storage engine for the table doesn't support nullable columns +SHOW WARNINGS; +Level Code Message +Error 1178 The storage engine for the table doesn't support nullable columns +Error 1005 Can't create table 'test.t1' (errno: 138) End of 5.1 tests diff --git a/mysql-test/t/csv.test b/mysql-test/t/csv.test index 6c83fbfdc9c..7b4f95bbf8a 100644 --- a/mysql-test/t/csv.test +++ b/mysql-test/t/csv.test @@ -1755,9 +1755,9 @@ insert into t1 values(); select BIN(a) from t1; drop table t1; # We prevent creation of table with nullable ENUM ---error ER_CANT_CREATE_TABLE +--error ER_CHECK_NOT_IMPLEMENTED create table t1(a enum('foo','bar') default null) engine=csv; ---error ER_CANT_CREATE_TABLE +--error ER_CHECK_NOT_IMPLEMENTED create table t1(a enum('foo','bar') default 'foo') engine=csv; # Enum columns must be specified as NOT NULL create table t1(a enum('foo','bar') default 'foo' not null) engine=csv; @@ -1765,5 +1765,12 @@ insert into t1 values(); select * from t1; drop table t1; +# +# BUG#32817 - though CSV is marked as supported create table is rejected +# with error 1005. +# +--error ER_CHECK_NOT_IMPLEMENTED +CREATE TABLE t1(a INT) ENGINE=CSV; +SHOW WARNINGS; --echo End of 5.1 tests diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index 75c3f70dec4..b5ef4620108 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -1486,7 +1486,10 @@ int ha_tina::create(const char *name, TABLE *table_arg, for (Field **field= table_arg->s->field; *field; field++) { if ((*field)->real_maybe_null()) - DBUG_RETURN(-1); + { + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns"); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } } From 2bbf12c3305295b41639c98ca805437b4efc0e3a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 7 Dec 2007 19:27:45 -0500 Subject: [PATCH 14/15] Bug#29830 Test case 'events_scheduling' fails on Mac OS X and Windows. Change LAST_EXECUTED time the execution start time, instead of the execution completion time. This ensures the END time always the same or later than the LAST_EXECUTED time. mysql-test/t/disabled.def: Enable events_scheduling test --- mysql-test/r/events_scheduling.result | 4 ++-- mysql-test/t/disabled.def | 1 - mysql-test/t/events_scheduling.test | 2 +- sql/event_data_objects.cc | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result index d45bffcd7ff..033136ba354 100644 --- a/mysql-test/r/events_scheduling.result +++ b/mysql-test/r/events_scheduling.result @@ -78,10 +78,10 @@ FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA=DATABASE() AND EVENT_NAME='event_2'; IF(TIME_TO_SEC(TIMEDIFF(ENDS,STARTS))=6, 'OK', 'ERROR') OK -SELECT IF(LAST_EXECUTED-ENDS < 3, 'OK', 'ERROR') +SELECT IF(LAST_EXECUTED-ENDS <= 0, 'OK', 'ERROR') FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA=DATABASE() AND EVENT_NAME='event_2'; -IF(LAST_EXECUTED-ENDS < 3, 'OK', 'ERROR') +IF(LAST_EXECUTED-ENDS <= 0, 'OK', 'ERROR') OK "Already dropped because ended. Therefore an error." DROP EVENT event_3; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 3746d20ebd8..234b502b22b 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -16,7 +16,6 @@ ctype_big5 : BUG#26711 2007-06-21 Lars Test has never worked on Do federated_transactions : Bug#29523 Transactions do not work events : Bug#32664 events.test fails randomly -events_scheduling : Bug#29830 Test case 'events_scheduling' fails on Mac OS X and Windows lowercase_table3 : Bug#32667 lowercase_table3.test reports to error log kill : Bug#29149: Test "kill" fails on Windows grant3 : Bug#32723: grant3.test fails diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test index b1eeae1e020..226cad0f3eb 100644 --- a/mysql-test/t/events_scheduling.test +++ b/mysql-test/t/events_scheduling.test @@ -87,7 +87,7 @@ SELECT IF(TIME_TO_SEC(TIMEDIFF(ENDS,STARTS))=6, 'OK', 'ERROR') FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA=DATABASE() AND EVENT_NAME='event_2'; -SELECT IF(LAST_EXECUTED-ENDS < 3, 'OK', 'ERROR') +SELECT IF(LAST_EXECUTED-ENDS <= 0, 'OK', 'ERROR') FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA=DATABASE() AND EVENT_NAME='event_2'; diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index adac2b596c1..f4b64ab3012 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1649,8 +1649,6 @@ err: void Event_queue_element::mark_last_executed(THD *thd) { - thd->set_current_time(); - last_executed= (my_time_t) thd->query_start(); last_executed_changed= TRUE; From e223c32077788ca9d398efa23f2431eba4b3e7fc Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Dec 2007 15:32:10 +0100 Subject: [PATCH 15/15] Bug#30273 - merge tables: Can't lock file (errno: 155) The patch for Bug 26379 (Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table) fixed this bug too. However it revealed a new bug that crashed the server. Flushing a merge table at the moment when it is between open and attach of children crashed the server. The flushing thread wants to abort locks on the flushed table. It calls ha_myisammrg::lock_count() and ha_myisammrg::store_lock() on the TABLE object of the other thread. Changed ha_myisammrg::lock_count() and ha_myisammrg::store_lock() to accept non-attached children. ha_myisammrg::lock_count() returns the number of MyISAM tables in the MERGE table so that the memory allocation done by get_lock_data() is done correctly, even if the children become attached before ha_myisammrg::store_lock() is called. ha_myisammrg::store_lock() will not return any lock if the children are not attached. This is however a change in the handler interface. lock_count() can now return a higher number than store_lock() stores locks. This is more safe than the reverse implementation would be. get_lock_data() in the SQL layer is adjusted accordingly. It sets MYSQL_LOCK::lock_count based on the number of locks returned by the handler::store_lock() calls, not based on the numbers returned by the handler::lock_count() calls. The latter are only used for allocation of memory now. No test case. The test suite cannot reliably run FLUSH between lock_count() and store_lock() of another thread. The bug report contains a program that can repeat the problem with some probability. include/myisammrg.h: Bug#30273 - merge tables: Can't lock file (errno: 155) Added mutex to struct st_myrg_info (MYRG_INFO). sql/handler.h: Bug#30273 - merge tables: Can't lock file (errno: 155) Extended comments for handler::lock_count() and handler::store_lock(). sql/lock.cc: Bug#30273 - merge tables: Can't lock file (errno: 155) Changed get_lock_data() so that the final lock_count is taken from the number of locks returned from handler::store_lock() instead of from handler::lock_count(). sql/sql_base.cc: Fixed a purecov comment. (unrelated to the rest of the changeset) storage/myisammrg/ha_myisammrg.cc: Bug#30273 - merge tables: Can't lock file (errno: 155) Changed ha_myisammrg::lock_count() and ha_myisammrg::store_lock() to accept non-attached children. Protected ha_myisammrg::store_lock() by MYRG_INFO::mutex. storage/myisammrg/myrg_close.c: Bug#30273 - merge tables: Can't lock file (errno: 155) Added MYRG_INFO::mutex destruction to myrg_parent_close(). storage/myisammrg/myrg_open.c: Bug#30273 - merge tables: Can't lock file (errno: 155) Added MYRG_INFO::mutex initialization to myrg_parent_open(). Protected myrg_attach_children() and myrg_detach_children() by MYRG_INFO::mutex. Fixed a purecov comment. (unrelated to the rest of the changeset) --- include/myisammrg.h | 1 + sql/handler.h | 11 ++++++++++- sql/lock.cc | 20 +++++++++++++++++--- sql/sql_base.cc | 2 +- storage/myisammrg/ha_myisammrg.cc | 31 +++++++++++++++++++++++++++++-- storage/myisammrg/myrg_close.c | 1 + storage/myisammrg/myrg_open.c | 17 ++++++++++++++++- 7 files changed, 75 insertions(+), 8 deletions(-) diff --git a/include/myisammrg.h b/include/myisammrg.h index 0cf3aa222b3..cc6e6aac6cd 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -74,6 +74,7 @@ typedef struct st_myrg_info LIST open_list; QUEUE by_key; ulong *rec_per_key_part; /* for sql optimizing */ + pthread_mutex_t mutex; } MYRG_INFO; diff --git a/sql/handler.h b/sql/handler.h index b91d8a39b88..140b44704a9 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1637,13 +1637,22 @@ public: virtual int repair_partitions(THD *thd) { return HA_ERR_WRONG_COMMAND; } - /* lock_count() can be more than one if the table is a MERGE */ + /** + @note lock_count() can return > 1 if the table is MERGE or partitioned. + */ virtual uint lock_count(void) const { return 1; } /** Is not invoked for non-transactional temporary tables. + @note store_lock() can return more than one lock if the table is MERGE + or partitioned. + @note that one can NOT rely on table->in_use in store_lock(). It may refer to a different thread if called from mysql_lock_abort_for_thread(). + + @note If the table is MERGE, store_lock() can return less locks + than lock_count() claimed. This can happen when the MERGE children + are not attached when this is called from another thread. */ virtual THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, diff --git a/sql/lock.cc b/sql/lock.cc index 29a07858bc1..ef4a0cc3d83 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -847,9 +847,6 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1); to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2); sql_lock->table_count=lock_count; - sql_lock->lock_count=tables; - DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d", - sql_lock->table_count, sql_lock->lock_count)); for (i=0 ; i < count ; i++) { @@ -889,6 +886,23 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, for ( ; org_locks != locks ; org_locks++) (*org_locks)->debug_print_param= (void *) table; } + /* + We do not use 'tables', because there are cases where store_lock() + returns less locks than lock_count() claimed. This can happen when + a FLUSH TABLES tries to abort locks from a MERGE table of another + thread. When that thread has just opened the table, but not yet + attached its children, it cannot return the locks. lock_count() + always returns the number of locks that an attached table has. + This is done to avoid the reverse situation: If lock_count() would + return 0 for a non-attached MERGE table, and that table becomes + attached between the calls to lock_count() and store_lock(), then + we would have allocated too little memory for the lock data. Now + we may allocate too much, but better safe than memory overrun. + And in the FLUSH case, the memory is released quickly anyway. + */ + sql_lock->lock_count= locks - locks_buf; + DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d", + sql_lock->table_count, sql_lock->lock_count)); DBUG_RETURN(sql_lock); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index a7d5848dd66..095ca26b921 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8377,7 +8377,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) We also downgrade locks after the upgrade to WRITE_ONLY */ -/* purecov: begin unused */ +/* purecov: begin deadcode */ void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) { VOID(pthread_mutex_lock(&LOCK_open)); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index f91b0dc7a92..4d3d8362a03 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -901,7 +901,14 @@ int ha_myisammrg::external_lock(THD *thd, int lock_type) uint ha_myisammrg::lock_count(void) const { - DBUG_ASSERT(this->file->children_attached); + /* + Return the real lock count even if the children are not attached. + This method is used for allocating memory. If we would return 0 + to another thread (e.g. doing FLUSH TABLE), and attach the children + before the other thread calls store_lock(), then we would return + more locks in store_lock() than we claimed by lock_count(). The + other tread would overrun its memory. + */ return file->tables; } @@ -911,7 +918,24 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, enum thr_lock_type lock_type) { MYRG_TABLE *open_table; - DBUG_ASSERT(this->file->children_attached); + + /* + This method can be called while another thread is attaching the + children. If the processor reorders instructions or write to memory, + 'children_attached' could be set before 'open_tables' has all the + pointers to the children. Use of a mutex here and in + myrg_attach_children() forces consistent data. + */ + pthread_mutex_lock(&this->file->mutex); + + /* + When MERGE table is open, but not yet attached, other threads + could flush it, which means call mysql_lock_abort_for_thread() + on this threads TABLE. 'children_attached' is FALSE in this + situaton. Since the table is not locked, return no lock data. + */ + if (!this->file->children_attached) + goto end; /* purecov: tested */ for (open_table=file->open_tables ; open_table != file->end_table ; @@ -921,6 +945,9 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) open_table->table->lock.type=lock_type; } + + end: + pthread_mutex_unlock(&this->file->mutex); return to; } diff --git a/storage/myisammrg/myrg_close.c b/storage/myisammrg/myrg_close.c index b402a35a253..97216ed47fe 100644 --- a/storage/myisammrg/myrg_close.c +++ b/storage/myisammrg/myrg_close.c @@ -58,6 +58,7 @@ int myrg_close(MYRG_INFO *info) pthread_mutex_lock(&THR_LOCK_open); myrg_open_list=list_delete(myrg_open_list,&info->open_list); pthread_mutex_unlock(&THR_LOCK_open); + VOID(pthread_mutex_destroy(&info->mutex)); my_free((uchar*) info,MYF(0)); if (error) { diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 6fb1888f84d..b5002116164 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -33,7 +33,7 @@ myrg_attach_children(). Please duplicate changes in these functions or make common sub-functions. */ -/* purecov: begin unused */ +/* purecov: begin deadcode */ /* not used in MySQL server */ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) { @@ -171,6 +171,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) VOID(my_close(fd,MYF(0))); end_io_cache(&file); + VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST)); m_info->open_list.data=(void*) m_info; pthread_mutex_lock(&THR_LOCK_open); myrg_open_list=list_add(myrg_open_list,&m_info->open_list); @@ -328,6 +329,7 @@ MYRG_INFO *myrg_parent_open(const char *parent_name, end_io_cache(&file_cache); VOID(my_close(fd, MYF(0))); + VOID(pthread_mutex_init(&m_info->mutex, MY_MUTEX_INIT_FAST)); m_info->open_list.data= (void*) m_info; pthread_mutex_lock(&THR_LOCK_open); @@ -393,6 +395,14 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, DBUG_ENTER("myrg_attach_children"); DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking)); + /* + This function can be called while another thread is trying to abort + locks of this MERGE table. If the processor reorders instructions or + write to memory, 'children_attached' could be set before + 'open_tables' has all the pointers to the children. Use of a mutex + here and in ha_myisammrg::store_lock() forces consistent data. + */ + pthread_mutex_lock(&m_info->mutex); rc= 1; errpos= 0; file_offset= 0; @@ -464,6 +474,7 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, m_info->keys= min_keys; m_info->last_used_table= m_info->open_tables; m_info->children_attached= TRUE; + pthread_mutex_unlock(&m_info->mutex); DBUG_RETURN(0); err: @@ -473,6 +484,7 @@ err: my_free((char*) m_info->rec_per_key_part, MYF(0)); m_info->rec_per_key_part= NULL; } + pthread_mutex_unlock(&m_info->mutex); my_errno= save_errno; DBUG_RETURN(1); } @@ -494,6 +506,8 @@ err: int myrg_detach_children(MYRG_INFO *m_info) { DBUG_ENTER("myrg_detach_children"); + /* For symmetry with myrg_attach_children() we use the mutex here. */ + pthread_mutex_lock(&m_info->mutex); if (m_info->tables) { /* Do not attach/detach an empty child list. */ @@ -504,6 +518,7 @@ int myrg_detach_children(MYRG_INFO *m_info) m_info->del= 0; m_info->data_file_length= 0; m_info->options= 0; + pthread_mutex_unlock(&m_info->mutex); DBUG_RETURN(0); }