From 2b749a7bf4f6a6d70f05e8e4b42d088b397adea8 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 15 May 2018 11:46:55 +0300 Subject: [PATCH 1/8] MDEV-654 Assertion `share->now_transactional' failed in flush_log_for_bitmap on concurrent workload with Aria tables Problem was that we the bitmap needs to be flushed before disabling logging of redo entires, as writing the bitmap to disk by background checkpoint may cause redo entries. --- storage/maria/ha_maria.cc | 1 + storage/maria/ma_recovery.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 0c41037f33e..ca2d050ddfe 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -1312,6 +1312,7 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt) old_proc_info= thd_proc_info(thd, "Checking status"); thd_progress_init(thd, 3); error= maria_chk_status(param, file); // Not fatal + /* maria_chk_size() will flush the page cache for this file */ if (maria_chk_size(param, file)) error= 1; if (!error) diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index 75a8f4f4559..af0d9476f2a 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -3521,6 +3521,14 @@ void _ma_tmp_disable_logging_for_table(MARIA_HA *info, { MARIA_SHARE *share= info->s; DBUG_ENTER("_ma_tmp_disable_logging_for_table"); + + /* + We have to ensure that bitmap is flushed, as it's checking + that share->now_transactional is set + */ + if (share->now_transactional && share->data_file_type == BLOCK_RECORD) + _ma_bitmap_flush_all(share); + if (log_incomplete) { uchar log_data[FILEID_STORE_SIZE]; From b050df4fd3ee5f2377a814fd24ac3774d1458f99 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 15 May 2018 12:30:32 +0300 Subject: [PATCH 2/8] MDEV-14943 Alter table ORDER BY bug Problem was that if copy_data_between_tables() didn't do proper clean up in case of failures: - copy object was not properly freed - end_bulk_insert() was not called - mysql_trans_prepare_alter_copy_data() set THD->transaction.on to false which was not properly restored The last part caused a crash in Aria as Aria depends on that THD is correct. Other things: - Reset info->switched_transactional after usage (safety) - Reset bulk_insert_single_undo (safety) --- mysql-test/suite/maria/alter.result | 16 +++++++++++++++ mysql-test/suite/maria/alter.test | 17 +++++++++++++++ sql/sql_table.cc | 32 +++++++++++++++++++++-------- storage/maria/ha_maria.cc | 1 + storage/maria/ma_recovery.c | 3 +++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/maria/alter.result b/mysql-test/suite/maria/alter.result index 1a7daf5a1ee..c63688dddd6 100644 --- a/mysql-test/suite/maria/alter.result +++ b/mysql-test/suite/maria/alter.result @@ -31,3 +31,19 @@ pk i 8 88 9 99 DROP TABLE t1; +CREATE TABLE t1 (f INT) ENGINE=Aria transactional=1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f` int(11) DEFAULT NULL +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 TRANSACTIONAL=1 +INSERT INTO t1 VALUES (1),(2); +ALTER TABLE t1 ORDER BY unknown_column; +ERROR 42S22: Unknown column 'unknown_column' in 'order clause' +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f` int(11) DEFAULT NULL +) ENGINE=Aria DEFAULT CHARSET=latin1 PAGE_CHECKSUM=1 TRANSACTIONAL=1 +CREATE TABLE t2 SELECT * FROM t1; +DROP TABLE t1, t2; diff --git a/mysql-test/suite/maria/alter.test b/mysql-test/suite/maria/alter.test index abca4865688..09672cdfa3b 100644 --- a/mysql-test/suite/maria/alter.test +++ b/mysql-test/suite/maria/alter.test @@ -25,3 +25,20 @@ INSERT INTO t1 VALUES (2,0),(3,33),(4,0),(5,55),(6,66),(7,0),(8,88),(9,99); ALTER TABLE t1 ENABLE KEYS; SELECT * FROM t1 WHERE i = 0 OR pk BETWEEN 6 AND 10; DROP TABLE t1; + +# +# MDEV-14943 +# Assertion `block->type == PAGECACHE_EMPTY_PAGE || block->type == type || +# type == PAGECACHE_LSN_PAGE || type == PAGECACHE_READ_UNKNOWN_PAGE || +# block->type == PAGECACHE_READ_UNKNOWN_PAGE' failed in pagecache_read upon +# CREATE ... SELECT from Aria table +# + +CREATE TABLE t1 (f INT) ENGINE=Aria transactional=1; +SHOW CREATE TABLE t1; +INSERT INTO t1 VALUES (1),(2); +--error ER_BAD_FIELD_ERROR +ALTER TABLE t1 ORDER BY unknown_column; +SHOW CREATE TABLE t1; +CREATE TABLE t2 SELECT * FROM t1; +DROP TABLE t1, t2; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8f3468a44db..27d579a6b19 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9356,9 +9356,7 @@ bool mysql_trans_prepare_alter_copy_data(THD *thd) This needs to be done before external_lock. */ - if (ha_enable_transaction(thd, FALSE)) - DBUG_RETURN(TRUE); - DBUG_RETURN(FALSE); + DBUG_RETURN(ha_enable_transaction(thd, FALSE) != 0); } @@ -9409,6 +9407,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, ha_rows examined_rows; ha_rows found_rows; bool auto_increment_field_copied= 0; + bool cleanup_done= 0; ulonglong save_sql_mode= thd->variables.sql_mode; ulonglong prev_insert_id, time_to_report_progress; Field **dfield_ptr= to->default_field; @@ -9417,15 +9416,23 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, /* Two or 3 stages; Sorting, copying data and update indexes */ thd_progress_init(thd, 2 + MY_TEST(order)); - if (mysql_trans_prepare_alter_copy_data(thd)) - DBUG_RETURN(-1); - if (!(copy= new Copy_field[to->s->fields])) DBUG_RETURN(-1); /* purecov: inspected */ + if (mysql_trans_prepare_alter_copy_data(thd)) + { + delete [] copy; + DBUG_RETURN(-1); + } + /* We need external lock before we can disable/enable keys */ if (to->file->ha_external_lock(thd, F_WRLCK)) + { + /* Undo call to mysql_trans_prepare_alter_copy_data() */ + ha_enable_transaction(thd, TRUE); + delete [] copy; DBUG_RETURN(-1); + } alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); @@ -9435,7 +9442,6 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, from->file->info(HA_STATUS_VARIABLE); to->file->ha_start_bulk_insert(from->file->stats.records, ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT); - List_iterator it(create); Create_field *def; copy_end=copy; @@ -9637,7 +9643,6 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, } end_read_record(&info); free_io_cache(from); - delete [] copy; THD_STAGE_INFO(thd, stage_enabling_keys); thd_progress_next_stage(thd); @@ -9652,6 +9657,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->print_error(my_errno,MYF(0)); error= 1; } + cleanup_done= 1; to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); if (mysql_trans_commit_alter_copy_data(thd)) @@ -9663,6 +9669,16 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, *copied= found_count; *deleted=delete_count; to->file->ha_release_auto_increment(); + delete [] copy; + + if (!cleanup_done) + { + /* This happens if we get an error during initialzation of data */ + DBUG_ASSERT(error); + to->file->ha_end_bulk_insert(); + ha_enable_transaction(thd, TRUE); + } + if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME)) diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index f52336de9d0..80f278ab2eb 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -2224,6 +2224,7 @@ end: _ma_reenable_logging_for_table(file, bulk_insert_single_undo == BULK_INSERT_SINGLE_UNDO_AND_NO_REPAIR); + bulk_insert_single_undo= BULK_INSERT_NONE; // Safety } DBUG_RETURN(err); } diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index 4c3310c68c5..b0c38e9f24d 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -3583,7 +3583,10 @@ my_bool _ma_reenable_logging_for_table(MARIA_HA *info, my_bool flush_pages) if (share->now_transactional == share->base.born_transactional || !info->switched_transactional) + { + info->switched_transactional= FALSE; DBUG_RETURN(0); + } info->switched_transactional= FALSE; if ((share->now_transactional= share->base.born_transactional)) From d703e09cd6706673fbb127f540d3917068b40755 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 21 Sep 2017 16:30:24 +0300 Subject: [PATCH 3/8] Fix that FLUSH TABLES FOR EXPORT also works for Aria tables. - Added missing test case for MyISAM --- mysql-test/r/myisam.result | 8 ++++++++ mysql-test/suite/maria/maria.result | 8 ++++++++ mysql-test/suite/maria/maria.test | 13 +++++++++++++ mysql-test/t/myisam.test | 13 +++++++++++++ storage/maria/ha_maria.cc | 2 +- 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 6b23aefd73b..bedfa413b55 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -2545,6 +2545,14 @@ OPTIMIZE TABLE t1; Table Op Msg_type Msg_text test.t1 optimize status OK DROP TABLE t1; +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)) engine=myisam; +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), +(6,'0'),(7,'0'); +flush tables test.t1 for export; +insert into t1 values (8,'0'); +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +unlock tables; +drop table t1; show variables like 'myisam_block_size'; Variable_name Value myisam_block_size 1024 diff --git a/mysql-test/suite/maria/maria.result b/mysql-test/suite/maria/maria.result index 8078687dad5..f4d7f3d32c6 100644 --- a/mysql-test/suite/maria/maria.result +++ b/mysql-test/suite/maria/maria.result @@ -2771,3 +2771,11 @@ test.t1 check status OK SET aria_repair_threads=@@global.aria_repair_threads; SET aria_sort_buffer_size=@@global.aria_sort_buffer_size; DROP TABLE t1; +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)); +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), +(6,'0'),(7,'0'); +flush tables test.t1 for export; +insert into t1 values (8,'0'); +ERROR HY000: Table 't1' was locked with a READ lock and can't be updated +unlock tables; +drop table t1; diff --git a/mysql-test/suite/maria/maria.test b/mysql-test/suite/maria/maria.test index 27e9a45fda7..17dfb803328 100644 --- a/mysql-test/suite/maria/maria.test +++ b/mysql-test/suite/maria/maria.test @@ -2028,6 +2028,19 @@ SET aria_repair_threads=@@global.aria_repair_threads; SET aria_sort_buffer_size=@@global.aria_sort_buffer_size; DROP TABLE t1; +# +# Check FLUSH FOR EXPORT +# + +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)); +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), + (6,'0'),(7,'0'); +flush tables test.t1 for export; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t1 values (8,'0'); +unlock tables; +drop table t1; + # # End of test # diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 62260ba43aa..796083c0622 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -1762,6 +1762,19 @@ ALTER TABLE t1 DISABLE KEYS; OPTIMIZE TABLE t1; DROP TABLE t1; +# +# Check FLUSH FOR EXPORT +# + +CREATE TABLE t1(a INT, b CHAR(10), KEY(a), KEY(b)) engine=myisam; +INSERT INTO t1 VALUES(1,'0'),(2,'0'),(3,'0'),(4,'0'),(5,'0'), + (6,'0'),(7,'0'); +flush tables test.t1 for export; +--error ER_TABLE_NOT_LOCKED_FOR_WRITE +insert into t1 values (8,'0'); +unlock tables; +drop table t1; + # # Check some variables # diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 80f278ab2eb..748300e48b8 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -980,7 +980,7 @@ int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | HA_FILE_BASED | HA_CAN_GEOMETRY | CANNOT_ROLLBACK_FLAG | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | HA_CAN_REPAIR | - HA_CAN_VIRTUAL_COLUMNS | + HA_CAN_VIRTUAL_COLUMNS | HA_CAN_EXPORT | HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT), can_enable_indexes(1), bulk_insert_single_undo(BULK_INSERT_NONE) {} From aa2e1ade17e656446c534c02e9cb0adab50b46e1 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Wed, 16 May 2018 21:01:26 +0400 Subject: [PATCH 4/8] (almost) sane core handling in mtr Analyze core independently of max-save-datadir and max-save-core setting. Increment $num_saved_cores only if core was actually saved. "Move any core files from e.g. mysqltest" independently of max-save-datadir setting. Note: it may overwrite core from mysqld, which might not be desired (it did work this way even before). --- mysql-test/mysql-test-run.pl | 81 ++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 0f915a8777a..de1871cbf03 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -637,50 +637,59 @@ sub run_test_server ($$$) { my $worker_savename= basename($worker_savedir); my $savedir= "$opt_vardir/log/$worker_savename"; + # Move any core files from e.g. mysqltest + foreach my $coref (glob("core*"), glob("*.dmp")) + { + mtr_report(" - found '$coref', moving it to '$worker_savedir'"); + move($coref, $worker_savedir); + } + + find( + { + no_chdir => 1, + wanted => sub + { + my $core_file= $File::Find::name; + my $core_name= basename($core_file); + + # Name beginning with core, not ending in .gz + if (($core_name =~ /^core/ and $core_name !~ /\.gz$/) + or (IS_WINDOWS and $core_name =~ /\.dmp$/)) + { + # Ending with .dmp + mtr_report(" - found '$core_name'", + "($num_saved_cores/$opt_max_save_core)"); + + My::CoreDump->show($core_file, $exe_mysqld, $opt_parallel); + + # Limit number of core files saved + if ($opt_max_save_core > 0 && + $num_saved_cores >= $opt_max_save_core) + { + mtr_report(" - deleting it, already saved", + "$opt_max_save_core"); + unlink("$core_file"); + } + else + { + mtr_compress_file($core_file) unless @opt_cases; + ++$num_saved_cores; + } + } + } + }, + $worker_savedir); + if ($opt_max_save_datadir > 0 && $num_saved_datadir >= $opt_max_save_datadir) { mtr_report(" - skipping '$worker_savedir/'"); rmtree($worker_savedir); } - else { + else + { mtr_report(" - saving '$worker_savedir/' to '$savedir/'"); rename($worker_savedir, $savedir); - # Move any core files from e.g. mysqltest - foreach my $coref (glob("core*"), glob("*.dmp")) - { - mtr_report(" - found '$coref', moving it to '$savedir'"); - move($coref, $savedir); - } - if ($opt_max_save_core > 0) { - # Limit number of core files saved - find({ no_chdir => 1, - wanted => sub { - my $core_file= $File::Find::name; - my $core_name= basename($core_file); - - # Name beginning with core, not ending in .gz - if (($core_name =~ /^core/ and $core_name !~ /\.gz$/) - or (IS_WINDOWS and $core_name =~ /\.dmp$/)){ - # Ending with .dmp - mtr_report(" - found '$core_name'", - "($num_saved_cores/$opt_max_save_core)"); - - My::CoreDump->show($core_file, $exe_mysqld, $opt_parallel); - - if ($num_saved_cores >= $opt_max_save_core) { - mtr_report(" - deleting it, already saved", - "$opt_max_save_core"); - unlink("$core_file"); - } else { - mtr_compress_file($core_file) unless @opt_cases; - } - ++$num_saved_cores; - } - } - }, - $savedir); - } } resfile_print_test(); $num_saved_datadir++; From ef295c31e3d50a99a90beccfb4deb9d9d36ac1e3 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 16 May 2018 21:51:46 +0300 Subject: [PATCH 5/8] MDEV-11129 CREATE OR REPLACE TABLE t1 AS SELECT spfunc() crashes if spfunc() references t1 Fixed by extending unique_table() with a flag to not allow usage of the replaced table. I also cleaned up find_dup_table() to not use goto next. I also added more comments to the code in find_dup_table() --- mysql-test/r/create_or_replace.result | 20 ++++++++++++ mysql-test/t/create_or_replace.test | 25 +++++++++++++++ sql/sql_base.cc | 46 ++++++++++++++++----------- sql/sql_base.h | 6 +++- sql/sql_delete.cc | 2 +- sql/sql_insert.cc | 3 +- sql/sql_parse.cc | 2 +- 7 files changed, 82 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/create_or_replace.result b/mysql-test/r/create_or_replace.result index a43dc2eaca4..0d171f9f87a 100644 --- a/mysql-test/r/create_or_replace.result +++ b/mysql-test/r/create_or_replace.result @@ -453,3 +453,23 @@ CREATE OR REPLACE TABLE t1 AS SELECT f1(); UNLOCK TABLES; DROP FUNCTION f1; DROP TABLE t1; +# +# MDEV-11129 +# CREATE OR REPLACE TABLE t1 AS SELECT spfunc() crashes if spfunc() +# references t1 +# +CREATE OR REPLACE TABLE t1(a INT); +CREATE FUNCTION f1() RETURNS VARCHAR(16383) +BEGIN +INSERT INTO t1 VALUES(1); +RETURN 'test'; +END; +$$ +CREATE OR REPLACE TABLE t1 AS SELECT f1(); +ERROR HY000: Table 't1' is specified twice, both as a target for 'CREATE' and as a separate source for data +LOCK TABLE t1 WRITE; +CREATE OR REPLACE TABLE t1 AS SELECT f1(); +ERROR HY000: Table 't1' was not locked with LOCK TABLES +UNLOCK TABLES; +DROP FUNCTION f1; +DROP TABLE t1; diff --git a/mysql-test/t/create_or_replace.test b/mysql-test/t/create_or_replace.test index b37417f39d0..5ebb3031be3 100644 --- a/mysql-test/t/create_or_replace.test +++ b/mysql-test/t/create_or_replace.test @@ -398,3 +398,28 @@ CREATE OR REPLACE TABLE t1 AS SELECT f1(); UNLOCK TABLES; DROP FUNCTION f1; DROP TABLE t1; + +--echo # +--echo # MDEV-11129 +--echo # CREATE OR REPLACE TABLE t1 AS SELECT spfunc() crashes if spfunc() +--echo # references t1 +--echo # + +CREATE OR REPLACE TABLE t1(a INT); +DELIMITER $$; +CREATE FUNCTION f1() RETURNS VARCHAR(16383) +BEGIN + INSERT INTO t1 VALUES(1); + RETURN 'test'; +END; +$$ +DELIMITER ;$$ +--error ER_UPDATE_TABLE_USED +CREATE OR REPLACE TABLE t1 AS SELECT f1(); +LOCK TABLE t1 WRITE; +--error ER_TABLE_NOT_LOCKED +CREATE OR REPLACE TABLE t1 AS SELECT f1(); +UNLOCK TABLES; + +DROP FUNCTION f1; +DROP TABLE t1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 554c8cffeaf..bdde123f18c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1278,7 +1278,8 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, @param thd thread handle @param table table which should be checked @param table_list list of tables - @param check_alias whether to check tables' aliases + @param check_flag whether to check tables' aliases + Currently this is only used by INSERT NOTE: to exclude derived tables from check we use following mechanism: a) during derived table processing set THD::derived_tables_processing @@ -1307,9 +1308,9 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table, static TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, - bool check_alias) + uint check_flag) { - TABLE_LIST *res; + TABLE_LIST *res= 0; const char *d_name, *t_name, *t_alias; DBUG_ENTER("find_dup_table"); DBUG_PRINT("enter", ("table alias: %s", table->alias)); @@ -1345,17 +1346,15 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, retry: DBUG_PRINT("info", ("real table: %s.%s", d_name, t_name)); - for (TABLE_LIST *tl= table_list;;) + for (TABLE_LIST *tl= table_list; tl ; tl= tl->next_global, res= 0) { - if (tl && - tl->select_lex && tl->select_lex->master_unit() && + if (tl->select_lex && tl->select_lex->master_unit() && tl->select_lex->master_unit()->executed) { /* There is no sense to check tables of already executed parts of the query */ - tl= tl->next_global; continue; } /* @@ -1364,21 +1363,29 @@ retry: */ if (! (res= find_table_in_global_list(tl, d_name, t_name))) break; + tl= res; // We can continue search after this table /* Skip if same underlying table. */ if (res->table && (res->table == table->table)) - goto next; + continue; + + if (check_flag & CHECK_DUP_FOR_CREATE) + DBUG_RETURN(res); /* Skip if table alias does not match. */ - if (check_alias) + if (check_flag & CHECK_DUP_FOR_CREATE) { if (my_strcasecmp(table_alias_charset, t_alias, res->alias)) - goto next; + continue; } /* - Skip if marked to be excluded (could be a derived table) or if - entry is a prelocking placeholder. + If table is not excluded (could be a derived table) and table is not + a prelocking placeholder then we found either a duplicate entry + or a table that is part of a derived table (handled below). + Examples are: + INSERT INTO t1 SELECT * FROM t1; + INSERT INTO t1 SELECT * FROM view_containing_t1; */ if (res->select_lex && !res->select_lex->exclude_from_table_unique_test && @@ -1390,14 +1397,17 @@ retry: processed in derived table or top select of multi-update/multi-delete (exclude_from_table_unique_test) or prelocking placeholder. */ -next: - tl= res->next_global; DBUG_PRINT("info", ("found same copy of table or table which we should skip")); } if (res && res->belong_to_derived) { - /* Try to fix */ + /* + We come here for queries of type: + INSERT INTO t1 (SELECT tmp.a FROM (select * FROM t1) as tmp); + + Try to fix by materializing the derived table + */ TABLE_LIST *derived= res->belong_to_derived; if (derived->is_merged_derived()) { @@ -1429,7 +1439,7 @@ next: TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, - bool check_alias) + uint check_flag) { TABLE_LIST *dup; @@ -1443,12 +1453,12 @@ unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, for (child= table->next_global; child && child->parent_l == table; child= child->next_global) { - if ((dup= find_dup_table(thd, child, child->next_global, check_alias))) + if ((dup= find_dup_table(thd, child, child->next_global, check_flag))) break; } } else - dup= find_dup_table(thd, table, table_list, check_alias); + dup= find_dup_table(thd, table, table_list, check_flag); return dup; } /* diff --git a/sql/sql_base.h b/sql/sql_base.h index 04f132ac936..af42a15ab29 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -61,6 +61,10 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND, IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE, IGNORE_EXCEPT_NON_UNIQUE}; +/* Flag bits for unique_table() */ +#define CHECK_DUP_ALLOW_DIFFERENT_ALIAS 1 +#define CHECK_DUP_FOR_CREATE 2 + uint create_tmp_table_def_key(THD *thd, char *key, const char *db, const char *table_name); uint get_table_def_key(const TABLE_LIST *table_list, const char **key); @@ -254,7 +258,7 @@ void kill_delayed_threads_for_table(TABLE_SHARE *share); void close_thread_table(THD *thd, TABLE **table_ptr); bool close_temporary_tables(THD *thd); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, - bool check_alias); + uint check_flag); int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans); void close_temporary_table(THD *thd, TABLE *table, bool free_share, bool delete_table); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0780f418c80..609e5472ba6 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -903,7 +903,7 @@ multi_delete::initialize_tables(JOIN *join) TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); tables_to_delete_from|= tbl->table->map; if (delete_while_scanning && - unique_table(thd, tbl, join->tables_list, false)) + unique_table(thd, tbl, join->tables_list, 0)) { /* If the table we are going to delete from appears diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 6036bbeb8b1..8b03c722898 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1483,7 +1483,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, { Item *fake_conds= 0; TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, table_list, table_list->next_global, 1))) + if ((duplicate= unique_table(thd, table_list, table_list->next_global, + CHECK_DUP_ALLOW_DIFFERENT_ALIAS))) { update_non_unique_table_error(table_list, "INSERT", duplicate); DBUG_RETURN(TRUE); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 87edf93bb4a..1162aa901d1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2970,7 +2970,7 @@ case SQLCOM_PREPARE: TABLE_LIST *duplicate; if ((duplicate= unique_table(thd, lex->query_tables, lex->query_tables->next_global, - 0))) + CHECK_DUP_FOR_CREATE))) { update_non_unique_table_error(lex->query_tables, "CREATE", duplicate); From 1b2078b4d8aa64bad8bc2b3a201e0cf8c075e6a6 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 15 May 2018 17:34:47 +0200 Subject: [PATCH 6/8] MDEV-15318 CREATE .. SELECT VALUES produces invalid table structure When Item_insert_value needs a dummy field, use zero-length Field_string, not Field_null. The latter isn't compatible with CREATE ... SELECT. --- mysql-test/r/insert_select.result | 9 +++++++++ mysql-test/t/insert_select.test | 10 ++++++++++ sql/item.cc | 8 ++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result index 8bfc4e9215e..157cc677d85 100644 --- a/mysql-test/r/insert_select.result +++ b/mysql-test/r/insert_select.result @@ -853,3 +853,12 @@ INSERT IGNORE INTO t1 SELECT t1.a FROM t1,t1 t2,t1 t3,t1 t4,t1 t5,t1 t6,t1 t7; SET GLOBAL myisam_data_pointer_size = @old_myisam_data_pointer_size; DROP TABLE t1; End of 5.1 tests +create table t1 (i int); +create table t2 as select values(i) as a from t1; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` binary(0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1, t2; +End of 5.5 tests diff --git a/mysql-test/t/insert_select.test b/mysql-test/t/insert_select.test index bcd87c2688d..385e372bc3b 100644 --- a/mysql-test/t/insert_select.test +++ b/mysql-test/t/insert_select.test @@ -423,3 +423,13 @@ SET GLOBAL myisam_data_pointer_size = @old_myisam_data_pointer_size; DROP TABLE t1; --echo End of 5.1 tests + +# +# MDEV-15318 CREATE .. SELECT VALUES produces invalid table structure +# +create table t1 (i int); +create table t2 as select values(i) as a from t1; +show create table t2; +drop table t1, t2; + +--echo End of 5.5 tests diff --git a/sql/item.cc b/sql/item.cc index c5c6df0ec48..135255ee21e 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8541,10 +8541,10 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) } else { - Field *tmp_field= field_arg->field; - /* charset doesn't matter here, it's to avoid sigsegv only */ - tmp_field= new Field_null(0, 0, Field::NONE, field_arg->field->field_name, - &my_charset_bin); + static uchar null_bit=1; + /* charset doesn't matter here */ + Field *tmp_field= new Field_string(0, 0, &null_bit, 1, Field::NONE, + field_arg->field->field_name, &my_charset_bin); if (tmp_field) { tmp_field->init(field_arg->field->table); From 27a7365f42ce0184a004e09b3bb1cadb868c8f64 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 18 May 2018 00:23:15 +0100 Subject: [PATCH 7/8] MDEV-16220 MTR - do not pass UTF8 on the command line for mysql client. It should work ok on all Unixes, but on Windows ,only worked by accident in the past, with client not being Unicode safe. It stopped working with Visual Studio 2017 15.7 update now. --- mysql-test/r/grant.result | 5 ---- mysql-test/r/grant_not_windows.result | 7 +++++ mysql-test/t/grant.test | 8 ----- mysql-test/t/grant_not_windows.test | 13 +++++++++ mysql-test/t/mysql.test | 16 +++++++--- mysql-test/t/mysql_cp932.test | 42 +++++++++++++++++++++++---- 6 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 mysql-test/r/grant_not_windows.result create mode 100644 mysql-test/t/grant_not_windows.test diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 94087393489..66cf6d35758 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -1647,11 +1647,6 @@ drop user mysqluser11@localhost; drop database mysqltest1; End of 5.0 tests set names utf8; -grant select on test.* to юзер_юзер@localhost; -user() -юзер_юзер@localhost -revoke all on test.* from юзер_юзер@localhost; -drop user юзер_юзер@localhost; grant select on test.* to очень_длинный_юзер@localhost; ERROR HY000: String 'очень_длинный_юзер' is too long for user name (should be no longer than 16) set names default; diff --git a/mysql-test/r/grant_not_windows.result b/mysql-test/r/grant_not_windows.result new file mode 100644 index 00000000000..5cf0c1deb32 --- /dev/null +++ b/mysql-test/r/grant_not_windows.result @@ -0,0 +1,7 @@ +set names utf8; +grant select on test.* to юзер_юзер@localhost; +user() +юзер_юзер@localhost +revoke all on test.* from юзер_юзер@localhost; +drop user юзер_юзер@localhost; +set names default; diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index efa79c5b768..2a8961e46a9 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -1574,15 +1574,7 @@ drop database mysqltest1; --echo End of 5.0 tests - -# -# Bug#21432 Database/Table name limited to 64 bytes, not chars, problems with multi-byte -# set names utf8; -grant select on test.* to юзер_юзер@localhost; ---exec $MYSQL --default-character-set=utf8 --user=юзер_юзер -e "select user()" -revoke all on test.* from юзер_юзер@localhost; -drop user юзер_юзер@localhost; --error ER_WRONG_STRING_LENGTH grant select on test.* to очень_длинный_юзер@localhost; set names default; diff --git a/mysql-test/t/grant_not_windows.test b/mysql-test/t/grant_not_windows.test new file mode 100644 index 00000000000..575516c409b --- /dev/null +++ b/mysql-test/t/grant_not_windows.test @@ -0,0 +1,13 @@ + # UTF8 parameters to mysql client do not work on Windows +--source include/not_windows.inc +--source include/not_embedded.inc + +# +# Bug#21432 Database/Table name limited to 64 bytes, not chars, problems with multi-byte +# +set names utf8; +grant select on test.* to юзер_юзер@localhost; +--exec $MYSQL --default-character-set=utf8 --user=юзер_юзер -e "select user()" +revoke all on test.* from юзер_юзер@localhost; +drop user юзер_юзер@localhost; +set names default; diff --git a/mysql-test/t/mysql.test b/mysql-test/t/mysql.test index dd964c46420..87756768c7f 100644 --- a/mysql-test/t/mysql.test +++ b/mysql-test/t/mysql.test @@ -51,14 +51,22 @@ drop table t1; # # Bug#17939 Wrong table format when using UTF8 strings -# ---exec $MYSQL --default-character-set=utf8 --table -e "SELECT 'John Doe' as '__tañgè Ñãmé'" 2>&1 ---exec $MYSQL --default-character-set=utf8 --table -e "SELECT '__tañgè Ñãmé' as 'John Doe'" 2>&1 +write_file $MYSQL_TMP_DIR/mysql_in; +SELECT 'John Doe' as '__tañgè Ñãmé'; +SELECT '__tañgè Ñãmé' as 'John Doe'; +EOF +--exec $MYSQL --default-character-set=utf8 --table < $MYSQL_TMP_DIR/mysql_in 2>&1 +remove_file $MYSQL_TMP_DIR/mysql_in; # # Bug#18265 -- mysql client: No longer right-justifies numeric columns # ---exec $MYSQL -t --default-character-set utf8 test -e "create table t1 (i int, j int, k char(25) charset utf8); insert into t1 (i) values (1); insert into t1 (k) values ('<----------------------->'); insert into t1 (k) values ('<-----'); insert into t1 (k) values ('Τη γλώσσα'); insert into t1 (k) values ('ᛖᚴ ᚷᛖᛏ'); select * from t1; DROP TABLE t1;" +write_file $MYSQL_TMP_DIR/mysql_in; +create table t1 (i int, j int, k char(25) charset utf8); insert into t1 (i) values (1); insert into t1 (k) values ('<----------------------->'); insert into t1 (k) values ('<-----'); insert into t1 (k) values ('Τη γλώσσα'); insert into t1 (k) values ('ᛖᚴ ᚷᛖᛏ'); select * from t1; DROP TABLE t1; +EOF +--exec $MYSQL -t --default-character-set utf8 test < $MYSQL_TMP_DIR/mysql_in +remove_file $MYSQL_TMP_DIR/mysql_in; + # # "DESCRIBE" commands may return strange NULLness flags. diff --git a/mysql-test/t/mysql_cp932.test b/mysql-test/t/mysql_cp932.test index 60a129c3805..8fba5750d89 100644 --- a/mysql-test/t/mysql_cp932.test +++ b/mysql-test/t/mysql_cp932.test @@ -10,13 +10,43 @@ # BUG#16217 - MySQL client misinterprets multi-byte char as escape `\' # +let $mysql_in= $MYSQL_TMP_DIR/mysql_in; + # new command \C or charset ---exec $MYSQL --default-character-set=utf8 test -e "\C cp932 \g" ---exec $MYSQL --default-character-set=cp932 test -e "charset utf8;" +write_file $mysql_in; +\C cp932 \g +EOF +--exec $MYSQL --default-character-set=utf8 test < $mysql_in +remove_file $mysql_in; + +write_file $mysql_in; +charset utf8; +EOF +--exec $MYSQL --default-character-set=cp932 test < $mysql_in +remove_file $mysql_in; # its usage to switch internally in mysql to requested charset ---exec $MYSQL --default-character-set=utf8 test -e "charset cp932; select '\'; create table t1 (c_cp932 TEXT CHARACTER SET cp932); insert into t1 values('\'); select * from t1; drop table t1;" ---exec $MYSQL --default-character-set=utf8 test -e "charset cp932; select '\'" ---exec $MYSQL --default-character-set=utf8 test -e "/*charset cp932 */; set character_set_client= cp932; select '\'" ---exec $MYSQL --default-character-set=utf8 test -e "/*!\C cp932 */; set character_set_client= cp932; select '\'" +write_file $mysql_in; +charset cp932; select '\'; create table t1 (c_cp932 TEXT CHARACTER SET cp932); insert into t1 values('\'); select * from t1; drop table t1; +EOF +--exec $MYSQL --default-character-set=utf8 test < $mysql_in +remove_file $mysql_in; + +write_file $mysql_in; +charset cp932; select '\' +EOF +--exec $MYSQL --default-character-set=utf8 test < $mysql_in +remove_file $mysql_in; + +write_file $mysql_in; +/*charset cp932 */; set character_set_client= cp932; select '\' +EOF +--exec $MYSQL --default-character-set=utf8 test < $mysql_in +remove_file $mysql_in; + +write_file $mysql_in; +/*!\C cp932 */; set character_set_client= cp932; select '\' +EOF +--exec $MYSQL --default-character-set=utf8 test < $mysql_in +remove_file $mysql_in; From cf5226174bcb41611e9650ac04104a9b16f368be Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 19 May 2018 15:34:17 +0200 Subject: [PATCH 8/8] MDEV-11129 CREATE OR REPLACE TABLE t1 AS SELECT spfunc() crashes if spfunc() references t1 fix a typo that broke the main.view test followup for ef295c31e3d --- sql/sql_base.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bdde123f18c..dc61f396605 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1373,7 +1373,7 @@ retry: DBUG_RETURN(res); /* Skip if table alias does not match. */ - if (check_flag & CHECK_DUP_FOR_CREATE) + if (check_flag & CHECK_DUP_ALLOW_DIFFERENT_ALIAS) { if (my_strcasecmp(table_alias_charset, t_alias, res->alias)) continue;