From 5ce6fb59a03108cfff11c71ca3f660fb2d8ea11e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 22:40:55 +0100 Subject: [PATCH 01/18] disable LeakSanitizer for unit.dbug test --- dbug/tests.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dbug/tests.c b/dbug/tests.c index 22a445fdeca..70424046bf4 100644 --- a/dbug/tests.c +++ b/dbug/tests.c @@ -86,3 +86,11 @@ int main (int argc __attribute__((unused)), return 0; #endif /* DBUG_OFF */ } + +#ifdef __SANITIZE_ADDRESS__ +/* Disable LeakSanitizer in this executable */ +const char* __asan_default_options() +{ + return "detect_leaks=0"; +} +#endif From 84645366c459b01771223a6d1a20bf7ac38adf48 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 20:19:50 +0100 Subject: [PATCH 02/18] ASAN loves stack, give it some fixes these test failures in ASAN builds (in 10.1 and 10.4): * main.signal_demo3 * main.sp * sys_vars.max_sp_recursion_depth_func * mroonga/storage.foreign_key_delete_existent * mroonga/storage.foreign_key_delete_nonexistent * mroonga/storage.foreign_key_insert_existent * mroonga/storage.foreign_key_update_existent * mroonga/storage.foreign_key_update_nonexistent * mroonga/storage.function_command_auto-escape * mroonga/storage.function_command_select * mroonga/storage.variable_enable_operations_recording_insert --- include/my_pthread.h | 6 +++++- mysql-test/r/mysqld--help,win.rdiff | 2 +- mysql-test/r/mysqld--help.result | 2 +- mysql-test/suite/sys_vars/inc/sysvars_server.inc | 2 +- .../suite/sys_vars/r/sysvars_server_embedded.result | 4 ++-- .../suite/sys_vars/r/sysvars_server_notembedded.result | 4 ++-- mysql-test/suite/sys_vars/r/thread_stack_basic.result | 10 +++++----- mysql-test/suite/sys_vars/t/thread_stack_basic.test | 10 +++++----- mysql-test/t/mysqld--help.test | 2 +- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index ae2f912f979..2b3edb73528 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -734,7 +734,11 @@ extern void my_mutex_end(void); We need to have at least 256K stack to handle calls to myisamchk_init() with the current number of keys and key parts. */ -#define DEFAULT_THREAD_STACK (289*1024L) +#ifdef __SANITIZE_ADDRESS__ +#define DEFAULT_THREAD_STACK (364*1024L) +#else +#define DEFAULT_THREAD_STACK (292*1024L) +#endif #endif #define MY_PTHREAD_LOCK_READ 0 diff --git a/mysql-test/r/mysqld--help,win.rdiff b/mysql-test/r/mysqld--help,win.rdiff index 6e18d16feaf..128155870b8 100644 --- a/mysql-test/r/mysqld--help,win.rdiff +++ b/mysql-test/r/mysqld--help,win.rdiff @@ -133,6 +133,6 @@ -thread-pool-oversubscribe 3 -thread-pool-stall-limit 500 +thread-pool-min-threads 1 - thread-stack 295936 + thread-stack 299008 time-format %H:%i:%s timed-mutexes FALSE diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index fdb4d8c280d..4ff44dcd251 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1449,7 +1449,7 @@ thread-pool-idle-timeout 60 thread-pool-max-threads 1000 thread-pool-oversubscribe 3 thread-pool-stall-limit 500 -thread-stack 295936 +thread-stack 299008 time-format %H:%i:%s timed-mutexes FALSE tmp-table-size 16777216 diff --git a/mysql-test/suite/sys_vars/inc/sysvars_server.inc b/mysql-test/suite/sys_vars/inc/sysvars_server.inc index cb06b40f8c9..cffc7e7fa62 100644 --- a/mysql-test/suite/sys_vars/inc/sysvars_server.inc +++ b/mysql-test/suite/sys_vars/inc/sysvars_server.inc @@ -12,7 +12,7 @@ set sql_mode=ansi_quotes; set global div_precision_increment=5; --replace_regex /^\/\S+/PATH/ ---replace_result $MASTER_MYPORT MASTER_MYPORT +--replace_result $MASTER_MYPORT MASTER_MYPORT 372736 299008 select * from information_schema.system_variables where variable_name not like 'aria%' and variable_name not like 'debug%' and diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 4da76bdaec1..9ca6995d7ef 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3917,9 +3917,9 @@ READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME THREAD_STACK SESSION_VALUE NULL -GLOBAL_VALUE 295936 +GLOBAL_VALUE 299008 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 295936 +DEFAULT_VALUE 299008 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The stack size for each thread diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 3e34bfce8f9..9288912eb57 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -4687,9 +4687,9 @@ READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME THREAD_STACK SESSION_VALUE NULL -GLOBAL_VALUE 295936 +GLOBAL_VALUE 299008 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 295936 +DEFAULT_VALUE 299008 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The stack size for each thread diff --git a/mysql-test/suite/sys_vars/r/thread_stack_basic.result b/mysql-test/suite/sys_vars/r/thread_stack_basic.result index d5a30bbaf6e..5be5db5626c 100644 --- a/mysql-test/suite/sys_vars/r/thread_stack_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_stack_basic.result @@ -1,20 +1,20 @@ select @@global.thread_stack; @@global.thread_stack -295936 +299008 select @@session.thread_stack; ERROR HY000: Variable 'thread_stack' is a GLOBAL variable show global variables like 'thread_stack'; Variable_name Value -thread_stack 295936 +thread_stack 299008 show session variables like 'thread_stack'; Variable_name Value -thread_stack 295936 +thread_stack 299008 select * from information_schema.global_variables where variable_name='thread_stack'; VARIABLE_NAME VARIABLE_VALUE -THREAD_STACK 295936 +THREAD_STACK 299008 select * from information_schema.session_variables where variable_name='thread_stack'; VARIABLE_NAME VARIABLE_VALUE -THREAD_STACK 295936 +THREAD_STACK 299008 set global thread_stack=1; ERROR HY000: Variable 'thread_stack' is a read only variable set session thread_stack=1; diff --git a/mysql-test/suite/sys_vars/t/thread_stack_basic.test b/mysql-test/suite/sys_vars/t/thread_stack_basic.test index 2d4d144572d..bfd3fb40db3 100644 --- a/mysql-test/suite/sys_vars/t/thread_stack_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_stack_basic.test @@ -1,17 +1,17 @@ # # only global # ---replace_result 196608 262144 +--replace_result 372736 299008 select @@global.thread_stack; --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@session.thread_stack; ---replace_result 196608 262144 +--replace_result 372736 299008 show global variables like 'thread_stack'; ---replace_result 196608 262144 +--replace_result 372736 299008 show session variables like 'thread_stack'; ---replace_result 196608 262144 +--replace_result 372736 299008 select * from information_schema.global_variables where variable_name='thread_stack'; ---replace_result 196608 262144 +--replace_result 372736 299008 select * from information_schema.session_variables where variable_name='thread_stack'; # diff --git a/mysql-test/t/mysqld--help.test b/mysql-test/t/mysqld--help.test index 3fb924b9199..8b4674c140c 100644 --- a/mysql-test/t/mysqld--help.test +++ b/mysql-test/t/mysqld--help.test @@ -57,7 +57,7 @@ perl; # fixes for 32-bit s/\b4294967295\b/18446744073709551615/; s/\b2146435072\b/9223372036853727232/; - s/\b196608\b/262144/; + s/\b372736\b/299008/; s/\b4294963200\b/18446744073709547520/; foreach $var (@env) { s/\Q$ENV{$var}\E/$var/ } next if /use --skip-(use-)?symbolic-links to disable/; # for valgrind, again From 5f105e756b50930252fa1e630605b19448d5e736 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 20:14:55 +0100 Subject: [PATCH 03/18] MDEV-18625 ASAN unknown-crash in my_copy_fix_mb / ha_mroonga::storage_inplace_alter_table_add_column disable inplace alter for adding stored generated columns. This fixes mroonga/storage.column_generated_stored_add_column failures in ASAN_OPTIONS="abort_on_error=1" runs Also, add a test case that shows the bug without ASAN. --- storage/mroonga/ha_mroonga.cpp | 2 +- .../storage/r/column_generated_stored_add_column.result | 3 ++- .../storage/t/column_generated_stored_add_column.test | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index bf0c1eab3e3..98afb14c5cf 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -14525,6 +14525,7 @@ enum_alter_inplace_result ha_mroonga::wrapper_check_if_supported_inplace_alter( Alter_inplace_info::ALTER_COLUMN_NULLABLE | Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE | Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE | + Alter_inplace_info::ADD_STORED_GENERATED_COLUMN | Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT ) ) @@ -14643,7 +14644,6 @@ enum_alter_inplace_result ha_mroonga::storage_check_if_supported_inplace_alter( Alter_inplace_info::DROP_UNIQUE_INDEX | MRN_ALTER_INPLACE_INFO_ADD_VIRTUAL_COLUMN | MRN_ALTER_INPLACE_INFO_ADD_STORED_BASE_COLUMN | - MRN_ALTER_INPLACE_INFO_ADD_STORED_GENERATED_COLUMN | Alter_inplace_info::DROP_COLUMN | Alter_inplace_info::ALTER_COLUMN_NAME; if (ha_alter_info->handler_flags & explicitly_unsupported_flags) { diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_generated_stored_add_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_generated_stored_add_column.result index 20213f0cbf8..924c3134a3e 100644 --- a/storage/mroonga/mysql-test/mroonga/storage/r/column_generated_stored_add_column.result +++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_generated_stored_add_column.result @@ -1,9 +1,10 @@ -DROP TABLE IF EXISTS logs; +set names utf8mb4; CREATE TABLE logs ( id INT, record JSON ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4; INSERT INTO logs(id, record) VALUES (1, '{"level": "info", "message": "start"}'); +INSERT INTO logs(id, record) VALUES (1, json_object('message', repeat('☹', 253))); ALTER TABLE logs ADD COLUMN message VARCHAR(255) GENERATED ALWAYS AS (json_extract(`record`, '$.message')) STORED; ALTER TABLE logs ADD FULLTEXT INDEX(message) comment 'tokenizer "TokenBigramSplitSymbolAlphaDigit"'; INSERT INTO logs(id, record) VALUES (2, '{"level": "info", "message": "restart"}'); diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_generated_stored_add_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_generated_stored_add_column.test index 8561688db3a..deae021d540 100644 --- a/storage/mroonga/mysql-test/mroonga/storage/t/column_generated_stored_add_column.test +++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_generated_stored_add_column.test @@ -18,9 +18,7 @@ --source ../../include/mroonga/skip_mariadb_10_1_or_earlier.inc --source ../../include/mroonga/have_mroonga.inc ---disable_warnings -DROP TABLE IF EXISTS logs; ---enable_warnings +set names utf8mb4; CREATE TABLE logs ( id INT, @@ -28,6 +26,7 @@ CREATE TABLE logs ( ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4; INSERT INTO logs(id, record) VALUES (1, '{"level": "info", "message": "start"}'); +INSERT INTO logs(id, record) VALUES (1, json_object('message', repeat('☹', 253))); ALTER TABLE logs ADD COLUMN message VARCHAR(255) GENERATED ALWAYS AS (json_extract(`record`, '$.message')) STORED; ALTER TABLE logs ADD FULLTEXT INDEX(message) comment 'tokenizer "TokenBigramSplitSymbolAlphaDigit"'; From 2b4027e63369329a1491c1d9402ff2ddf30d3d64 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 20:15:52 +0100 Subject: [PATCH 04/18] mronga: fix a memory leak use the correct delete operator This fixes mroonga/storage.column_generated_stored_add_column failures in ASAN_OPTIONS="abort_on_error=1" runs --- storage/mroonga/ha_mroonga.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index 98afb14c5cf..289ad4fa6c4 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -2591,7 +2591,7 @@ ha_mroonga::~ha_mroonga() } if (blob_buffers) { - delete [] blob_buffers; + ::delete [] blob_buffers; } grn_obj_unlink(ctx, &top_left_point); grn_obj_unlink(ctx, &bottom_right_point); From 65070beffd2e9279145b48d1f27c517b6588e543 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 6 Mar 2019 01:19:48 +0100 Subject: [PATCH 05/18] MDEV-13818 CREATE INDEX leaks memory if running out of undo log space free already allocated indexes if row_merge_create_index() fails This fixes innodb.alter_crash failure in ASAN_OPTIONS="abort_on_error=1" runs --- storage/innobase/handler/handler0alter.cc | 5 ++++- storage/innobase/row/row0merge.cc | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index a2a13b90e3a..d0b577f7535 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4860,7 +4860,7 @@ new_clustered_failed: /* Create the indexes in SYS_INDEXES and load into dictionary. */ - for (ulint a = 0; a < ctx->num_to_add_index; a++) { + for (int a = 0; a < ctx->num_to_add_index; a++) { if (index_defs[a].ind_type & DICT_VIRTUAL && ctx->num_to_drop_vcol > 0 && !new_clustered) { @@ -4877,6 +4877,9 @@ new_clustered_failed: if (!ctx->add_index[a]) { error = ctx->trx->error_state; DBUG_ASSERT(error != DB_SUCCESS); + while (--a >= 0) { + dict_mem_index_free(ctx->add_index[a]); + } goto error_handling; } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index c1ee3d29ce1..92528ed4d2c 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -4418,6 +4418,7 @@ row_merge_create_index( this index, to ensure read consistency. */ ut_ad(index->trx_id == trx->id); } else { + dict_mem_index_free(index); index = NULL; } From 2f742b571f93e8c4a87145ee80757192f14a019b Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 20:12:02 +0100 Subject: [PATCH 06/18] MDEV-18376 Memory leak in main.mysqladmin don't initialize mysql structure before it actually becomes needed. This fixes main.mysqladmin failures in ASAN_OPTIONS="abort_on_error=1" runs --- client/mysqladmin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 5e7fb80b2b5..b5e54be899a 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -318,7 +318,6 @@ int main(int argc,char *argv[]) char **commands, **save_argv, **temp_argv; MY_INIT(argv[0]); - mysql_init(&mysql); sf_leaking_memory=1; /* don't report memory leaks on early exits */ load_defaults_or_exit("my", load_default_groups, &argc, &argv); save_argv = argv; /* Save for free_defaults */ @@ -347,6 +346,7 @@ int main(int argc,char *argv[]) sf_leaking_memory=0; /* from now on we cleanup properly */ + mysql_init(&mysql); if (opt_compress) mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS); if (opt_connect_timeout) From 57dd892ce8dc93bcd7eee953e01ebe8ed7e414c8 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 00:32:04 +0100 Subject: [PATCH 07/18] fix memory leaks in mysql_client_test This fixes main.mysql_client_test, main.mysql_client_test_comp, main.mysql_client_test_nonblock failures in ASAN_OPTIONS="abort_on_error=1" runs --- tests/mysql_client_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index d12ca2ab96a..9c64342954a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -18426,6 +18426,7 @@ static void test_bug42373() DIE_UNLESS(rc == 1); mysql_stmt_close(stmt); + mysql_close(&con); /* Now try with a multi-statement. */ DIE_UNLESS(mysql_client_init(&con)); @@ -18888,8 +18889,6 @@ static void test_progress_reporting() conn= client_connect(CLIENT_PROGRESS_OBSOLETE, MYSQL_PROTOCOL_TCP, 0); - if (!(conn->server_capabilities & CLIENT_PROGRESS_OBSOLETE)) - return; DIE_UNLESS(conn->client_flag & CLIENT_PROGRESS_OBSOLETE); mysql_options(conn, MYSQL_PROGRESS_CALLBACK, (void*) report_progress); @@ -19739,6 +19738,7 @@ static void test_bulk_delete() DIE_IF(atoi(row[0]) != 3); } DIE_IF(i != 1); + mysql_free_result(result); rc= mysql_query(mysql, "DROP TABLE t1"); myquery(rc); From 2faefe5f7f432f8d9b3435dd8f4e2a37163c5527 Mon Sep 17 00:00:00 2001 From: Galina Shalygina Date: Sat, 23 Feb 2019 22:16:33 +0300 Subject: [PATCH 08/18] MDEV-18383: Missing rows with pushdown condition defined with IF-function using Item_cond This bug is similar to the bug MDEV-16765. It appears because of the wrong pushdown into HAVING clause while this pushdown shouldn't be made at all. This happens because function that checks if Item_cond can be pushed always returns that it can be pushed. To fix it new method Item_cond::excl_dep_on_table() was added. --- mysql-test/r/derived_cond_pushdown.result | 21 +++++++++++++++++++++ mysql-test/t/derived_cond_pushdown.test | 22 ++++++++++++++++++++++ sql/item_cmpfunc.cc | 17 +++++++++++++++++ sql/item_cmpfunc.h | 1 + 4 files changed, 61 insertions(+) diff --git a/mysql-test/r/derived_cond_pushdown.result b/mysql-test/r/derived_cond_pushdown.result index 14c8e4d5e8f..00acdf241a6 100644 --- a/mysql-test/r/derived_cond_pushdown.result +++ b/mysql-test/r/derived_cond_pushdown.result @@ -10480,4 +10480,25 @@ EXPLAIN } DROP VIEW v1,v2; DROP TABLE t1; +# +# MDEV-18383: pushdown condition with the IF structure +# defined with Item_cond item +# +CREATE TABLE t1(a INT, b INT); +CREATE TABLE t2(c INT, d INT); +INSERT INTO t1 VALUES (1,2),(3,4),(5,6); +INSERT INTO t2 VALUES (1,3),(3,7),(5,1); +SELECT * +FROM t1, +( +SELECT MAX(d) AS max_d,c +FROM t2 +GROUP BY c +) AS tab +WHERE t1.a=tab.c AND +IF(2,t1.a=1 OR t1.b>5,1=1); +a b max_d c +1 2 3 1 +5 6 1 5 +DROP TABLE t1,t2; # End of 10.2 tests diff --git a/mysql-test/t/derived_cond_pushdown.test b/mysql-test/t/derived_cond_pushdown.test index 25cb29e13db..313d77d2332 100644 --- a/mysql-test/t/derived_cond_pushdown.test +++ b/mysql-test/t/derived_cond_pushdown.test @@ -2102,4 +2102,26 @@ eval EXPLAIN FORMAT=JSON $q2; DROP VIEW v1,v2; DROP TABLE t1; +--echo # +--echo # MDEV-18383: pushdown condition with the IF structure +--echo # defined with Item_cond item +--echo # + +CREATE TABLE t1(a INT, b INT); +CREATE TABLE t2(c INT, d INT); +INSERT INTO t1 VALUES (1,2),(3,4),(5,6); +INSERT INTO t2 VALUES (1,3),(3,7),(5,1); + +SELECT * +FROM t1, +( + SELECT MAX(d) AS max_d,c + FROM t2 + GROUP BY c +) AS tab +WHERE t1.a=tab.c AND + IF(2,t1.a=1 OR t1.b>5,1=1); + +DROP TABLE t1,t2; + --echo # End of 10.2 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7357e57733a..badb0191471 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4994,6 +4994,23 @@ Item *Item_cond::build_clone(THD *thd, MEM_ROOT *mem_root) } +bool Item_cond::excl_dep_on_table(table_map tab_map) +{ + if (used_tables() & OUTER_REF_TABLE_BIT) + return false; + if (!(used_tables() & ~tab_map)) + return true; + List_iterator_fast li(list); + Item *item; + while ((item= li++)) + { + if (!item->excl_dep_on_table(tab_map)) + return false; + } + return true; +} + + bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel) { List_iterator_fast li(list); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1b119c743d3..d083248f6cd 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -2229,6 +2229,7 @@ public: Item_transformer transformer, uchar *arg_t); bool eval_not_null_tables(void *opt_arg); Item *build_clone(THD *thd, MEM_ROOT *mem_root); + bool excl_dep_on_table(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); }; From 8024f8c6b86b204c3475e562587640cf2b141683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Mar 2019 11:57:14 +0200 Subject: [PATCH 09/18] MDEV-18272 InnoDB fails to rollback after exceeding FOREIGN KEY recursion depth row_mysql_handle_errors(): Correct the wrong error handling for the code DB_FOREIGN_EXCEED_MAX_CASCADE that was introduced in https://github.com/mysql/mysql-server/commit/c0923d396aef46799883390e9dcf7bbf173e4a03 commit 35f5429eda94f2ce87e453653cd22298d5851cfa Author: Jimmy Yang Date: Wed Oct 6 06:55:34 2010 -0700 Manual port Bug #Bug #54582 "stack overflow when opening many tables linked with foreign keys at once" from mysql-5.1-security to mysql-5.5-security again. rb://391 approved by Heikki No known test case exists for repeating the bug before MariaDB 10.2. The scenario should be that DB_FOREIGN_EXCEED_MAX_CASCADE is returned, then InnoDB wrongly skips the rollback to the start of the current row operation, and finally the SQL layer commits the transaction. Normally the SQL layer would roll back either the entire transaction or to the start of the statement. In the faulty scenario, InnoDB would leave the transaction in an inconsistent state, and the SQL layer could commit the transaction. --- storage/innobase/row/row0mysql.c | 10 ++++++---- storage/xtradb/row/row0mysql.c | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 8804e2c4e03..c16d1b8ea36 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2000, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -572,8 +573,7 @@ handle_new_error: switch (err) { case DB_LOCK_WAIT_TIMEOUT: if (row_rollback_on_timeout) { - trx_general_rollback_for_mysql(trx, NULL); - break; + goto rollback; } /* fall through */ case DB_DUPLICATE_KEY: @@ -586,6 +586,7 @@ handle_new_error: case DB_TOO_MANY_CONCURRENT_TRXS: case DB_OUT_OF_FILE_SPACE: case DB_INTERRUPTED: + rollback_to_savept: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -609,6 +610,7 @@ handle_new_error: case DB_DEADLOCK: case DB_LOCK_TABLE_FULL: + rollback: /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ @@ -638,14 +640,14 @@ handle_new_error: "InnoDB: you dump the tables, look at\n" "InnoDB: " REFMAN "forcing-innodb-recovery.html" " for help.\n", stderr); - break; + goto rollback_to_savept; case DB_FOREIGN_EXCEED_MAX_CASCADE: fprintf(stderr, "InnoDB: Cannot delete/update rows with" " cascading foreign key constraints that exceed max" " depth of %lu\n" "Please drop excessive foreign constraints" " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD); - break; + goto rollback_to_savept; default: fprintf(stderr, "InnoDB: unknown error code %lu\n", (ulong) err); diff --git a/storage/xtradb/row/row0mysql.c b/storage/xtradb/row/row0mysql.c index 5b01adf7c82..a17a8d59b83 100644 --- a/storage/xtradb/row/row0mysql.c +++ b/storage/xtradb/row/row0mysql.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2000, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -574,8 +575,7 @@ handle_new_error: switch (err) { case DB_LOCK_WAIT_TIMEOUT: if (row_rollback_on_timeout) { - trx_general_rollback_for_mysql(trx, NULL); - break; + goto rollback; } /* fall through */ case DB_DUPLICATE_KEY: @@ -588,6 +588,7 @@ handle_new_error: case DB_TOO_MANY_CONCURRENT_TRXS: case DB_OUT_OF_FILE_SPACE: case DB_INTERRUPTED: + rollback_to_savept: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -611,6 +612,7 @@ handle_new_error: case DB_DEADLOCK: case DB_LOCK_TABLE_FULL: + rollback: /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ @@ -640,14 +642,14 @@ handle_new_error: "InnoDB: you dump the tables, look at\n" "InnoDB: " REFMAN "forcing-innodb-recovery.html" " for help.\n", stderr); - break; + goto rollback_to_savept; case DB_FOREIGN_EXCEED_MAX_CASCADE: fprintf(stderr, "InnoDB: Cannot delete/update rows with" " cascading foreign key constraints that exceed max" " depth of %lu\n" "Please drop excessive foreign constraints" " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD); - break; + goto rollback_to_savept; default: fprintf(stderr, "InnoDB: unknown error code %lu\n", (ulong) err); From 1a5028f43081f9877743a465317ad3e33bd9ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Mar 2019 14:31:54 +0200 Subject: [PATCH 10/18] Fix the WITH_ASAN clang build of dynamic plugins --- cmake/plugin.cmake | 2 +- mysql-test/suite/innodb/r/alter_crash.result | 149 ++++++++++++ mysql-test/suite/innodb/t/alter_crash.test | 228 +++++++++++++++++++ 3 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/innodb/r/alter_crash.result create mode 100644 mysql-test/suite/innodb/t/alter_crash.test diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 9c9c7082a43..cfd9677333e 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -209,7 +209,7 @@ MACRO(MYSQL_ADD_PLUGIN) ELSEIF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") TARGET_LINK_LIBRARIES (${target} mysqld) ENDIF() - ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT WITH_ASAN) TARGET_LINK_LIBRARIES (${target} "-Wl,--no-undefined") ENDIF() diff --git a/mysql-test/suite/innodb/r/alter_crash.result b/mysql-test/suite/innodb/r/alter_crash.result new file mode 100644 index 00000000000..8de02cc5fbd --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_crash.result @@ -0,0 +1,149 @@ +# +# Bug#20015132 ALTER TABLE FAILS TO CHECK IF TABLE IS CORRUPTED +# +CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; +SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); +ERROR HY000: Too many active concurrent transactions +SET DEBUG_DBUG=@saved_debug_dbug; +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); +SET DEBUG_DBUG='+d,dict_set_index_corrupted'; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Warning InnoDB: Index c2 is marked as corrupted +test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check error Corrupt +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Warning InnoDB: Index c2 is marked as corrupted +test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check error Corrupt +ALTER TABLE t1 DROP INDEX c2; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check error Corrupt +ALTER TABLE t1 ADD INDEX (c2,c3); +ERROR HY000: Index c3 is corrupted +ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL; +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +ALTER TABLE t1 ADD INDEX (c2,c3); +DROP TABLE t1; +# +# Bug #14669848 CRASH DURING ALTER MAKES ORIGINAL TABLE INACCESSIBLE +# +# -- Scenario 1: +# Crash the server in ha_innobase::commit_inplace_alter_table() +# just after committing the dictionary changes. +CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; +INSERT INTO t1 VALUES (1,2),(3,4); +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; +ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); +ERROR HY000: Lost connection to MySQL server during query +# Restart mysqld after the crash and reconnect. +# Manual *.frm recovery begin. +# Manual recovery end +FLUSH TABLES; +# Drop the orphaned original table. +# Files in datadir after manual recovery. +t1.frm +t1.ibd +SHOW TABLES; +Tables_in_test +t1 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f1` int(11) NOT NULL, + `f2` int(11) NOT NULL, + PRIMARY KEY (`f2`,`f1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +INSERT INTO t1 VALUES (5,6),(7,8); +SELECT * FROM t1; +f1 f2 +1 2 +3 4 +5 6 +7 8 +DROP TABLE t1; +CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; +ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); +DROP TABLE t1; +# -- Scenario 2: +# Crash the server in ha_innobase::commit_inplace_alter_table() +# just before committing the dictionary changes, but after +# writing the MLOG_FILE_RENAME records. As the mini-transaction +# is not committed, the renames will not be replayed. +CREATE TABLE t2 (f1 int not null, f2 int not null) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,2),(3,4); +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_before_commit'; +ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); +ERROR HY000: Lost connection to MySQL server during query +# Startup the server after the crash +# Read and remember the temporary table name +# Manual *.frm recovery begin. The dictionary was not updated +# and the files were not renamed. The rebuilt table +# was left behind on purpose, to faciliate data recovery. +# Manual recovery end +# Drop the orphaned rebuilt table. +SHOW TABLES; +Tables_in_test +t2 +INSERT INTO t2 VALUES (5,6),(7,8); +SELECT * from t2; +f1 f2 +1 2 +3 4 +5 6 +7 8 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `f1` int(11) NOT NULL, + `f2` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +DROP TABLE t2; +CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; +ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); +DROP TABLE t2; +# ------------------------- +# End of Testing Scenario 2 +# ------------------------- +# +# Bug#19330255 WL#7142 - CRASH DURING ALTER TABLE LEADS TO +# DATA DICTIONARY INCONSISTENCY +# +CREATE TABLE t1(a int PRIMARY KEY, b varchar(255), c int NOT NULL) +ENGINE=InnoDB; +INSERT INTO t1 SET a=1,c=2; +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; +ALTER TABLE t1 ADD INDEX (b), CHANGE c d int, ALGORITHM=INPLACE; +ERROR HY000: Lost connection to MySQL server during query +# Restart mysqld after the crash and reconnect. +# Manual *.frm recovery begin. +# Manual recovery end +FLUSH TABLES; +# Drop the orphaned original table. +# Files in datadir after manual recovery. +t1.frm +t1.ibd +SHOW TABLES; +Tables_in_test +t1 +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` varchar(255) DEFAULT NULL, + `d` int(11) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +UPDATE t1 SET d=NULL; +SELECT * FROM t1; +a b d +1 NULL NULL +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test new file mode 100644 index 00000000000..54cc51aecf4 --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -0,0 +1,228 @@ +# Crash-safe InnoDB ALTER operations + +--source include/not_valgrind.inc +--source include/not_embedded.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/not_crashrep.inc + +--disable_query_log +call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log'); +call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47'); +call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`'); +call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); +--enable_query_log + +--echo # +--echo # Bug#20015132 ALTER TABLE FAILS TO CHECK IF TABLE IS CORRUPTED +--echo # + +CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; +SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; +--error ER_TOO_MANY_CONCURRENT_TRXS +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); + +SET DEBUG_DBUG=@saved_debug_dbug; +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); +# Flag the secondary indexes corrupted. +SET DEBUG_DBUG='+d,dict_set_index_corrupted'; +CHECK TABLE t1; + +# Ensure that the corruption is permanent. +--source include/restart_mysqld.inc +CHECK TABLE t1; +ALTER TABLE t1 DROP INDEX c2; +CHECK TABLE t1; +# We refuse an ALTER TABLE that would modify the InnoDB data dictionary +# while leaving some of the table corrupted. +--error ER_INDEX_CORRUPT +ALTER TABLE t1 ADD INDEX (c2,c3); +# This will rebuild the table, uncorrupting all secondary indexes. +ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL; +CHECK TABLE t1; +ALTER TABLE t1 ADD INDEX (c2,c3); +DROP TABLE t1; + +let $MYSQLD_DATADIR= `select @@datadir`; +let datadir= `select @@datadir`; + +# These are from include/shutdown_mysqld.inc and allow to call start_mysqld.inc +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect + +--echo # +--echo # Bug #14669848 CRASH DURING ALTER MAKES ORIGINAL TABLE INACCESSIBLE +--echo # +--echo # -- Scenario 1: +--echo # Crash the server in ha_innobase::commit_inplace_alter_table() +--echo # just after committing the dictionary changes. + +CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=innodb; +INSERT INTO t1 VALUES (1,2),(3,4); +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; + +let $orig_table_id = `SELECT table_id + FROM information_schema.innodb_sys_tables + WHERE name = 'test/t1'`; + +# Write file to make mysql-test-run.pl expect crash +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +--error 2013 +ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); + +--echo # Restart mysqld after the crash and reconnect. +--source include/start_mysqld.inc + +let $temp_table_name = `SELECT SUBSTR(name, 6) + FROM information_schema.innodb_sys_tables + WHERE table_id = $orig_table_id`; + +--echo # Manual *.frm recovery begin. + +--move_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/$temp_table_name.frm + +perl; +my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; +my $t1_frm = "$ENV{'datadir'}/test/t1.frm"; +rename($frm_file[0], $t1_frm); +EOF + +--echo # Manual recovery end + +FLUSH TABLES; + +--echo # Drop the orphaned original table. +--disable_query_log +eval DROP TABLE `#mysql50#$temp_table_name`; +--enable_query_log + +--echo # Files in datadir after manual recovery. +--list_files $MYSQLD_DATADIR/test + +SHOW TABLES; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (5,6),(7,8); +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; +ALTER TABLE t1 ADD PRIMARY KEY (f2, f1); +DROP TABLE t1; + +--echo # -- Scenario 2: +--echo # Crash the server in ha_innobase::commit_inplace_alter_table() +--echo # just before committing the dictionary changes, but after +--echo # writing the MLOG_FILE_RENAME records. As the mini-transaction +--echo # is not committed, the renames will not be replayed. + +CREATE TABLE t2 (f1 int not null, f2 int not null) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,2),(3,4); +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_before_commit'; + +let $orig_table_id = `SELECT table_id + FROM information_schema.innodb_sys_tables + WHERE name = 'test/t2'`; + +# Write file to make mysql-test-run.pl expect crash +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +--error 2013 +ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); + +--echo # Startup the server after the crash +--source include/start_mysqld.inc + +--echo # Read and remember the temporary table name +let $temp_table_name = `SELECT SUBSTRING(name,6) + FROM information_schema.innodb_sys_tables + WHERE name LIKE "test/#sql-ib$orig_table_id%"`; +# This second copy is an environment variable for the perl script below. +let temp_table_name = $temp_table_name; + +--echo # Manual *.frm recovery begin. The dictionary was not updated +--echo # and the files were not renamed. The rebuilt table +--echo # was left behind on purpose, to faciliate data recovery. + +perl; +my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; +my $target_frm = "$ENV{'datadir'}/test/$ENV{'temp_table_name'}.frm"; +rename($frm_file[0], $target_frm); +EOF + +--echo # Manual recovery end + +--echo # Drop the orphaned rebuilt table. +--disable_query_log +eval DROP TABLE `#mysql50#$temp_table_name`; +--enable_query_log + +SHOW TABLES; +INSERT INTO t2 VALUES (5,6),(7,8); +SELECT * from t2; +SHOW CREATE TABLE t2; +DROP TABLE t2; + +CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL) ENGINE=InnoDB; +ALTER TABLE t2 ADD PRIMARY KEY (f2, f1); +DROP TABLE t2; +--list_files $MYSQLD_DATADIR/test + +--echo # ------------------------- +--echo # End of Testing Scenario 2 +--echo # ------------------------- + +--echo # +--echo # Bug#19330255 WL#7142 - CRASH DURING ALTER TABLE LEADS TO +--echo # DATA DICTIONARY INCONSISTENCY +--echo # + +CREATE TABLE t1(a int PRIMARY KEY, b varchar(255), c int NOT NULL) +ENGINE=InnoDB; +INSERT INTO t1 SET a=1,c=2; +SET DEBUG_DBUG='+d,innodb_alter_commit_crash_after_commit'; + +let $orig_table_id = `select table_id from + information_schema.innodb_sys_tables where name = 'test/t1'`; + +# FIXME: MDEV-9469 'Incorrect key file' on ALTER TABLE +# Write file to make mysql-test-run.pl expect crash +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +# +--error 2013 +ALTER TABLE t1 ADD INDEX (b), CHANGE c d int, ALGORITHM=INPLACE; + +--echo # Restart mysqld after the crash and reconnect. +--source include/start_mysqld.inc + +let $temp_table_name = `SELECT SUBSTR(name, 6) + FROM information_schema.innodb_sys_tables + WHERE table_id = $orig_table_id`; + +--echo # Manual *.frm recovery begin. +--move_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/$temp_table_name.frm + +perl; +my @frm_file = glob "$ENV{'datadir'}/test/#sql-*.frm"; +my $t1_frm = "$ENV{'datadir'}/test/t1.frm"; +rename($frm_file[0], $t1_frm); +EOF + +--echo # Manual recovery end + +FLUSH TABLES; + +--echo # Drop the orphaned original table. +--disable_query_log +eval DROP TABLE `#mysql50#$temp_table_name`; +--enable_query_log + +--echo # Files in datadir after manual recovery. +--list_files $MYSQLD_DATADIR/test + +SHOW TABLES; +SHOW CREATE TABLE t1; +UPDATE t1 SET d=NULL; +SELECT * FROM t1; +DROP TABLE t1; From e3adf96aeb39ad43143ca2a9c7a2f23d06e8e26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Mar 2019 15:35:55 +0200 Subject: [PATCH 11/18] MDEV-13818 CREATE INDEX leaks memory if running out of undo log space row_merge_create_index_graph(): Relay the internal state from dict_create_index_step(). Our caller should free the index only if it was not copied, added to the cache, and freed. row_merge_create_index(): Free the index template if it was not added to the cache. This is a safer variant of the logic that was introduced in 65070beffd2e9279145b48d1f27c517b6588e543 in 10.2. prepare_inplace_alter_table_dict(): Add additional fault injection to exercise a code path where we have already added an index to the cache. --- mysql-test/suite/innodb/r/alter_crash.result | 15 +++++++++------ mysql-test/suite/innodb/t/alter_crash.test | 9 +++++---- storage/innobase/handler/handler0alter.cc | 12 ++++++++++-- storage/innobase/include/ut0dbg.h | 3 ++- storage/innobase/row/row0merge.cc | 17 ++++++++++------- storage/xtradb/handler/handler0alter.cc | 12 ++++++++++-- storage/xtradb/include/ut0dbg.h | 3 ++- storage/xtradb/row/row0merge.cc | 17 ++++++++++------- 8 files changed, 58 insertions(+), 30 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_crash.result b/mysql-test/suite/innodb/r/alter_crash.result index 8de02cc5fbd..4b49a788cd4 100644 --- a/mysql-test/suite/innodb/r/alter_crash.result +++ b/mysql-test/suite/innodb/r/alter_crash.result @@ -3,6 +3,9 @@ # CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,create_index_metadata_fail'; +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); +ERROR HY000: The table 't1' is full SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); ERROR HY000: Too many active concurrent transactions @@ -11,21 +14,21 @@ ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); SET DEBUG_DBUG='+d,dict_set_index_corrupted'; CHECK TABLE t1; Table Op Msg_type Msg_text -test.t1 check Warning InnoDB: Index c2 is marked as corrupted -test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check Warning InnoDB: Index "c2" is marked as corrupted +test.t1 check Warning InnoDB: Index "c3" is marked as corrupted test.t1 check error Corrupt CHECK TABLE t1; Table Op Msg_type Msg_text -test.t1 check Warning InnoDB: Index c2 is marked as corrupted -test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check Warning InnoDB: Index "c2" is marked as corrupted +test.t1 check Warning InnoDB: Index "c3" is marked as corrupted test.t1 check error Corrupt ALTER TABLE t1 DROP INDEX c2; CHECK TABLE t1; Table Op Msg_type Msg_text -test.t1 check Warning InnoDB: Index c3 is marked as corrupted +test.t1 check Warning InnoDB: Index "c3" is marked as corrupted test.t1 check error Corrupt ALTER TABLE t1 ADD INDEX (c2,c3); -ERROR HY000: Index c3 is corrupted +ERROR HY000: Index "c3" is corrupted ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL; CHECK TABLE t1; Table Op Msg_type Msg_text diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test index 54cc51aecf4..b417b441723 100644 --- a/mysql-test/suite/innodb/t/alter_crash.test +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -7,10 +7,7 @@ --source include/not_crashrep.inc --disable_query_log -call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log'); -call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47'); -call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`'); -call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); +call mtr.add_suppression('InnoDB: Flagged corruption of c[23]'); --enable_query_log --echo # @@ -19,6 +16,10 @@ call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,create_index_metadata_fail'; +--error ER_RECORD_FILE_FULL +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); + SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; --error ER_TOO_MANY_CONCURRENT_TRXS ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 2bcec2ce51f..b03b70e488e 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -3090,10 +3090,18 @@ prepare_inplace_alter_table_dict( /* Create the indexes in SYS_INDEXES and load into dictionary. */ for (ulint a = 0; a < ctx->num_to_add_index; a++) { - + DBUG_EXECUTE_IF( + "create_index_metadata_fail", + if (a + 1 == ctx->num_to_add_index) { + ctx->trx->error_state = DB_OUT_OF_FILE_SPACE; + ctx->add_index[a] = NULL; + goto index_created; + }); ctx->add_index[a] = row_merge_create_index( ctx->trx, ctx->new_table, &index_defs[a]); - +#ifndef DBUG_OFF +index_created: +#endif add_key_nums[a] = index_defs[a].key_number; if (!ctx->add_index[a]) { diff --git a/storage/innobase/include/ut0dbg.h b/storage/innobase/include/ut0dbg.h index 3f5baef0a3c..b4c941bc163 100644 --- a/storage/innobase/include/ut0dbg.h +++ b/storage/innobase/include/ut0dbg.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -84,7 +85,7 @@ ut_dbg_assertion_failed( /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) ut_a(EXPR) /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */ -#define ut_d(EXPR) do {EXPR;} while (0) +#define ut_d(EXPR) EXPR #else /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 356eec8e7ee..9fa2eaccfa8 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2018, MariaDB Corporation. +Copyright (c) 2014, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3702,7 +3702,7 @@ row_merge_create_index_graph( /*=========================*/ trx_t* trx, /*!< in: trx */ dict_table_t* table, /*!< in: table */ - dict_index_t* index) /*!< in: index */ + dict_index_t*& index) /*!< in,out: index */ { ind_node_t* node; /*!< Index creation node */ mem_heap_t* heap; /*!< Memory heap */ @@ -3726,6 +3726,8 @@ row_merge_create_index_graph( err = trx->error_state; + index = node->index; + que_graph_free((que_t*) que_node_get_parent(thr)); return(err); @@ -3767,20 +3769,21 @@ row_merge_create_index( ifield->prefix_len); } + ut_d(const dict_index_t* const index_template = index); /* Add the index to SYS_INDEXES, using the index prototype. */ err = row_merge_create_index_graph(trx, table, index); if (err == DB_SUCCESS) { - - index = dict_table_get_index_on_name(table, index_def->name); - - ut_a(index); - + ut_ad(index != index_template); /* Note the id of the transaction that created this index, we use it to restrict readers from accessing this index, to ensure read consistency. */ ut_ad(index->trx_id == trx->id); } else { + ut_ad(!index || index == index_template); + if (index) { + dict_mem_index_free(index); + } index = NULL; } diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index fa41c963464..b0cfb17fe28 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -3097,10 +3097,18 @@ prepare_inplace_alter_table_dict( /* Create the indexes in SYS_INDEXES and load into dictionary. */ for (ulint a = 0; a < ctx->num_to_add_index; a++) { - + DBUG_EXECUTE_IF( + "create_index_metadata_fail", + if (a + 1 == ctx->num_to_add_index) { + ctx->trx->error_state = DB_OUT_OF_FILE_SPACE; + ctx->add_index[a] = NULL; + goto index_created; + }); ctx->add_index[a] = row_merge_create_index( ctx->trx, ctx->new_table, &index_defs[a]); - +#ifndef DBUG_OFF +index_created: +#endif add_key_nums[a] = index_defs[a].key_number; if (!ctx->add_index[a]) { diff --git a/storage/xtradb/include/ut0dbg.h b/storage/xtradb/include/ut0dbg.h index 3f5baef0a3c..b4c941bc163 100644 --- a/storage/xtradb/include/ut0dbg.h +++ b/storage/xtradb/include/ut0dbg.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -84,7 +85,7 @@ ut_dbg_assertion_failed( /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) ut_a(EXPR) /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */ -#define ut_d(EXPR) do {EXPR;} while (0) +#define ut_d(EXPR) EXPR #else /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) diff --git a/storage/xtradb/row/row0merge.cc b/storage/xtradb/row/row0merge.cc index e4440640431..02d272f095a 100644 --- a/storage/xtradb/row/row0merge.cc +++ b/storage/xtradb/row/row0merge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2018, MariaDB Corporation. +Copyright (c) 2014, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3705,7 +3705,7 @@ row_merge_create_index_graph( /*=========================*/ trx_t* trx, /*!< in: trx */ dict_table_t* table, /*!< in: table */ - dict_index_t* index) /*!< in: index */ + dict_index_t*& index) /*!< in,out: index */ { ind_node_t* node; /*!< Index creation node */ mem_heap_t* heap; /*!< Memory heap */ @@ -3729,6 +3729,8 @@ row_merge_create_index_graph( err = trx->error_state; + index = node->index; + que_graph_free((que_t*) que_node_get_parent(thr)); return(err); @@ -3770,20 +3772,21 @@ row_merge_create_index( ifield->prefix_len); } + ut_d(const dict_index_t* const index_template = index); /* Add the index to SYS_INDEXES, using the index prototype. */ err = row_merge_create_index_graph(trx, table, index); if (err == DB_SUCCESS) { - - index = dict_table_get_index_on_name(table, index_def->name); - - ut_a(index); - + ut_ad(index != index_template); /* Note the id of the transaction that created this index, we use it to restrict readers from accessing this index, to ensure read consistency. */ ut_ad(index->trx_id == trx->id); } else { + ut_ad(!index || index == index_template); + if (index) { + dict_mem_index_free(index); + } index = NULL; } From 2bd204b965efbd371674d7c718b9c106c04ba933 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 6 Mar 2019 11:41:22 +0100 Subject: [PATCH 12/18] fix memory leaks in mysql_client_test This fixes main.mysql_client_test, main.mysql_client_test_comp, main.mysql_client_test_nonblock failures in ASAN_OPTIONS="abort_on_error=1" runs --- tests/mysql_client_test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 08fd003a919..6c5efd72c40 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -20166,6 +20166,7 @@ static void test_proxy_header_tcp(const char *ipaddr, int port) printf("%.*s %.*s\n", (int)addrlen, row[0], (int)addrlen, normalized_addr); DIE_UNLESS(strncmp(row[0], normalized_addr, addrlen) == 0); DIE_UNLESS(atoi(row[0] + addrlen+1) == port); + mysql_free_result(result); mysql_close(m); } sprintf(query,"DROP USER 'u'@'%s'",normalized_addr); @@ -20204,6 +20205,7 @@ static void test_proxy_header_localhost() mytest(result); row = mysql_fetch_row(result); DIE_UNLESS(strcmp(row[0], "localhost") == 0); + mysql_free_result(result); mysql_close(m); rc = mysql_query(mysql, "DROP USER 'u'@'localhost'"); myquery(rc); @@ -20301,6 +20303,7 @@ static void test_bulk_autoinc() { DIE_IF(atoi(row[0]) != id[i++]); } + mysql_free_result(result); rc= mysql_query(mysql, "DROP TABLE ai_field_value"); myquery(rc); } @@ -20416,6 +20419,7 @@ static void test_explain_meta() mct_close_log(); DIE("num_fields != 1"); } + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "EXPLAIN SELECT 1"); @@ -20432,6 +20436,7 @@ static void test_explain_meta() DIE("num_fields != 10"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "EXPLAIN format=json SELECT 1"); @@ -20448,6 +20453,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20465,6 +20471,7 @@ static void test_explain_meta() DIE("num_fields != 13"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "ANALYZE format=json SELECT 1"); @@ -20481,6 +20488,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); rc= mysql_query(mysql, "CREATE TABLE t1 (a int)"); @@ -20500,6 +20508,7 @@ static void test_explain_meta() DIE("num_fields != 10"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "EXPLAIN format=json INSERT INTO t1 values(1)"); @@ -20516,6 +20525,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20533,6 +20543,7 @@ static void test_explain_meta() DIE("num_fields != 13"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "ANALYZE format=json INSERT INTO t1 values(1)"); @@ -20549,6 +20560,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20566,6 +20578,7 @@ static void test_explain_meta() DIE("num_fields != 10"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "EXPLAIN format=json UPDATE t1 set a=2"); @@ -20582,6 +20595,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20599,6 +20613,7 @@ static void test_explain_meta() DIE("num_fields != 13"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "ANALYZE format=json UPDATE t1 set a=2"); @@ -20615,6 +20630,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20632,6 +20648,7 @@ static void test_explain_meta() DIE("num_fields != 10"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "EXPLAIN format=json DELETE FROM t1"); @@ -20648,6 +20665,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); @@ -20665,6 +20683,7 @@ static void test_explain_meta() DIE("num_fields != 13"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); strmov(query, "ANALYZE format=json DELETE FROM t1"); @@ -20681,6 +20700,7 @@ static void test_explain_meta() DIE("num_fields != 1"); } print_metadata(rs_metadata, num_fields); + mysql_free_result(rs_metadata); mysql_stmt_close(stmt); rc= mysql_query(mysql, "DROP TABLE t1"); From d30f17af4969cc1ce34f1925f5ea2bced9c6f7e9 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 5 Mar 2019 20:07:51 +0100 Subject: [PATCH 13/18] MDEV-13818 CREATE INDEX leaks memory if running out of undo log space if create_index_dict() fails, we need to free ctx->add_index[a] too This fixes innodb.alter_crash and innodb.instant_alter_debug failures in ASAN_OPTIONS="abort_on_error=1" runs --- storage/innobase/handler/handler0alter.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 5dcf0423c88..98c94b65c7b 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -5634,18 +5634,19 @@ new_table_failed: ctx->trx->table_id = user_table->id; for (ulint a = 0; a < ctx->num_to_add_index; a++) { - dict_index_t*& index = ctx->add_index[a]; + dict_index_t* index = ctx->add_index[a]; const bool has_new_v_col = index->has_new_v_col; index = create_index_dict(ctx->trx, index, add_v); if (!index) { error = ctx->trx->error_state; ut_ad(error != DB_SUCCESS); error_handling_drop_uncached: - while (++a < ctx->num_to_add_index) { + do { dict_mem_index_free(ctx->add_index[a]); - } + } while (++a < ctx->num_to_add_index); goto error_handling; } + ctx->add_index[a]= index; index->parser = index_defs[a].parser; index->has_new_v_col = has_new_v_col; From b4cda8bbbc23dbf8a7d59d06de390ea35695efe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Mar 2019 18:54:32 +0200 Subject: [PATCH 14/18] After-merge fix for GCC GCC does not like MY_ATTRIBUTE((nonnull)) on a reference-to-pointer parameter. clang did not flag an issue wit that. --- storage/innobase/row/row0merge.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 8eb720abb40..4f05899c6a1 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -4304,7 +4304,7 @@ row_merge_rename_tables_dict( @param[in,out] index index @param[in] add_v new virtual columns added along with add index call @return DB_SUCCESS or error code */ -MY_ATTRIBUTE((nonnull(1,2,3), warn_unused_result)) +MY_ATTRIBUTE((nonnull(1,2), warn_unused_result)) static dberr_t row_merge_create_index_graph( From f855ec24d7bd710f64f88f113144afde8d6595d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 7 Mar 2019 23:50:35 +0200 Subject: [PATCH 15/18] MDEV-18272: Add the test case The test case was accidentally omitted from the merge commit 913e33e423b434712a9d708cd0f0244d2f03cb0b. --- mysql-test/suite/innodb/r/foreign_key.result | 77 ++++++++++++++++++-- mysql-test/suite/innodb/t/foreign_key.test | 51 +++++++++++-- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result index cdc31d1188c..e2306d5a28f 100644 --- a/mysql-test/suite/innodb/r/foreign_key.result +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -137,6 +137,8 @@ SELECT unique_constraint_name FROM information_schema.referential_constraints WHERE table_name = 't2'; unique_constraint_name PRIMARY +SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; +SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; SELECT unique_constraint_name FROM information_schema.referential_constraints WHERE table_name = 't2'; unique_constraint_name @@ -194,17 +196,19 @@ DROP DATABASE best; # # MDEV-17541 KILL QUERY during lock wait in FOREIGN KEY check hangs # -connect fk, localhost, root,,; +connect con1, localhost, root,,; INSERT INTO t1 SET a=1; BEGIN; DELETE FROM t1; connection default; INSERT INTO t3 SET a=1; -connection fk; +connection con1; kill query @id; connection default; ERROR 70100: Query execution was interrupted -disconnect fk; +connection con1; +ROLLBACK; +connection default; DROP TABLE t3,t1; # # MDEV-18222 InnoDB: Failing assertion: heap->magic_n == MEM_BLOCK_MAGIC_N @@ -299,7 +303,7 @@ INSERT INTO matchmaking_group_users VALUES (10,1),(11,2); INSERT INTO matchmaking_group_maps VALUES (10,55),(11,66); BEGIN; UPDATE users SET name = 'qux' WHERE id = 1; -connect con1,localhost,root,,; +connection con1; SET innodb_lock_wait_timeout= 1; DELETE FROM matchmaking_groups WHERE id = 10; connection default; @@ -444,6 +448,69 @@ connection con1; kill query @id; connection default; ERROR 70100: Query execution was interrupted -disconnect con1; +connection con1; +ROLLBACK; +connection default; DROP TABLE t2,t1; +# +# MDEV-18272 InnoDB index corruption after failed DELETE CASCADE +# +CREATE TABLE t1 ( +pk TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, +a TINYINT UNSIGNED NOT NULL, b TINYINT UNSIGNED NOT NULL, KEY(b), +CONSTRAINT FOREIGN KEY (a) REFERENCES t1 (b) ON DELETE CASCADE +) ENGINE=InnoDB; +INSERT INTO t1 (a,b) VALUES +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,1),(0,1),(1,0); +connection con1; +START TRANSACTION WITH CONSISTENT SNAPSHOT; +connection default; +DELETE IGNORE FROM t1 WHERE b = 1; +Warnings: +Warning 152 InnoDB: Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 20. Please drop extra constraints and try again +Warning 1296 Got error 193 '`test`.`t1`, CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t1` (`b`) ON DELETE CASCADE' from InnoDB +Warning 152 InnoDB: Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 20. Please drop extra constraints and try again +Warning 1296 Got error 193 '`test`.`t1`, CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t1` (`b`) ON DELETE CASCADE' from InnoDB +SELECT a FROM t1 FORCE INDEX(a); +a +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +1 +SELECT * FROM t1; +pk a b +1 0 0 +2 0 0 +3 0 0 +4 0 0 +5 0 0 +6 0 0 +7 0 0 +8 0 0 +9 0 0 +10 0 0 +11 0 0 +12 0 0 +13 0 1 +14 0 1 +15 1 0 +disconnect con1; +InnoDB 0 transactions not purged +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; # End of 10.2 tests diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test index 4330f807d69..a4e045d4d5e 100644 --- a/mysql-test/suite/innodb/t/foreign_key.test +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -103,6 +103,9 @@ WHERE table_name = 't2'; --source include/restart_mysqld.inc +SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency; +SET GLOBAL innodb_purge_rseg_truncate_frequency = 1; + SELECT unique_constraint_name FROM information_schema.referential_constraints WHERE table_name = 't2'; @@ -148,6 +151,7 @@ SET FOREIGN_KEY_CHECKS=1; call mtr.add_suppression("InnoDB: Possible reasons:"); call mtr.add_suppression("InnoDB: \\([12]\\) Table "); call mtr.add_suppression("InnoDB: If table `test`\\.`t2` is a temporary table"); +call mtr.add_suppression("InnoDB: Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 15\\."); --enable_query_log CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; @@ -165,7 +169,7 @@ DROP DATABASE best; --echo # --echo # MDEV-17541 KILL QUERY during lock wait in FOREIGN KEY check hangs --echo # -connect (fk, localhost, root,,); +connect (con1, localhost, root,,); INSERT INTO t1 SET a=1; BEGIN; DELETE FROM t1; @@ -174,7 +178,7 @@ connection default; let $ID= `SELECT @id := CONNECTION_ID()`; send INSERT INTO t3 SET a=1; -connection fk; +connection con1; # Check that the above SELECT is blocked let $wait_condition= select count(*) = 1 from information_schema.processlist @@ -186,7 +190,10 @@ kill query @id; connection default; --error ER_QUERY_INTERRUPTED reap; -disconnect fk; + +connection con1; +ROLLBACK; +connection default; DROP TABLE t3,t1; @@ -286,7 +293,7 @@ INSERT INTO matchmaking_group_maps VALUES (10,55),(11,66); BEGIN; UPDATE users SET name = 'qux' WHERE id = 1; ---connect (con1,localhost,root,,) +--connection con1 SET innodb_lock_wait_timeout= 1; DELETE FROM matchmaking_groups WHERE id = 10; @@ -442,10 +449,44 @@ kill query @id; connection default; --error ER_QUERY_INTERRUPTED reap; -disconnect con1; + +connection con1; +ROLLBACK; +connection default; DROP TABLE t2,t1; +--echo # +--echo # MDEV-18272 InnoDB index corruption after failed DELETE CASCADE +--echo # +CREATE TABLE t1 ( + pk TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + a TINYINT UNSIGNED NOT NULL, b TINYINT UNSIGNED NOT NULL, KEY(b), + CONSTRAINT FOREIGN KEY (a) REFERENCES t1 (b) ON DELETE CASCADE +) ENGINE=InnoDB; + +INSERT INTO t1 (a,b) VALUES +(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0),(0,0), +(0,1),(0,1),(1,0); +connection con1; +START TRANSACTION WITH CONSISTENT SNAPSHOT; + +connection default; +DELETE IGNORE FROM t1 WHERE b = 1; + +SELECT a FROM t1 FORCE INDEX(a); +# This would wrongly return the empty result if +# the "goto rollback_to_savept" in row_mysql_handle_errors() is reverted. +SELECT * FROM t1; +# Allow purge to continue by closing the read view. +disconnect con1; + +# Wait for purge. With the fix reverted, the server would crash here. +--source include/wait_all_purged.inc +CHECK TABLE t1; +DROP TABLE t1; +SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; + --echo # End of 10.2 tests --source include/wait_until_count_sessions.inc From bf71d263621c90cbddc7bde9bf071dae503f333f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Mar 2019 00:16:40 +0200 Subject: [PATCH 16/18] MDEV-13818 after-merge fix: Extend the test case The 10.1 changes to the test case were accidentally omitted in the merge commit 913e33e423b434712a9d708cd0f0244d2f03cb0b. --- mysql-test/suite/innodb/r/alter_crash.result | 3 +++ mysql-test/suite/innodb/t/alter_crash.test | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/innodb/r/alter_crash.result b/mysql-test/suite/innodb/r/alter_crash.result index 3b6a5d1b39a..c19ec2a1c1a 100644 --- a/mysql-test/suite/innodb/r/alter_crash.result +++ b/mysql-test/suite/innodb/r/alter_crash.result @@ -3,6 +3,9 @@ # CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,create_index_metadata_fail'; +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); +ERROR HY000: The table 't1' is full SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); ERROR HY000: Too many active concurrent transactions diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test index 101e0fa44c2..c7cd1bcfe66 100644 --- a/mysql-test/suite/innodb/t/alter_crash.test +++ b/mysql-test/suite/innodb/t/alter_crash.test @@ -7,8 +7,6 @@ --source include/not_crashrep.inc --disable_query_log -call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log'); -call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47'); call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`'); call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); --enable_query_log @@ -19,6 +17,10 @@ call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted'); CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB; SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG='+d,create_index_metadata_fail'; +--error ER_RECORD_FILE_FULL +ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); + SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx'; --error ER_TOO_MANY_CONCURRENT_TRXS ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3); From 136d21c82f835dd85a53057c67d4476f5b971a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Mar 2019 09:13:48 +0200 Subject: [PATCH 17/18] MDEV-13818: Revert an incorrect change In commit d30f17af4969cc1ce34f1925f5ea2bced9c6f7e9 the change of the loop iteration broke another error handling path that did "goto error_handling_drop_uncached". Cover this code path with fault injection, and revert to the correct iteration. There are two fault injection labels innodb_OOM_prepare_inplace_alter. Their order was swapped in MDEV-11369, so that the label that used to be covered in an ADD INDEX code path would become unreachable because the label that is executed for any ALTER TABLE was executed first. Let us introduce the label innodb_OOM_prepare_add_index for the more specific case. --- .../suite/innodb/r/innodb-index-online.result | 4 ++++ .../suite/innodb/t/innodb-index-online.test | 4 ++++ storage/innobase/handler/handler0alter.cc | 18 ++++++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb-index-online.result b/mysql-test/suite/innodb/r/innodb-index-online.result index cc900ff0201..a0227d31393 100644 --- a/mysql-test/suite/innodb/r/innodb-index-online.result +++ b/mysql-test/suite/innodb/r/innodb-index-online.result @@ -43,6 +43,10 @@ SET DEBUG_DBUG = '+d,innodb_OOM_inplace_alter'; CREATE UNIQUE INDEX c2 ON t1(c2); ERROR HY000: Out of memory. SET DEBUG_DBUG = @saved_debug_dbug; +SET DEBUG_DBUG = '+d,innodb_OOM_prepare_add_index'; +ALTER TABLE t1 ADD KEY(c2), ADD KEY c3_10(c3(10)), ADD KEY c3_c2(c3(4),c2); +ERROR HY000: Out of memory. +SET DEBUG_DBUG = @saved_debug_dbug; CREATE UNIQUE INDEX c2 ON t1(c2); DROP INDEX c2 ON t1; connection default; diff --git a/mysql-test/suite/innodb/t/innodb-index-online.test b/mysql-test/suite/innodb/t/innodb-index-online.test index 5d92b010e60..4cdbdb7c584 100644 --- a/mysql-test/suite/innodb/t/innodb-index-online.test +++ b/mysql-test/suite/innodb/t/innodb-index-online.test @@ -53,6 +53,10 @@ SET DEBUG_DBUG = '+d,innodb_OOM_inplace_alter'; --error ER_OUT_OF_RESOURCES CREATE UNIQUE INDEX c2 ON t1(c2); SET DEBUG_DBUG = @saved_debug_dbug; +SET DEBUG_DBUG = '+d,innodb_OOM_prepare_add_index'; +--error ER_OUT_OF_RESOURCES +ALTER TABLE t1 ADD KEY(c2), ADD KEY c3_10(c3(10)), ADD KEY c3_c2(c3(4),c2); +SET DEBUG_DBUG = @saved_debug_dbug; CREATE UNIQUE INDEX c2 ON t1(c2); DROP INDEX c2 ON t1; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 84f0857dcb7..ae634c791b1 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -5659,11 +5659,9 @@ index_created: if (index) { dict_mem_index_free(index); } - a++; error_handling_drop_uncached: - while (a < ctx->num_to_add_index) { - dict_mem_index_free( - ctx->add_index[a++]); + while (++a < ctx->num_to_add_index) { + dict_mem_index_free(ctx->add_index[a]); } goto error_handling; } else { @@ -5693,10 +5691,6 @@ error_handling_drop_uncached: /* No need to allocate a modification log. */ DBUG_ASSERT(!index->online_log); } else { - DBUG_EXECUTE_IF( - "innodb_OOM_prepare_inplace_alter", - error = DB_OUT_OF_MEMORY; - goto error_handling_drop_uncached;); rw_lock_x_lock(&ctx->add_index[a]->lock); bool ok = row_log_allocate( @@ -5708,6 +5702,14 @@ error_handling_drop_uncached: rw_lock_x_unlock(&index->lock); + DBUG_EXECUTE_IF( + "innodb_OOM_prepare_add_index", + if (ok && a == 1) { + row_log_free( + index->online_log); + ok = false; + }); + if (!ok) { error = DB_OUT_OF_MEMORY; goto error_handling_drop_uncached; From 94eb56fb29334a1075103becb625685b23f7d437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 8 Mar 2019 10:21:58 +0200 Subject: [PATCH 18/18] Give ASAN some more stack When compiling CMAKE_BUILD_TYPE=Debug WITH_ASAN using clang-7 -O2 the following tests could fail due to insufficient stack size: main.signal_demo3 sys_vars.max_sp_recursion_depth_func --- include/my_pthread.h | 6 +++--- mysql-test/main/mysqld--help.test | 2 +- mysql-test/suite/sys_vars/inc/sysvars_server.inc | 2 +- mysql-test/suite/sys_vars/t/thread_stack_basic.test | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index 302d4e95cea..264125a8fe3 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2009, 2017, MariaDB Corporation. + Copyright (c) 2009, 2019, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -692,9 +692,9 @@ extern void my_mutex_end(void); with the current number of keys and key parts. */ #ifdef __SANITIZE_ADDRESS__ -#define DEFAULT_THREAD_STACK (364*1024L) +#define DEFAULT_THREAD_STACK (383*1024L) /* 392192 */ #else -#define DEFAULT_THREAD_STACK (292*1024L) +#define DEFAULT_THREAD_STACK (292*1024L) /* 299008 */ #endif #endif diff --git a/mysql-test/main/mysqld--help.test b/mysql-test/main/mysqld--help.test index 68ee2f087ab..c2b6424599a 100644 --- a/mysql-test/main/mysqld--help.test +++ b/mysql-test/main/mysqld--help.test @@ -57,7 +57,7 @@ perl; # fixes for 32-bit s/\b4294967295\b/18446744073709551615/; s/\b2146435072\b/9223372036853727232/; - s/\b372736\b/299008/; + s/\b392192\b/299008/; s/\b4294963200\b/18446744073709547520/; foreach $var (@env) { s/\Q$ENV{$var}\E/$var/ } next if /use --skip-(use-)?symbolic-links to disable/; # for valgrind, again diff --git a/mysql-test/suite/sys_vars/inc/sysvars_server.inc b/mysql-test/suite/sys_vars/inc/sysvars_server.inc index 1e504837587..af1b0ce4563 100644 --- a/mysql-test/suite/sys_vars/inc/sysvars_server.inc +++ b/mysql-test/suite/sys_vars/inc/sysvars_server.inc @@ -13,7 +13,7 @@ set sql_mode=ansi_quotes; set global div_precision_increment=5; --replace_regex /^\/\S+/PATH/ ---replace_result $MASTER_MYPORT MASTER_MYPORT 372736 299008 +--replace_result $MASTER_MYPORT MASTER_MYPORT 392192 299008 select * from information_schema.system_variables where variable_name not like 'aria%' and variable_name not like 'debug%' and diff --git a/mysql-test/suite/sys_vars/t/thread_stack_basic.test b/mysql-test/suite/sys_vars/t/thread_stack_basic.test index bfd3fb40db3..41015033fe9 100644 --- a/mysql-test/suite/sys_vars/t/thread_stack_basic.test +++ b/mysql-test/suite/sys_vars/t/thread_stack_basic.test @@ -1,17 +1,17 @@ # # only global # ---replace_result 372736 299008 +--replace_result 392192 299008 select @@global.thread_stack; --error ER_INCORRECT_GLOBAL_LOCAL_VAR select @@session.thread_stack; ---replace_result 372736 299008 +--replace_result 392192 299008 show global variables like 'thread_stack'; ---replace_result 372736 299008 +--replace_result 392192 299008 show session variables like 'thread_stack'; ---replace_result 372736 299008 +--replace_result 392192 299008 select * from information_schema.global_variables where variable_name='thread_stack'; ---replace_result 372736 299008 +--replace_result 392192 299008 select * from information_schema.session_variables where variable_name='thread_stack'; #