diff options
27 files changed, 651 insertions, 415 deletions
diff --git a/include/my_base.h b/include/my_base.h index 2c93165c912..5472473efa7 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 1995, 2017, MariaDB Corporation. + Copyright (c) 1995, 2018, 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 @@ -208,7 +208,13 @@ enum ha_extra_function { Used in ha_partition::handle_ordered_index_scan() to inform engine that we are starting an ordered index scan. Needed by Spider */ - HA_EXTRA_STARTING_ORDERED_INDEX_SCAN + HA_EXTRA_STARTING_ORDERED_INDEX_SCAN, + /** Start writing rows during ALTER TABLE...ALGORITHM=COPY. */ + HA_EXTRA_BEGIN_ALTER_COPY, + /** Finish writing rows during ALTER TABLE...ALGORITHM=COPY. */ + HA_EXTRA_END_ALTER_COPY, + /** Fake the start of a statement after wsrep_load_data_splitting hack */ + HA_EXTRA_FAKE_START_STMT }; /* Compatible option, to be deleted in 6.0 */ diff --git a/mysql-test/suite/innodb/r/alter_copy.result b/mysql-test/suite/innodb/r/alter_copy.result new file mode 100644 index 00000000000..286c5152ded --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_copy.result @@ -0,0 +1,213 @@ +# +# MDEV-11415 AVOID INTERMEDIATE COMMIT WHILE DOING +# ALTER TABLE...ALGORITHM=COPY +# +CREATE TABLE t(a SERIAL, b INT, c INT, d INT) ENGINE=InnoDB; +CREATE TABLE t1(a INT, b TEXT, c TEXT, +FULLTEXT(b), FULLTEXT(c(3)), FULLTEXT(b,c)) ENGINE=InnoDB; +BEGIN; +COMMIT; +SELECT COUNT(*) FROM t; +COUNT(*) +999 +UPDATE t SET b=a%7, c=a%11, d=a%13; +INSERT INTO t1 VALUES(1, 'This is a first b column', 'This is a first c column'); +INSERT INTO t1 VALUES(2, 'This is a second b column', 'This is a second c column'); +INSERT INTO t1(a) VALUES(3); +INSERT INTO t1 VALUES(4, 'This is a third b column', 'This is a third c column'); +DELETE FROM t1 WHERE a = 2; +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +a b c +1 This is a first b column This is a first c column +4 This is a third b column This is a third c column +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` text DEFAULT NULL, + `c` text DEFAULT NULL, + FULLTEXT KEY `b` (`b`), + FULLTEXT KEY `c` (`c`), + FULLTEXT KEY `b_2` (`b`,`c`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +ALTER TABLE t1 FORCE, ALGORITHM=COPY; +SET DEBUG_DBUG='+d,crash_commit_before'; +ALTER TABLE t ADD INDEX(b,c,d,a),ADD INDEX(b,c,a,d),ADD INDEX(b,a,c,d),ADD INDEX(b,a,d,c), +ADD INDEX(b,d,a,c),ADD INDEX(b,d,c,a),ADD INDEX(a,b,c,d),ADD INDEX(a,b,d,c), +ADD INDEX(a,c,b,d),ADD INDEX(a,c,d,b),ADD INDEX(a,d,b,c),ADD INDEX(a,d,c,b), +ADD INDEX(c,a,b,d),ADD INDEX(c,a,d,b),ADD INDEX(c,b,a,d),ADD INDEX(c,b,d,a), +ADD INDEX(c,d,a,b),ADD INDEX(c,d,b,a),ADD INDEX(d,a,b,c),ADD INDEX(d,a,c,b), +ADD INDEX(d,b,a,c),ADD INDEX(d,b,c,a),ADD INDEX(d,c,a,b),ADD INDEX(d,c,b,a), +ADD INDEX(a,b,c), ADD INDEX(a,c,b), ADD INDEX(a,c,d), ADD INDEX(a,d,c), +ADD INDEX(a,b,d), ADD INDEX(a,d,b), ADD INDEX(b,c,d), ADD INDEX(b,d,c), +ALGORITHM=COPY; +ERROR HY000: Lost connection to MySQL server during query +#sql-temporary.frm +#sql-temporary.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTSBEING_DELETED.ibd +FTSBEING_DELETED_CACHE.ibd +FTSCONFIG.ibd +FTSDELETED.ibd +FTSDELETED_CACHE.ibd +t.frm +t.ibd +t1.frm +t1.ibd +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + `d` int(11) DEFAULT NULL, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=latin1 +SELECT COUNT(*) FROM t; +COUNT(*) +999 +CHECK TABLE t; +Table Op Msg_type Msg_text +test.t check status OK +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +a b c +1 This is a first b column This is a first c column +4 This is a third b column This is a third c column +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` text DEFAULT NULL, + `c` text DEFAULT NULL, + FULLTEXT KEY `b` (`b`), + FULLTEXT KEY `c` (`c`), + FULLTEXT KEY `b_2` (`b`,`c`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +#sql-temporary.frm +#sql-temporary.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTSBEING_DELETED.ibd +FTSBEING_DELETED_CACHE.ibd +FTSCONFIG.ibd +FTSDELETED.ibd +FTSDELETED_CACHE.ibd +t.frm +t.ibd +t1.frm +t1.ibd +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + `d` int(11) DEFAULT NULL, + UNIQUE KEY `a` (`a`) +) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=latin1 +SELECT COUNT(*) FROM t; +COUNT(*) +999 +CHECK TABLE t; +Table Op Msg_type Msg_text +test.t check status OK +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +a b c +1 This is a first b column This is a first c column +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +a b c +1 This is a first b column This is a first c column +4 This is a third b column This is a third c column +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` text DEFAULT NULL, + `c` text DEFAULT NULL, + FULLTEXT KEY `b` (`b`), + FULLTEXT KEY `c` (`c`), + FULLTEXT KEY `b_2` (`b`,`c`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +#sql-temporary.frm +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTS_INDEX_1.ibd +FTS_INDEX_2.ibd +FTS_INDEX_3.ibd +FTS_INDEX_4.ibd +FTS_INDEX_5.ibd +FTS_INDEX_6.ibd +FTSBEING_DELETED.ibd +FTSBEING_DELETED_CACHE.ibd +FTSCONFIG.ibd +FTSDELETED.ibd +FTSDELETED_CACHE.ibd +t.frm +t.ibd +t1.frm +t1.ibd +DROP TABLE t1,t; diff --git a/mysql-test/suite/innodb/r/rename_table_debug.result b/mysql-test/suite/innodb/r/rename_table_debug.result index 7c9b961dee5..976b609bdd5 100644 --- a/mysql-test/suite/innodb/r/rename_table_debug.result +++ b/mysql-test/suite/innodb/r/rename_table_debug.result @@ -9,27 +9,4 @@ disconnect con1; SELECT * FROM t1; a b c d 1 NULL NULL NULL -BEGIN; -COMMIT; -UPDATE t1 SET b=a%7, c=a%11, d=a%13; -SET DEBUG_DBUG='+d,crash_commit_before'; -ALTER TABLE t1 -ADD INDEX(b,c,d,a),ADD INDEX(b,c,a,d),ADD INDEX(b,a,c,d),ADD INDEX(b,a,d,c), -ADD INDEX(b,d,a,c),ADD INDEX(b,d,c,a),ADD INDEX(a,b,c,d),ADD INDEX(a,b,d,c), -ADD INDEX(a,c,b,d),ADD INDEX(a,c,d,b),ADD INDEX(a,d,b,c),ADD INDEX(a,d,c,b), -ADD INDEX(c,a,b,d),ADD INDEX(c,a,d,b),ADD INDEX(c,b,a,d),ADD INDEX(c,b,d,a), -ADD INDEX(c,d,a,b),ADD INDEX(c,d,b,a),ADD INDEX(d,a,b,c),ADD INDEX(d,a,c,b), -ADD INDEX(d,b,a,c),ADD INDEX(d,b,c,a),ADD INDEX(d,c,a,b),ADD INDEX(d,c,b,a), -ADD INDEX(a,b,c), ADD INDEX(a,c,b), ADD INDEX(a,c,d), ADD INDEX(a,d,c), -ADD INDEX(a,b,d), ADD INDEX(a,d,b), ADD INDEX(b,c,d), ADD INDEX(b,d,c), -ALGORITHM=COPY; -ERROR HY000: Lost connection to MySQL server during query -CHECK TABLE t1; -Table Op Msg_type Msg_text -test.t1 check status OK -SELECT COUNT(*) FROM t1; -COUNT(*) -1000 DROP TABLE t1; -SET GLOBAL innodb_background_drop_list_empty= -@@GLOBAL.innodb_background_drop_list_empty; diff --git a/mysql-test/suite/innodb/t/alter_copy.test b/mysql-test/suite/innodb/t/alter_copy.test new file mode 100644 index 00000000000..0dce61994b5 --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_copy.test @@ -0,0 +1,91 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/not_embedded.inc + +--echo # +--echo # MDEV-11415 AVOID INTERMEDIATE COMMIT WHILE DOING +--echo # ALTER TABLE...ALGORITHM=COPY +--echo # + +CREATE TABLE t(a SERIAL, b INT, c INT, d INT) ENGINE=InnoDB; +CREATE TABLE t1(a INT, b TEXT, c TEXT, + FULLTEXT(b), FULLTEXT(c(3)), FULLTEXT(b,c)) ENGINE=InnoDB; + +let $c = 999; +BEGIN; +--disable_query_log +while ($c) { +INSERT INTO t() VALUES(); +dec $c; +} +--enable_query_log +COMMIT; + +SELECT COUNT(*) FROM t; +# try to make the to-be-created secondary index keys randomly distributed +UPDATE t SET b=a%7, c=a%11, d=a%13; + +INSERT INTO t1 VALUES(1, 'This is a first b column', 'This is a first c column'); +INSERT INTO t1 VALUES(2, 'This is a second b column', 'This is a second c column'); +INSERT INTO t1(a) VALUES(3); +INSERT INTO t1 VALUES(4, 'This is a third b column', 'This is a third c column'); +DELETE FROM t1 WHERE a = 2; +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +SHOW CREATE TABLE t1; +ALTER TABLE t1 FORCE, ALGORITHM=COPY; + +# crash right after the last write_row(), before the first commit of ALTER TABLE +--source include/expect_crash.inc + +SET DEBUG_DBUG='+d,crash_commit_before'; +--error 2013 +# create 32 secondary indexes +ALTER TABLE t ADD INDEX(b,c,d,a),ADD INDEX(b,c,a,d),ADD INDEX(b,a,c,d),ADD INDEX(b,a,d,c), + ADD INDEX(b,d,a,c),ADD INDEX(b,d,c,a),ADD INDEX(a,b,c,d),ADD INDEX(a,b,d,c), + ADD INDEX(a,c,b,d),ADD INDEX(a,c,d,b),ADD INDEX(a,d,b,c),ADD INDEX(a,d,c,b), + ADD INDEX(c,a,b,d),ADD INDEX(c,a,d,b),ADD INDEX(c,b,a,d),ADD INDEX(c,b,d,a), + ADD INDEX(c,d,a,b),ADD INDEX(c,d,b,a),ADD INDEX(d,a,b,c),ADD INDEX(d,a,c,b), + ADD INDEX(d,b,a,c),ADD INDEX(d,b,c,a),ADD INDEX(d,c,a,b),ADD INDEX(d,c,b,a), + ADD INDEX(a,b,c), ADD INDEX(a,c,b), ADD INDEX(a,c,d), ADD INDEX(a,d,c), + ADD INDEX(a,b,d), ADD INDEX(a,d,b), ADD INDEX(b,c,d), ADD INDEX(b,d,c), + ALGORITHM=COPY; + +--let $restart_parameters= --innodb-force-recovery=3 +--source include/start_mysqld.inc +let $datadir=`select @@datadir`; +--replace_regex /#sql-[0-9a-f_]*/#sql-temporary/ /FTS_[0-9a-f]*_[0-9a-f]*/FTS/ +--list_files $datadir/test +SHOW CREATE TABLE t; +SELECT COUNT(*) FROM t; +CHECK TABLE t; +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +SHOW CREATE TABLE t1; +CHECK TABLE t1; + +--let $restart_parameters= --innodb-read-only +--source include/restart_mysqld.inc +--replace_regex /#sql-[0-9a-f_]*/#sql-temporary/ /FTS_[0-9a-f]*_[0-9a-f]*/FTS/ + +--list_files $datadir/test +SHOW CREATE TABLE t; +SELECT COUNT(*) FROM t; +CHECK TABLE t; +SELECT * FROM t1 WHERE MATCH(b) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(c) AGAINST ('first'); +SELECT * FROM t1 WHERE MATCH(b,c) AGAINST ('column'); +SHOW CREATE TABLE t1; +CHECK TABLE t1; + +--let $restart_parameters= +--source include/restart_mysqld.inc +--replace_regex /#sql-[0-9a-f_]*/#sql-temporary/ /FTS_[0-9a-f]*_[0-9a-f]*/FTS/ +--list_files $datadir/test +DROP TABLE t1,t; + +# Work around missing crash recovery at the SQL layer. +--remove_files_wildcard $datadir/test #sql-*.frm diff --git a/mysql-test/suite/innodb/t/rename_table_debug.test b/mysql-test/suite/innodb/t/rename_table_debug.test index 20af12dc15c..df4331cf8bb 100644 --- a/mysql-test/suite/innodb/t/rename_table_debug.test +++ b/mysql-test/suite/innodb/t/rename_table_debug.test @@ -18,37 +18,4 @@ SET DEBUG_SYNC='now WAIT_FOR renamed'; --source include/restart_mysqld.inc --disconnect con1 SELECT * FROM t1; - -let $c = 999; -BEGIN; ---disable_query_log -while ($c) { -INSERT INTO t1() VALUES(); -dec $c; -} ---enable_query_log -COMMIT; -UPDATE t1 SET b=a%7, c=a%11, d=a%13; - ---source include/expect_crash.inc -SET DEBUG_DBUG='+d,crash_commit_before'; ---error 2013 -ALTER TABLE t1 -ADD INDEX(b,c,d,a),ADD INDEX(b,c,a,d),ADD INDEX(b,a,c,d),ADD INDEX(b,a,d,c), -ADD INDEX(b,d,a,c),ADD INDEX(b,d,c,a),ADD INDEX(a,b,c,d),ADD INDEX(a,b,d,c), -ADD INDEX(a,c,b,d),ADD INDEX(a,c,d,b),ADD INDEX(a,d,b,c),ADD INDEX(a,d,c,b), -ADD INDEX(c,a,b,d),ADD INDEX(c,a,d,b),ADD INDEX(c,b,a,d),ADD INDEX(c,b,d,a), -ADD INDEX(c,d,a,b),ADD INDEX(c,d,b,a),ADD INDEX(d,a,b,c),ADD INDEX(d,a,c,b), -ADD INDEX(d,b,a,c),ADD INDEX(d,b,c,a),ADD INDEX(d,c,a,b),ADD INDEX(d,c,b,a), -ADD INDEX(a,b,c), ADD INDEX(a,c,b), ADD INDEX(a,c,d), ADD INDEX(a,d,c), -ADD INDEX(a,b,d), ADD INDEX(a,d,b), ADD INDEX(b,c,d), ADD INDEX(b,d,c), -ALGORITHM=COPY; ---source include/start_mysqld.inc -CHECK TABLE t1; -SELECT COUNT(*) FROM t1; DROP TABLE t1; -# MDEV-11415 TODO: remove the following -SET GLOBAL innodb_background_drop_list_empty= -@@GLOBAL.innodb_background_drop_list_empty; -# Work around missing crash recovery at the SQL layer. ---remove_files_wildcard $datadir/test #sql-*.frm diff --git a/mysql-test/suite/versioning/r/trx_id.result b/mysql-test/suite/versioning/r/trx_id.result index a88ba7eec2b..940a6479934 100644 --- a/mysql-test/suite/versioning/r/trx_id.result +++ b/mysql-test/suite/versioning/r/trx_id.result @@ -53,8 +53,8 @@ add column e bigint unsigned as row end, add period for system_time(s, e), add system versioning, algorithm=copy; -select check_result(count(*) = @tmp) from mysql.transaction_registry; -check_result(count(*) = @tmp) +select check_result(count(*) = @tmp + 1) from mysql.transaction_registry; +check_result(count(*) = @tmp + 1) [CORRECT] # TRX_ID to TIMESTAMP versioning switch create or replace table t1 ( diff --git a/mysql-test/suite/versioning/t/trx_id.test b/mysql-test/suite/versioning/t/trx_id.test index ee4e927b506..933f0618577 100644 --- a/mysql-test/suite/versioning/t/trx_id.test +++ b/mysql-test/suite/versioning/t/trx_id.test @@ -57,8 +57,8 @@ alter table t1 add period for system_time(s, e), add system versioning, algorithm=copy; -select check_result(count(*) = @tmp) from mysql.transaction_registry; - +# With MDEV-14511 the transaction will be registered even for empty tables. +select check_result(count(*) = @tmp + 1) from mysql.transaction_registry; --echo # TRX_ID to TIMESTAMP versioning switch create or replace table t1 ( diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 8675aed3228..87c056e7bae 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2005, 2017, Oracle and/or its affiliates. - Copyright (c) 2009, 2017, MariaDB + Copyright (c) 2009, 2018, MariaDB 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 @@ -8897,6 +8897,9 @@ int ha_partition::extra(enum ha_extra_function operation) */ DBUG_RETURN(ER_UNSUPORTED_LOG_ENGINE); case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: + case HA_EXTRA_BEGIN_ALTER_COPY: + case HA_EXTRA_END_ALTER_COPY: + case HA_EXTRA_FAKE_START_STMT: DBUG_RETURN(loop_extra(operation)); default: { diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 9a15d4b9c0d..93669554a5a 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2010, 2017, MariaDB Corporation + Copyright (c) 2010, 2018, 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 @@ -97,6 +97,45 @@ public: #define GET (stack_pos != stack ? *--stack_pos : my_b_get(&cache)) #define PUSH(A) *(stack_pos++)=(A) +#ifdef WITH_WSREP +/** If requested by wsrep_load_data_splitting, commit and restart +the transaction after every 10,000 inserted rows. */ + +static bool wsrep_load_data_split(THD *thd, const TABLE *table, + const COPY_INFO &info) +{ + extern struct handlerton* innodb_hton_ptr; + + DBUG_ENTER("wsrep_load_data_split"); + + if (wsrep_load_data_splitting && wsrep_on(thd) + && info.records && !(info.records % 10000) + && thd->transaction.stmt.ha_list + && thd->transaction.stmt.ha_list->ht() == binlog_hton + && thd->transaction.stmt.ha_list->next() + && thd->transaction.stmt.ha_list->next()->ht() == innodb_hton_ptr + && !thd->transaction.stmt.ha_list->next()->next()) + { + WSREP_DEBUG("intermediate transaction commit in LOAD DATA"); + if (wsrep_run_wsrep_commit(thd, true) != WSREP_TRX_OK) DBUG_RETURN(true); + if (binlog_hton->commit(binlog_hton, thd, true)) DBUG_RETURN(true); + wsrep_post_commit(thd, true); + innodb_hton_ptr->commit(innodb_hton_ptr, thd, true); + table->file->extra(HA_EXTRA_FAKE_START_STMT); + } + + DBUG_RETURN(false); +} +# define WSREP_LOAD_DATA_SPLIT(thd,table,info) \ + if (wsrep_load_data_split(thd,table,info)) \ + { \ + table->auto_increment_field_not_null= FALSE; \ + DBUG_RETURN(1); \ + } +#else /* WITH_WSREP */ +#define WSREP_LOAD_DATA_SPLIT(thd,table,info) /* empty */ +#endif /* WITH_WSREP */ + class READ_INFO { File file; String data; /* Read buffer */ @@ -989,6 +1028,7 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, DBUG_RETURN(-1); } + WSREP_LOAD_DATA_SPLIT(thd, table, info); err= write_record(thd, table, &info); table->auto_increment_field_not_null= FALSE; if (err) @@ -1194,6 +1234,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, DBUG_RETURN(-1); } + WSREP_LOAD_DATA_SPLIT(thd, table, info); err= write_record(thd, table, &info); table->auto_increment_field_not_null= FALSE; if (err) @@ -1348,6 +1389,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, DBUG_RETURN(-1); } + WSREP_LOAD_DATA_SPLIT(thd, table, info); err= write_record(thd, table, &info); table->auto_increment_field_not_null= false; if (err) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7974718e8dd..dc0accad799 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -10357,6 +10357,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd->progress.max_counter= from->file->records(); time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10; + if (!ignore) /* for now, InnoDB needs the undo log for ALTER IGNORE */ + to->file->extra(HA_EXTRA_BEGIN_ALTER_COPY); while (!(error= info.read_record())) { @@ -10516,6 +10518,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, to->file->print_error(my_errno,MYF(0)); error= 1; } + if (!ignore) + to->file->extra(HA_EXTRA_END_ALTER_COPY); to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); if (mysql_trans_commit_alter_copy_data(thd)) diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 403f1d57b9d..59488d15750 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -2309,7 +2309,6 @@ fts_savepoint_create( /******************************************************************//** Create an FTS trx. @return FTS trx */ -static fts_trx_t* fts_trx_create( /*===========*/ @@ -3340,6 +3339,144 @@ fts_fetch_doc_from_rec( } } +/** Fetch the data from tuple and tokenize the document. +@param[in] get_doc FTS index's get_doc struct +@param[in] tuple tuple should be arranged in table schema order +@param[out] doc fts doc to hold parsed documents. */ +static +void +fts_fetch_doc_from_tuple( + fts_get_doc_t* get_doc, + const dtuple_t* tuple, + fts_doc_t* doc) +{ + dict_index_t* index; + st_mysql_ftparser* parser; + ulint doc_len = 0; + ulint processed_doc = 0; + ulint num_field; + + if (get_doc == NULL) { + return; + } + + index = get_doc->index_cache->index; + parser = get_doc->index_cache->index->parser; + num_field = dict_index_get_n_fields(index); + + for (ulint i = 0; i < num_field; i++) { + const dict_field_t* ifield; + const dict_col_t* col; + ulint pos; + dfield_t* field; + + ifield = dict_index_get_nth_field(index, i); + col = dict_field_get_col(ifield); + pos = dict_col_get_no(col); + field = dtuple_get_nth_field(tuple, pos); + + if (!get_doc->index_cache->charset) { + get_doc->index_cache->charset = fts_get_charset( + ifield->col->prtype); + } + + ut_ad(!dfield_is_ext(field)); + + doc->text.f_str = (byte*) dfield_get_data(field); + doc->text.f_len = dfield_get_len(field); + doc->found = TRUE; + doc->charset = get_doc->index_cache->charset; + + /* field data is NULL. */ + if (doc->text.f_len == UNIV_SQL_NULL || doc->text.f_len == 0) { + continue; + } + + if (processed_doc == 0) { + fts_tokenize_document(doc, NULL, parser); + } else { + fts_tokenize_document_next(doc, doc_len, NULL, parser); + } + + processed_doc++; + doc_len += doc->text.f_len + 1; + } +} + +/** Fetch the document from tuple, tokenize the text data and +insert the text data into fts auxiliary table and +its cache. Moreover this tuple fields doesn't contain any information +about externally stored field. This tuple contains data directly +converted from mysql. +@param[in] ftt FTS transaction table +@param[in] doc_id doc id +@param[in] tuple tuple from where data can be retrieved + and tuple should be arranged in table + schema order. */ +void +fts_add_doc_from_tuple( + fts_trx_table_t*ftt, + doc_id_t doc_id, + const dtuple_t* tuple) +{ + mtr_t mtr; + fts_cache_t* cache = ftt->table->fts->cache; + + ut_ad(cache->get_docs); + + if (!(ftt->table->fts->fts_status & ADDED_TABLE_SYNCED)) { + fts_init_index(ftt->table, FALSE); + } + + mtr_start(&mtr); + + ulint num_idx = ib_vector_size(cache->get_docs); + + for (ulint i = 0; i < num_idx; ++i) { + fts_doc_t doc; + dict_table_t* table; + fts_get_doc_t* get_doc; + + get_doc = static_cast<fts_get_doc_t*>( + ib_vector_get(cache->get_docs, i)); + table = get_doc->index_cache->index->table; + + fts_doc_init(&doc); + fts_fetch_doc_from_tuple( + get_doc, tuple, &doc); + + if (doc.found) { + mtr_commit(&mtr); + rw_lock_x_lock(&table->fts->cache->lock); + + if (table->fts->cache->stopword_info.status + & STOPWORD_NOT_INIT) { + fts_load_stopword(table, NULL, NULL, + NULL, TRUE, TRUE); + } + + fts_cache_add_doc( + table->fts->cache, + get_doc->index_cache, + doc_id, doc.tokens); + + rw_lock_x_unlock(&table->fts->cache->lock); + + if (cache->total_size > fts_max_cache_size / 5 + || fts_need_sync) { + fts_sync(cache->sync, true, false, false); + } + + mtr_start(&mtr); + + } + + fts_doc_free(&doc); + } + + mtr_commit(&mtr); +} + /*********************************************************************//** This function fetches the document inserted during the committing transaction, and tokenize the inserted text data and insert into diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 0479699f4f6..b190df6dded 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -185,7 +185,7 @@ static mysql_mutex_t pending_checkpoint_mutex; #define EQ_CURRENT_THD(thd) ((thd) == current_thd) -static struct handlerton* innodb_hton_ptr; +struct handlerton* innodb_hton_ptr; static const long AUTOINC_OLD_STYLE_LOCKING = 0; static const long AUTOINC_NEW_STYLE_LOCKING = 1; @@ -2996,7 +2996,6 @@ ha_innobase::ha_innobase( | (srv_force_primary_key ? HA_REQUIRE_PRIMARY_KEY : 0) ), m_start_of_scan(), - m_num_write_row(), m_mysql_has_locked() {} @@ -8177,7 +8176,6 @@ ha_innobase::write_row( #ifdef WITH_WSREP ibool auto_inc_inserted= FALSE; /* if NULL was inserted */ #endif - ulint sql_command; int error_result = 0; bool auto_inc_used = false; @@ -8195,7 +8193,7 @@ ha_innobase::write_row( DB_FORCED_ABORT, 0, m_user_thd)); } - /* Step-1: Validation checks before we commence write_row operation. */ + /* Validation checks before we commence write_row operation. */ if (high_level_read_only) { ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); @@ -8216,131 +8214,7 @@ ha_innobase::write_row( ++trx->will_lock; } - /* Step-2: Intermediate commit if original operation involves ALTER - table with algorithm = copy. Intermediate commit ease pressure on - recovery if server crashes while ALTER is active. */ - sql_command = thd_sql_command(m_user_thd); - - if ((sql_command == SQLCOM_ALTER_TABLE - || sql_command == SQLCOM_OPTIMIZE - || sql_command == SQLCOM_CREATE_INDEX -#ifdef WITH_WSREP - || (sql_command == SQLCOM_LOAD - && wsrep_load_data_splitting && wsrep_on(m_user_thd) - && !thd_test_options( - m_user_thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) -#endif /* WITH_WSREP */ - || sql_command == SQLCOM_DROP_INDEX) - && m_num_write_row >= 10000) { -#ifdef WITH_WSREP - if (sql_command == SQLCOM_LOAD && wsrep_on(m_user_thd)) { - WSREP_DEBUG("forced trx split for LOAD: %s", - wsrep_thd_query(m_user_thd)); - } -#endif /* WITH_WSREP */ - /* ALTER TABLE is COMMITted at every 10000 copied rows. - The IX table lock for the original table has to be re-issued. - As this method will be called on a temporary table where the - contents of the original table is being copied to, it is - a bit tricky to determine the source table. The cursor - position in the source table need not be adjusted after the - intermediate COMMIT, since writes by other transactions are - being blocked by a MySQL table lock TL_WRITE_ALLOW_READ. */ - - dict_table_t* src_table; - enum lock_mode mode; - - m_num_write_row = 0; - - /* Commit the transaction. This will release the table - locks, so they have to be acquired again. */ - - /* Altering an InnoDB table */ - /* Get the source table. */ - src_table = lock_get_src_table( - m_prebuilt->trx, m_prebuilt->table, &mode); - if (!src_table) { -no_commit: - /* Unknown situation: do not commit */ - ; - } else if (src_table == m_prebuilt->table) { -#ifdef WITH_WSREP - if (wsrep_on(m_user_thd) && - wsrep_load_data_splitting && - sql_command == SQLCOM_LOAD && - !thd_test_options(m_user_thd, - OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) - { - switch (wsrep_run_wsrep_commit(m_user_thd, 1)) { - case WSREP_TRX_OK: - break; - case WSREP_TRX_SIZE_EXCEEDED: - case WSREP_TRX_CERT_FAIL: - case WSREP_TRX_ERROR: - DBUG_RETURN(1); - } - - if (binlog_hton->commit(binlog_hton, m_user_thd, 1)) { - DBUG_RETURN(1); - } - wsrep_post_commit(m_user_thd, TRUE); - } -#endif /* WITH_WSREP */ - /* Source table is not in InnoDB format: - no need to re-acquire locks on it. */ - - /* Altering to InnoDB format */ - innobase_commit(ht, m_user_thd, 1); - /* Note that this transaction is still active. */ - trx_register_for_2pc(m_prebuilt->trx); - /* We will need an IX lock on the destination table. */ - m_prebuilt->sql_stat_start = TRUE; - } else { -#ifdef WITH_WSREP - if (wsrep_on(m_user_thd) && - wsrep_load_data_splitting && - sql_command == SQLCOM_LOAD && - !thd_test_options(m_user_thd, - OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { - switch (wsrep_run_wsrep_commit(m_user_thd, 1)) { - case WSREP_TRX_OK: - break; - case WSREP_TRX_SIZE_EXCEEDED: - case WSREP_TRX_CERT_FAIL: - case WSREP_TRX_ERROR: - DBUG_RETURN(1); - } - - if (binlog_hton->commit(binlog_hton, m_user_thd, 1)) { - DBUG_RETURN(1); - } - - wsrep_post_commit(m_user_thd, TRUE); - } -#endif /* WITH_WSREP */ - /* Ensure that there are no other table locks than - LOCK_IX and LOCK_AUTO_INC on the destination table. */ - - if (!lock_is_table_exclusive(m_prebuilt->table, - m_prebuilt->trx)) { - goto no_commit; - } - - /* Commit the transaction. This will release the table - locks, so they have to be acquired again. */ - innobase_commit(ht, m_user_thd, 1); - /* Note that this transaction is still active. */ - trx_register_for_2pc(m_prebuilt->trx); - /* Re-acquire the table lock on the source table. */ - row_lock_table_for_mysql(m_prebuilt, src_table, mode); - /* We will need an IX lock on the destination table. */ - m_prebuilt->sql_stat_start = TRUE; - } - } - - m_num_write_row++; - - /* Step-3: Handling of Auto-Increment Columns. */ + /* Handling of Auto-Increment Columns. */ if (table->next_number_field && record == table->record[0]) { /* Reset the error code before calling @@ -8373,7 +8247,7 @@ no_commit: auto_inc_used = true; } - /* Step-4: Prepare INSERT graph that will be executed for actual INSERT + /* Prepare INSERT graph that will be executed for actual INSERT (This is a one time operation) */ if (m_prebuilt->mysql_template == NULL || m_prebuilt->template_type != ROW_MYSQL_WHOLE_ROW) { @@ -8389,12 +8263,12 @@ no_commit: vers_set_fields = table->versioned_write(VERS_TRX_ID) ? ROW_INS_VERSIONED : ROW_INS_NORMAL; - /* Step-5: Execute insert graph that will result in actual insert. */ + /* Execute insert graph that will result in actual insert. */ error = row_insert_for_mysql((byte*) record, m_prebuilt, vers_set_fields); DEBUG_SYNC(m_user_thd, "ib_after_row_insert"); - /* Step-6: Handling of errors related to auto-increment. */ + /* Handling of errors related to auto-increment. */ if (auto_inc_used) { ulonglong auto_inc; ulonglong col_max_value; @@ -8422,13 +8296,11 @@ no_commit: must update the autoinc counter if we are performing those statements. */ - switch (sql_command) { + switch (thd_sql_command(m_user_thd)) { case SQLCOM_LOAD: - if (trx->duplicates) { - - goto set_max_autoinc; + if (!trx->duplicates) { + break; } - break; case SQLCOM_REPLACE: case SQLCOM_INSERT_SELECT: @@ -8517,7 +8389,7 @@ set_max_autoinc: innobase_srv_conc_exit_innodb(m_prebuilt); report_error: - /* Step-7: Cleanup and exit. */ + /* Cleanup and exit. */ if (error == DB_TABLESPACE_DELETED) { ib_senderrf( trx->mysql_thd, IB_LOG_LEVEL_ERROR, @@ -15752,6 +15624,26 @@ ha_innobase::extra( case HA_EXTRA_WRITE_CANNOT_REPLACE: thd_to_trx(ha_thd())->duplicates &= ~TRX_DUP_REPLACE; break; + case HA_EXTRA_BEGIN_ALTER_COPY: + m_prebuilt->table->skip_alter_undo = 1; + if (m_prebuilt->table->is_temporary() + || !m_prebuilt->table->versioned_by_id()) { + break; + } + trx_start_if_not_started(m_prebuilt->trx, true); + m_prebuilt->trx->mod_tables.insert( + trx_mod_tables_t::value_type( + const_cast<dict_table_t*>(m_prebuilt->table), + 0)) + .first->second.set_versioned(0); + break; + case HA_EXTRA_END_ALTER_COPY: + m_prebuilt->table->skip_alter_undo = 0; + break; + case HA_EXTRA_FAKE_START_STMT: + trx_register_for_2pc(m_prebuilt->trx); + m_prebuilt->sql_stat_start = true; + break; default:/* Do nothing */ ; } @@ -15854,7 +15746,7 @@ ha_innobase::start_stmt( init_table_handle_for_HANDLER(); m_prebuilt->select_lock_type = LOCK_X; m_prebuilt->stored_select_lock_type = LOCK_X; - error = row_lock_table_for_mysql(m_prebuilt, NULL, 1); + error = row_lock_table(m_prebuilt); if (error != DB_SUCCESS) { int st = convert_error_code_to_mysql( @@ -16118,8 +16010,7 @@ ha_innobase::external_lock( && thd_test_options(thd, OPTION_NOT_AUTOCOMMIT) && thd_in_lock_tables(thd)) { - dberr_t error = row_lock_table_for_mysql( - m_prebuilt, NULL, 0); + dberr_t error = row_lock_table(m_prebuilt); if (error != DB_SUCCESS) { diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index d7f5d36a680..492e61be3fa 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -515,9 +515,6 @@ protected: ROW_SEL_EXACT_PREFIX, or undefined */ uint m_last_match_mode; - /** number of write_row() calls */ - uint m_num_write_row; - /** If mysql has locked with external_lock() */ bool m_mysql_has_locked; }; diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index e9b60debbc9..01840379a71 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1609,6 +1609,13 @@ struct dict_table_t { Use DICT_TF2_FLAG_IS_SET() to parse this flag. */ unsigned flags2:DICT_TF2_BITS; + /** TRUE if the table is an intermediate table during copy alter + operation or a partition/subpartition which is required for copying + data and skip the undo log for insertion of row in the table. + This variable will be set and unset during extra(), or during the + process of altering partitions */ + unsigned skip_alter_undo:1; + /*!< whether this is in a single-table tablespace and the .ibd file is missing or page decryption failed and page is corrupted */ unsigned file_unreadable:1; diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index 813e34b43d3..362bdcb7fe6 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -1015,5 +1015,27 @@ fts_check_corrupt( dict_table_t* base_table, trx_t* trx); +/** Fetch the document from tuple, tokenize the text data and +insert the text data into fts auxiliary table and +its cache. Moreover this tuple fields doesn't contain any information +about externally stored field. This tuple contains data directly +converted from mysql. +@param[in] ftt FTS transaction table +@param[in] doc_id doc id +@param[in] tuple tuple from where data can be retrieved + and tuple should be arranged in table + schema order. */ +void +fts_add_doc_from_tuple( + fts_trx_table_t*ftt, + doc_id_t doc_id, + const dtuple_t* tuple); + +/** Create an FTS trx. +@param[in,out] trx InnoDB Transaction +@return FTS transaction. */ +fts_trx_t* +fts_trx_create( + trx_t* trx); #endif /*!< fts0fts.h */ diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 658211b4aca..1ef2f12975a 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -554,30 +554,6 @@ lock_rec_find_set_bit( bit set */ /*********************************************************************//** -Gets the source table of an ALTER TABLE transaction. The table must be -covered by an IX or IS table lock. -@return the source table of transaction, if it is covered by an IX or -IS table lock; dest if there is no source table, and NULL if the -transaction is locking more than two tables or an inconsistency is -found */ -dict_table_t* -lock_get_src_table( -/*===============*/ - trx_t* trx, /*!< in: transaction */ - dict_table_t* dest, /*!< in: destination of ALTER TABLE */ - lock_mode* mode); /*!< out: lock mode of the source table */ -/*********************************************************************//** -Determine if the given table is exclusively "owned" by the given -transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC -on the table. -@return TRUE if table is only locked by trx, with LOCK_IX, and -possibly LOCK_AUTO_INC */ -ibool -lock_is_table_exclusive( -/*====================*/ - const dict_table_t* table, /*!< in: table */ - const trx_t* trx); /*!< in: transaction */ -/*********************************************************************//** Checks if a lock request lock1 has to wait for request lock2. @return TRUE if lock1 has to wait for lock2 to be removed */ ibool diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 407e1705a0f..77b8337ac91 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2000, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, 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 @@ -219,21 +219,11 @@ row_lock_table_autoinc_for_mysql( table handle */ MY_ATTRIBUTE((nonnull, warn_unused_result)); -/*********************************************************************//** -Sets a table lock on the table mentioned in prebuilt. +/** Lock a table. +@param[in,out] prebuilt table handle @return error code or DB_SUCCESS */ dberr_t -row_lock_table_for_mysql( -/*=====================*/ - row_prebuilt_t* prebuilt, /*!< in: prebuilt struct in the MySQL - table handle */ - dict_table_t* table, /*!< in: table to lock, or NULL - if prebuilt->table should be - locked as - prebuilt->select_lock_type */ - ulint mode) /*!< in: lock mode of table - (ignored if table==NULL) */ - MY_ATTRIBUTE((nonnull(1))); +row_lock_table(row_prebuilt_t* prebuilt); /** System Versioning: row_insert_for_mysql() modes */ enum ins_mode_t { diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 9d7f6d61fc4..792fb8ffc8b 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -618,145 +618,6 @@ lock_get_size(void) } /*********************************************************************//** -Gets the source table of an ALTER TABLE transaction. The table must be -covered by an IX or IS table lock. -@return the source table of transaction, if it is covered by an IX or -IS table lock; dest if there is no source table, and NULL if the -transaction is locking more than two tables or an inconsistency is -found */ -dict_table_t* -lock_get_src_table( -/*===============*/ - trx_t* trx, /*!< in: transaction */ - dict_table_t* dest, /*!< in: destination of ALTER TABLE */ - lock_mode* mode) /*!< out: lock mode of the source table */ -{ - dict_table_t* src; - lock_t* lock; - - ut_ad(!lock_mutex_own()); - - src = NULL; - *mode = LOCK_NONE; - - /* The trx mutex protects the trx_locks for our purposes. - Other transactions could want to convert one of our implicit - record locks to an explicit one. For that, they would need our - trx mutex. Waiting locks can be removed while only holding - lock_sys->mutex, but this is a running transaction and cannot - thus be holding any waiting locks. */ - trx_mutex_enter(trx); - - for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks); - lock != NULL; - lock = UT_LIST_GET_NEXT(trx_locks, lock)) { - lock_table_t* tab_lock; - lock_mode lock_mode; - if (!(lock_get_type_low(lock) & LOCK_TABLE)) { - /* We are only interested in table locks. */ - continue; - } - tab_lock = &lock->un_member.tab_lock; - if (dest == tab_lock->table) { - /* We are not interested in the destination table. */ - continue; - } else if (!src) { - /* This presumably is the source table. */ - src = tab_lock->table; - if (UT_LIST_GET_LEN(src->locks) != 1 - || UT_LIST_GET_FIRST(src->locks) != lock) { - /* We only support the case when - there is only one lock on this table. */ - src = NULL; - goto func_exit; - } - } else if (src != tab_lock->table) { - /* The transaction is locking more than - two tables (src and dest): abort */ - src = NULL; - goto func_exit; - } - - /* Check that the source table is locked by - LOCK_IX or LOCK_IS. */ - lock_mode = lock_get_mode(lock); - if (lock_mode == LOCK_IX || lock_mode == LOCK_IS) { - if (*mode != LOCK_NONE && *mode != lock_mode) { - /* There are multiple locks on src. */ - src = NULL; - goto func_exit; - } - *mode = lock_mode; - } - } - - if (!src) { - /* No source table lock found: flag the situation to caller */ - src = dest; - } - -func_exit: - trx_mutex_exit(trx); - return(src); -} - -/*********************************************************************//** -Determine if the given table is exclusively "owned" by the given -transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC -on the table. -@return TRUE if table is only locked by trx, with LOCK_IX, and -possibly LOCK_AUTO_INC */ -ibool -lock_is_table_exclusive( -/*====================*/ - const dict_table_t* table, /*!< in: table */ - const trx_t* trx) /*!< in: transaction */ -{ - const lock_t* lock; - ibool ok = FALSE; - - ut_ad(table); - ut_ad(trx); - - lock_mutex_enter(); - - for (lock = UT_LIST_GET_FIRST(table->locks); - lock != NULL; - lock = UT_LIST_GET_NEXT(locks, &lock->un_member.tab_lock)) { - if (lock->trx != trx) { - /* A lock on the table is held - by some other transaction. */ - goto not_ok; - } - - if (!(lock_get_type_low(lock) & LOCK_TABLE)) { - /* We are interested in table locks only. */ - continue; - } - - switch (lock_get_mode(lock)) { - case LOCK_IX: - ok = TRUE; - break; - case LOCK_AUTO_INC: - /* It is allowed for trx to hold an - auto_increment lock. */ - break; - default: -not_ok: - /* Other table locks than LOCK_IX are not allowed. */ - ok = FALSE; - goto func_exit; - } - } - -func_exit: - lock_mutex_exit(); - - return(ok); -} - -/*********************************************************************//** Sets the wait flag of a lock and the back pointer in trx to lock. */ UNIV_INLINE void diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 5ed664ee0ee..027d159f4e4 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -3230,7 +3230,7 @@ row_ins_clust_index_entry( n_uniq = dict_index_is_unique(index) ? index->n_uniq : 0; - const ulint flags = index->table->no_rollback() ? BTR_NO_ROLLBACK + ulint flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : dict_table_is_temporary(index->table) ? BTR_NO_LOCKING_FLAG : 0; const ulint orig_n_fields = entry->n_fields; @@ -3238,6 +3238,17 @@ row_ins_clust_index_entry( /* Try first optimistic descent to the B-tree */ log_free_check(); + /* For intermediate table during copy alter table, + skip the undo log and record lock checking for + insertion operation. + */ + if (index->table->skip_alter_undo) { + flags |= BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG; + } + + /* Try first optimistic descent to the B-tree */ + log_free_check(); + err = row_ins_clust_index_entry_low( flags, BTR_MODIFY_LEAF, index, n_uniq, entry, n_ext, thr, dup_chk_only); @@ -3283,6 +3294,7 @@ row_ins_sec_index_entry( dberr_t err; mem_heap_t* offsets_heap; mem_heap_t* heap; + trx_id_t trx_id = 0; DBUG_EXECUTE_IF("row_ins_sec_index_entry_timeout", { DBUG_SET("-d,row_ins_sec_index_entry_timeout"); @@ -3305,13 +3317,22 @@ row_ins_sec_index_entry( /* Try first optimistic descent to the B-tree */ log_free_check(); - const ulint flags = dict_table_is_temporary(index->table) + ulint flags = dict_table_is_temporary(index->table) ? BTR_NO_LOCKING_FLAG : 0; + /* For intermediate table during copy alter table, + skip the undo log and record lock checking for + insertion operation. + */ + if (index->table->skip_alter_undo) { + trx_id = thr_get_trx(thr)->id; + flags |= BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG; + } + err = row_ins_sec_index_entry_low( flags, BTR_MODIFY_LEAF, index, offsets_heap, heap, entry, - 0, thr, dup_chk_only); + trx_id, thr, dup_chk_only); if (err == DB_FAIL) { mem_heap_empty(heap); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 2418e8de433..6605fd681ed 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1266,20 +1266,11 @@ run_again: return(err); } -/*********************************************************************//** -Sets a table lock on the table mentioned in prebuilt. +/** Lock a table. +@param[in,out] prebuilt table handle @return error code or DB_SUCCESS */ dberr_t -row_lock_table_for_mysql( -/*=====================*/ - row_prebuilt_t* prebuilt, /*!< in: prebuilt struct in the MySQL - table handle */ - dict_table_t* table, /*!< in: table to lock, or NULL - if prebuilt->table should be - locked as - prebuilt->select_lock_type */ - ulint mode) /*!< in: lock mode of table - (ignored if table==NULL) */ +row_lock_table(row_prebuilt_t* prebuilt) { trx_t* trx = prebuilt->trx; que_thr_t* thr; @@ -1309,17 +1300,10 @@ run_again: trx_start_if_not_started_xa(trx, false); - if (table) { - err = lock_table( - 0, table, - static_cast<enum lock_mode>(mode), thr); - } else { - err = lock_table( - 0, prebuilt->table, - static_cast<enum lock_mode>( - prebuilt->select_lock_type), - thr); - } + err = lock_table(0, prebuilt->table, + static_cast<enum lock_mode>( + prebuilt->select_lock_type), + thr); trx->error_state = err; @@ -1599,9 +1583,21 @@ error_exit: } } - /* Pass NULL for the columns affected, since an INSERT affects - all FTS indexes. */ - fts_trx_add_op(trx, table, doc_id, FTS_INSERT, NULL); + if (table->skip_alter_undo) { + if (trx->fts_trx == NULL) { + trx->fts_trx = fts_trx_create(trx); + } + + fts_trx_table_t ftt; + ftt.table = table; + ftt.fts_trx = trx->fts_trx; + + fts_add_doc_from_tuple(&ftt, doc_id, node->row); + } else { + /* Pass NULL for the columns affected, since an INSERT affects + all FTS indexes. */ + fts_trx_add_op(trx, table, doc_id, FTS_INSERT, NULL); + } } que_thr_stop_for_mysql_no_error(thr, trx); diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 7e49b2de1e9..99e83779dbd 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -752,6 +752,7 @@ row_purge_upd_exist_or_extern_func( mem_heap_t* heap; ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)); + ut_ad(!node->table->skip_alter_undo); if (node->rec_type == TRX_UNDO_UPD_DEL_REC || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { @@ -1035,6 +1036,7 @@ row_purge_record_func( bool purged = true; ut_ad(!node->found_clust); + ut_ad(!node->table->skip_alter_undo); clust_index = dict_table_get_first_index(node->table); diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc index f968fb780ae..c14af408dda 100644 --- a/storage/innobase/row/row0uins.cc +++ b/storage/innobase/row/row0uins.cc @@ -456,6 +456,7 @@ close_table: dict_table_close(node->table, dict_locked, FALSE); node->table = NULL; } else { + ut_ad(!node->table->skip_alter_undo); clust_index = dict_table_get_first_index(node->table); if (clust_index != NULL) { diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc index c452aa889c8..b4aec1f3c4d 100644 --- a/storage/innobase/row/row0umod.cc +++ b/storage/innobase/row/row0umod.cc @@ -1144,6 +1144,8 @@ row_undo_mod_parse_undo_rec( return; } + ut_ad(!node->table->skip_alter_undo); + if (UNIV_UNLIKELY(!fil_table_accessible(node->table))) { close_table: /* Normally, tables should not disappear or become diff --git a/storage/innobase/row/row0undo.cc b/storage/innobase/row/row0undo.cc index 91c0a5f50ae..2fcfecd5c11 100644 --- a/storage/innobase/row/row0undo.cc +++ b/storage/innobase/row/row0undo.cc @@ -172,6 +172,8 @@ row_undo_search_clust_to_pcur( ulint* offsets = offsets_; rec_offs_init(offsets_); + ut_ad(!node->table->skip_alter_undo); + mtr_start(&mtr); clust_index = dict_table_get_first_index(node->table); diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 203160b35d6..e204059dd04 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -581,6 +581,7 @@ row_upd_changes_field_size_or_external( ulint i; ut_ad(rec_offs_validate(NULL, index, offsets)); + ut_ad(!index->table->skip_alter_undo); n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { @@ -705,6 +706,7 @@ row_upd_rec_in_place( ulint i; ut_ad(rec_offs_validate(rec, index, offsets)); + ut_ad(!index->table->skip_alter_undo); if (rec_offs_comp(offsets)) { #ifdef UNIV_DEBUG @@ -1013,6 +1015,7 @@ row_upd_build_sec_rec_difference_binary( ut_ad(rec_offs_n_fields(offsets) == dtuple_get_n_fields(entry)); ut_ad(!rec_offs_any_extern(offsets)); ut_ad(!rec_offs_any_default(offsets)); + ut_ad(!index->table->skip_alter_undo); update = upd_create(dtuple_get_n_fields(entry), heap); @@ -1094,6 +1097,7 @@ row_upd_build_difference_binary( /* This function is used only for a clustered index */ ut_a(dict_index_is_clust(index)); + ut_ad(!index->table->skip_alter_undo); update = upd_create(n_fld + n_v_fld, heap); @@ -1356,6 +1360,8 @@ row_upd_index_replace_new_col_vals_index_pos( const upd_t* update, mem_heap_t* heap) { + ut_ad(!index->table->skip_alter_undo); + const page_size_t& page_size = dict_table_page_size(index->table); dtuple_set_info_bits(entry, update->info_bits); @@ -1410,6 +1416,8 @@ row_upd_index_replace_new_col_vals( = dict_table_get_first_index(index->table); const page_size_t& page_size = dict_table_page_size(index->table); + ut_ad(!index->table->skip_alter_undo); + dtuple_set_info_bits(entry, update->info_bits); for (i = 0; i < dict_index_get_n_fields(index); i++) { @@ -1484,6 +1492,8 @@ row_upd_replace_vcol( ulint i; ulint n_cols; + ut_ad(!table->skip_alter_undo); + n_cols = dtuple_get_n_v_fields(row); for (col_no = 0; col_no < n_cols; col_no++) { dfield_t* dfield; @@ -1705,6 +1715,7 @@ row_upd_changes_ord_field_binary_func( ut_ad(thr); ut_ad(thr->graph); ut_ad(thr->graph->trx); + ut_ad(!index->table->skip_alter_undo); n_unique = dict_index_get_n_unique(index); @@ -1964,6 +1975,8 @@ row_upd_changes_doc_id( dict_index_t* clust_index; fts_t* fts = table->fts; + ut_ad(!table->skip_alter_undo); + clust_index = dict_table_get_first_index(table); /* Convert from index-specific column number to table-global @@ -1986,6 +1999,8 @@ row_upd_changes_fts_column( dict_index_t* clust_index; fts_t* fts = table->fts; + ut_ad(!table->skip_alter_undo); + if (upd_fld_is_virtual_col(upd_field)) { col_no = upd_field->field_no; return(dict_table_is_fts_column(fts->indexes, col_no, true)); @@ -2836,6 +2851,7 @@ row_upd_clust_rec( ut_ad(node); ut_ad(dict_index_is_clust(index)); ut_ad(!thr_get_trx(thr)->in_rollback); + ut_ad(!node->table->skip_alter_undo); pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index 1a68e46361b..a9f4cef6579 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -2290,6 +2290,8 @@ trx_undo_prev_version_build( const bool is_temp = dict_table_is_temporary(index->table); rec_trx_id = row_get_rec_trx_id(rec, index, offsets); + ut_ad(!index->table->skip_alter_undo); + if (trx_undo_get_undo_rec( roll_ptr, is_temp, heap, rec_trx_id, index->table->name, &undo_rec)) { diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp index 0b0de7a41ed..6641ebda551 100644 --- a/storage/mroonga/ha_mroonga.cpp +++ b/storage/mroonga/ha_mroonga.cpp @@ -542,6 +542,16 @@ static const char *mrn_inspect_extra_function(enum ha_extra_function operation) inspected = "HA_EXTRA_DETACH_CHILDREN"; break; case HA_EXTRA_STARTING_ORDERED_INDEX_SCAN: + inspected = "HA_EXTRA_STARTING_ORDERED_INDEX_SCAN"; + break; + case HA_EXTRA_BEGIN_ALTER_COPY: + inspected = "HA_EXTRA_BEGIN_ALTER_COPY"; + break; + case HA_EXTRA_END_ALTER_COPY: + inspected = "HA_EXTRA_END_ALTER_COPY"; + break; + case HA_EXTRA_FAKE_START_STMT: + inspected = "HA_EXTRA_FAKE_START_STMT"; break; #ifdef MRN_HAVE_HA_EXTRA_EXPORT case HA_EXTRA_EXPORT: |