1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-29504/MDEV-29849 TRUNCATE breaks FOREIGN KEY locking

ha_innobase::referenced_by_foreign_key(): Protect the check with
dict_sys.freeze(), to prevent races with TRUNCATE TABLE.
The test innodb.instant_alter_crash has been adjusted for this
additional locking.

dict_table_is_referenced_by_foreign_key(): Removed (merged to
the only caller).

create_table_info_t::create_table(): Ignore missing indexes for
FOREIGN KEY constraints if foreign_key_checks=0.

create_table_info_t::create_table_update_dict(): Rewritten as
a static function. Do not return any error.

ha_innobase::create(): When trx!=nullptr and we are operating
on a persistent table, do not rollback, commit, or release the
data dictionary latch.

ha_innobase::truncate(): Protect the entire critical section
with an exclusive dict_sys.latch, so that
ha_innobase::referenced_by_foreign_key() on referenced tables
will return a consistent result. In case of a failure,
invoke dict_load_foreigns() to restore also any FOREIGN KEY
constraints.

ha_innobase::free_foreign_key_create_info(): Define inline.

lock_release(): Disregard innodb_evict_tables_on_commit_debug=ON
when dict_sys.locked() holds. It would hold when fts_load_stopword()
is invoked by create_table_info_t::create_table_update_dict().

dict_sys_t::locked(): Return whether the current thread is holding
the exclusive dict_sys.latch.

dict_sys_t::frozen_not_locked(): Return whether any thread is
holding a shared dict_sys.latch.

In the test main.mysql_upgrade, the InnoDB persistent statistics
will no longer be recalculated in ha_innobase::open() as part of
CHECK TABLE ... FOR UPGRADE. They were deleted earlier in the test.

Tested by: Matthias Leich
This commit is contained in:
Marko Mäkelä
2022-11-08 17:34:34 +02:00
parent f4519fb772
commit e572c745dc
18 changed files with 448 additions and 440 deletions

View File

@@ -1319,10 +1319,6 @@ partition p2008 values less than (2009)
); );
select length(table_name) from mysql.innodb_table_stats; select length(table_name) from mysql.innodb_table_stats;
length(table_name) length(table_name)
79
79
79
79
drop table extralongname_extralongname_extralongname_extralongname_ext; drop table extralongname_extralongname_extralongname_extralongname_ext;
# End of 10.0 tests # End of 10.0 tests
set sql_mode=default; set sql_mode=default;

View File

@@ -33,11 +33,19 @@ b bigint unsigned NOT NULL,
d1 date NOT NULL, d1 date NOT NULL,
PRIMARY KEY (b,d1) PRIMARY KEY (b,d1)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
DROP TABLE b;
set foreign_key_checks = 1;
CREATE TABLE b (
b bigint unsigned NOT NULL,
d1 date NOT NULL,
PRIMARY KEY (b,d1)
) ENGINE=InnoDB;
ERROR HY000: Can't create table `bug_fk`.`b` (errno: 150 "Foreign key constraint is incorrectly formed") ERROR HY000: Can't create table `bug_fk`.`b` (errno: 150 "Foreign key constraint is incorrectly formed")
show warnings; show warnings;
Level Code Message Level Code Message
Error 1005 Can't create table `bug_fk`.`b` (errno: 150 "Foreign key constraint is incorrectly formed") Error 1005 Can't create table `bug_fk`.`b` (errno: 150 "Foreign key constraint is incorrectly formed")
Warning 1215 Cannot add foreign key constraint for `b` Warning 1215 Cannot add foreign key constraint for `b`
set foreign_key_checks = 0;
DROP TABLE IF EXISTS d; DROP TABLE IF EXISTS d;
Warnings: Warnings:
Note 1051 Unknown table 'bug_fk.d' Note 1051 Unknown table 'bug_fk.d'

View File

@@ -2531,9 +2531,19 @@ disconnect b;
set foreign_key_checks=0; set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb; create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb; create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
set foreign_key_checks=1; set foreign_key_checks=1;
insert into t2 values (1,1);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`a`))
set foreign_key_checks=0;
drop table t1;
set foreign_key_checks=1;
insert into t2 values (1,1);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `t1` (`a`))
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
drop table t2; drop table t2;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
drop table t1;
set foreign_key_checks=0; set foreign_key_checks=0;
create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1; create table t1(a varchar(10) primary key) engine = innodb DEFAULT CHARSET=latin1;
create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8; create table t2 (a varchar(10), foreign key (a) references t1(a)) engine = innodb DEFAULT CHARSET=utf8;

View File

@@ -34,13 +34,15 @@ ROLLBACK;
InnoDB 0 transactions not purged InnoDB 0 transactions not purged
INSERT INTO t2 VALUES INSERT INTO t2 VALUES
(16,1551,'Omnium enim rerum'),(128,1571,' principia parva sunt'); (16,1551,'Omnium enim rerum'),(128,1571,' principia parva sunt');
BEGIN;
UPDATE t1 SET c2=c2+1;
connect ddl, localhost, root; connect ddl, localhost, root;
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever'; SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
ALTER TABLE t2 DROP COLUMN c3, ADD COLUMN c5 TEXT DEFAULT 'naturam abhorrere'; ALTER TABLE t2 DROP COLUMN c3, ADD COLUMN c5 TEXT DEFAULT 'naturam abhorrere';
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
UPDATE t1 SET c2=c2+1; COMMIT;
# Kill the server # Kill the server
disconnect ddl; disconnect ddl;
# restart # restart
@@ -61,6 +63,8 @@ DELETE FROM t2;
ROLLBACK; ROLLBACK;
InnoDB 0 transactions not purged InnoDB 0 transactions not purged
INSERT INTO t2 VALUES (64,42,'De finibus bonorum'), (347,33101,' et malorum'); INSERT INTO t2 VALUES (64,42,'De finibus bonorum'), (347,33101,' et malorum');
BEGIN;
DELETE FROM t1;
connect ddl, localhost, root; connect ddl, localhost, root;
ALTER TABLE t2 DROP COLUMN c3; ALTER TABLE t2 DROP COLUMN c3;
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever'; SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
@@ -68,7 +72,7 @@ ALTER TABLE t2 ADD COLUMN (c4 TEXT NOT NULL DEFAULT ' et malorum');
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
DELETE FROM t1; COMMIT;
# Kill the server # Kill the server
disconnect ddl; disconnect ddl;
# restart # restart
@@ -138,6 +142,8 @@ InnoDB 0 transactions not purged
# #
# MDEV-24323 Crash on recovery after kill during instant ADD COLUMN # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
# #
BEGIN;
INSERT INTO t1 VALUES(0,0);
connect ddl, localhost, root; connect ddl, localhost, root;
CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2)) CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
ENGINE=InnoDB; ENGINE=InnoDB;
@@ -147,7 +153,7 @@ ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
INSERT INTO t1 VALUES(0,0); COMMIT;
# Kill the server # Kill the server
disconnect ddl; disconnect ddl;
# restart # restart
@@ -183,13 +189,15 @@ DROP TABLE t2,t3;
# #
CREATE TABLE t2(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t2(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6); INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6);
BEGIN;
DELETE FROM t1;
connect ddl, localhost, root; connect ddl, localhost, root;
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever'; SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
ALTER TABLE t2 ADD COLUMN b TINYINT UNSIGNED NOT NULL DEFAULT 42 FIRST; ALTER TABLE t2 ADD COLUMN b TINYINT UNSIGNED NOT NULL DEFAULT 42 FIRST;
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
DELETE FROM t1; COMMIT;
# Kill the server # Kill the server
disconnect ddl; disconnect ddl;
# restart # restart

View File

@@ -80,9 +80,19 @@ SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY; ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY;
INSERT INTO t1 VALUES (1,1); INSERT INTO t1 VALUES (1,1);
LOCK TABLES t1 WRITE; LOCK TABLES t1 WRITE;
SET FOREIGN_KEY_CHECKS=1;
TRUNCATE t1; TRUNCATE t1;
ERROR HY000: Cannot add foreign key constraint for `t1` ERROR HY000: Cannot add foreign key constraint for `t1`
INSERT INTO t1 VALUES (2,2); INSERT INTO t1 VALUES (2,2);
ERROR HY000: Table 't1' was not locked with LOCK TABLES
SELECT * FROM t1;
pk a
1 1
UNLOCK TABLES;
INSERT INTO t1 VALUES (2,2);
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t1` (`a`))
SET FOREIGN_KEY_CHECKS=0;
INSERT INTO t1 VALUES (2,2);
SELECT * FROM t1; SELECT * FROM t1;
pk a pk a
1 1 1 1

View File

@@ -46,7 +46,15 @@ show create table c;
# #
# Note that column b has different type in parent table # Note that column b has different type in parent table
# #
--error 1005 CREATE TABLE b (
b bigint unsigned NOT NULL,
d1 date NOT NULL,
PRIMARY KEY (b,d1)
) ENGINE=InnoDB;
DROP TABLE b;
set foreign_key_checks = 1;
--error ER_CANT_CREATE_TABLE
CREATE TABLE b ( CREATE TABLE b (
b bigint unsigned NOT NULL, b bigint unsigned NOT NULL,
d1 date NOT NULL, d1 date NOT NULL,
@@ -54,6 +62,7 @@ CREATE TABLE b (
) ENGINE=InnoDB; ) ENGINE=InnoDB;
show warnings; show warnings;
set foreign_key_checks = 0;
DROP TABLE IF EXISTS d; DROP TABLE IF EXISTS d;

View File

@@ -1598,12 +1598,22 @@ disconnect b;
set foreign_key_checks=0; set foreign_key_checks=0;
create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb; create table t2 (a int primary key, b int, foreign key (b) references t1(a)) engine = innodb;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
set foreign_key_checks=1;
--error ER_NO_REFERENCED_ROW_2
insert into t2 values (1,1);
set foreign_key_checks=0;
drop table t1;
set foreign_key_checks=1;
--error ER_NO_REFERENCED_ROW_2
insert into t2 values (1,1);
# Embedded server doesn't chdir to data directory # Embedded server doesn't chdir to data directory
--replace_result $MYSQLTEST_VARDIR . master-data/ '' --replace_result $MYSQLTEST_VARDIR . master-data/ ''
--error ER_CANT_CREATE_TABLE --error ER_CANT_CREATE_TABLE
create table t1(a char(10) primary key, b varchar(20)) engine = innodb; create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
set foreign_key_checks=1;
drop table t2; drop table t2;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
drop table t1;
# test that FKs between different charsets are not accepted in CREATE even # test that FKs between different charsets are not accepted in CREATE even
# when f_k_c is 0 # when f_k_c is 0

View File

@@ -47,6 +47,9 @@ ROLLBACK;
INSERT INTO t2 VALUES INSERT INTO t2 VALUES
(16,1551,'Omnium enim rerum'),(128,1571,' principia parva sunt'); (16,1551,'Omnium enim rerum'),(128,1571,' principia parva sunt');
BEGIN;
UPDATE t1 SET c2=c2+1;
connect ddl, localhost, root; connect ddl, localhost, root;
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever'; SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
--send --send
@@ -55,7 +58,7 @@ ALTER TABLE t2 DROP COLUMN c3, ADD COLUMN c5 TEXT DEFAULT 'naturam abhorrere';
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
UPDATE t1 SET c2=c2+1; COMMIT;
--source include/kill_mysqld.inc --source include/kill_mysqld.inc
disconnect ddl; disconnect ddl;
@@ -73,6 +76,8 @@ ROLLBACK;
--source include/wait_all_purged.inc --source include/wait_all_purged.inc
INSERT INTO t2 VALUES (64,42,'De finibus bonorum'), (347,33101,' et malorum'); INSERT INTO t2 VALUES (64,42,'De finibus bonorum'), (347,33101,' et malorum');
BEGIN;
DELETE FROM t1;
connect ddl, localhost, root; connect ddl, localhost, root;
ALTER TABLE t2 DROP COLUMN c3; ALTER TABLE t2 DROP COLUMN c3;
@@ -83,7 +88,7 @@ ALTER TABLE t2 ADD COLUMN (c4 TEXT NOT NULL DEFAULT ' et malorum');
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
DELETE FROM t1; COMMIT;
--source include/kill_mysqld.inc --source include/kill_mysqld.inc
disconnect ddl; disconnect ddl;
@@ -177,6 +182,9 @@ DELETE FROM t2;
--echo # --echo #
--echo # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN --echo # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
--echo # --echo #
BEGIN;
INSERT INTO t1 VALUES(0,0);
connect ddl, localhost, root; connect ddl, localhost, root;
CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2)) CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
ENGINE=InnoDB; ENGINE=InnoDB;
@@ -189,7 +197,7 @@ ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
INSERT INTO t1 VALUES(0,0); COMMIT;
--source include/kill_mysqld.inc --source include/kill_mysqld.inc
disconnect ddl; disconnect ddl;
@@ -207,6 +215,9 @@ DROP TABLE t2,t3;
CREATE TABLE t2(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t2(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6); INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6);
BEGIN;
DELETE FROM t1;
connect ddl, localhost, root; connect ddl, localhost, root;
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever'; SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
--send --send
@@ -215,7 +226,7 @@ ALTER TABLE t2 ADD COLUMN b TINYINT UNSIGNED NOT NULL DEFAULT 42 FIRST;
connection default; connection default;
SET DEBUG_SYNC='now WAIT_FOR ddl'; SET DEBUG_SYNC='now WAIT_FOR ddl';
SET GLOBAL innodb_flush_log_at_trx_commit=1; SET GLOBAL innodb_flush_log_at_trx_commit=1;
DELETE FROM t1; COMMIT;
--source include/kill_mysqld.inc --source include/kill_mysqld.inc
disconnect ddl; disconnect ddl;

View File

@@ -92,8 +92,19 @@ SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY; ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY;
INSERT INTO t1 VALUES (1,1); INSERT INTO t1 VALUES (1,1);
LOCK TABLES t1 WRITE; LOCK TABLES t1 WRITE;
SET FOREIGN_KEY_CHECKS=1;
--error ER_CANNOT_ADD_FOREIGN --error ER_CANNOT_ADD_FOREIGN
TRUNCATE t1; TRUNCATE t1;
# Whether TRUNCATE succeeds or fails, it will reload FOREIGN KEY constraints.
# As a result, ha_innobase::referenced_by_foreign_key() will retun TRUE
# (for the self-referential key), and the statement will fail.
--error ER_TABLE_NOT_LOCKED
INSERT INTO t1 VALUES (2,2);
SELECT * FROM t1;
UNLOCK TABLES;
--error ER_NO_REFERENCED_ROW_2
INSERT INTO t1 VALUES (2,2);
SET FOREIGN_KEY_CHECKS=0;
INSERT INTO t1 VALUES (2,2); INSERT INTO t1 VALUES (2,2);
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;

View File

@@ -62,7 +62,6 @@ SET @saved_debug_dbug= @@debug_dbug;
CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB; CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB;
SET debug_dbug='+d,ib_create_table_fail_too_many_trx'; SET debug_dbug='+d,ib_create_table_fail_too_many_trx';
TRUNCATE t1; TRUNCATE t1;
ERROR HY000: Got error -1 "Internal error < 0 (Not system error)" from storage engine InnoDB
SET debug_dbug=@saved_debug_dbug; SET debug_dbug=@saved_debug_dbug;
DROP TABLE t1; DROP TABLE t1;
# End of 10.3 tests # End of 10.3 tests

View File

@@ -91,7 +91,6 @@ SET @saved_debug_dbug= @@debug_dbug;
CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB; CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB;
SET debug_dbug='+d,ib_create_table_fail_too_many_trx'; SET debug_dbug='+d,ib_create_table_fail_too_many_trx';
--error ER_GET_ERRNO
TRUNCATE t1; TRUNCATE t1;
SET debug_dbug=@saved_debug_dbug; SET debug_dbug=@saved_debug_dbug;
DROP TABLE t1; DROP TABLE t1;

View File

@@ -685,8 +685,7 @@ dict_acquire_mdl_shared(dict_table_t *table,
} }
else else
{ {
ut_ad(dict_sys.frozen()); ut_ad(dict_sys.frozen_not_locked());
ut_ad(!dict_sys.locked());
db_len= dict_get_db_name_len(table->name.m_name); db_len= dict_get_db_name_len(table->name.m_name);
} }
@@ -1003,7 +1002,7 @@ void dict_sys_t::lock_wait(SRW_LOCK_ARGS(const char *file, unsigned line))
latch_ex_wait_start.store(0, std::memory_order_relaxed); latch_ex_wait_start.store(0, std::memory_order_relaxed);
ut_ad(!latch_readers); ut_ad(!latch_readers);
ut_ad(!latch_ex); ut_ad(!latch_ex);
ut_d(latch_ex= true); ut_d(latch_ex= pthread_self());
return; return;
} }
@@ -1021,15 +1020,15 @@ void dict_sys_t::lock_wait(SRW_LOCK_ARGS(const char *file, unsigned line))
latch.wr_lock(SRW_LOCK_ARGS(file, line)); latch.wr_lock(SRW_LOCK_ARGS(file, line));
ut_ad(!latch_readers); ut_ad(!latch_readers);
ut_ad(!latch_ex); ut_ad(!latch_ex);
ut_d(latch_ex= true); ut_d(latch_ex= pthread_self());
} }
#ifdef UNIV_PFS_RWLOCK #ifdef UNIV_PFS_RWLOCK
ATTRIBUTE_NOINLINE void dict_sys_t::unlock() ATTRIBUTE_NOINLINE void dict_sys_t::unlock()
{ {
ut_ad(latch_ex); ut_ad(latch_ex == pthread_self());
ut_ad(!latch_readers); ut_ad(!latch_readers);
ut_d(latch_ex= false); ut_d(latch_ex= 0);
latch.wr_unlock(); latch.wr_unlock();
} }
@@ -2749,17 +2748,6 @@ dict_index_build_internal_fts(
} }
/*====================== FOREIGN KEY PROCESSING ========================*/ /*====================== FOREIGN KEY PROCESSING ========================*/
/*********************************************************************//**
Checks if a table is referenced by foreign keys.
@return TRUE if table is referenced by a foreign key */
ibool
dict_table_is_referenced_by_foreign_key(
/*====================================*/
const dict_table_t* table) /*!< in: InnoDB table */
{
return(!table->referenced_set.empty());
}
/**********************************************************************//** /**********************************************************************//**
Removes a foreign constraint struct from the dictionary cache. */ Removes a foreign constraint struct from the dictionary cache. */
void void

View File

@@ -11473,6 +11473,8 @@ innobase_fts_load_stopword(
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
THD* thd) /*!< in: current thread */ THD* thd) /*!< in: current thread */
{ {
ut_ad(dict_sys.locked());
const char *stopword_table= THDVAR(thd, ft_user_stopword_table); const char *stopword_table= THDVAR(thd, ft_user_stopword_table);
if (!stopword_table) if (!stopword_table)
{ {
@@ -11482,9 +11484,11 @@ innobase_fts_load_stopword(
mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_unlock(&LOCK_global_system_variables);
} }
return !high_level_read_only && table->fts->dict_locked= true;
fts_load_stopword(table, trx, stopword_table, bool success= fts_load_stopword(table, trx, stopword_table,
THDVAR(thd, ft_enable_stopword), false); THDVAR(thd, ft_enable_stopword), false);
table->fts->dict_locked= false;
return success;
} }
/** Parse the table name into normal name and remote path if needed. /** Parse the table name into normal name and remote path if needed.
@@ -12820,15 +12824,18 @@ int create_table_info_t::create_table(bool create_fk)
dberr_t err = create_fk ? create_foreign_keys() : DB_SUCCESS; dberr_t err = create_fk ? create_foreign_keys() : DB_SUCCESS;
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
const dict_err_ignore_t ignore_err = m_trx->check_foreigns
? DICT_ERR_IGNORE_NONE : DICT_ERR_IGNORE_FK_NOKEY;
/* Check that also referencing constraints are ok */ /* Check that also referencing constraints are ok */
dict_names_t fk_tables; dict_names_t fk_tables;
err = dict_load_foreigns(m_table_name, nullptr, err = dict_load_foreigns(m_table_name, nullptr,
m_trx->id, true, m_trx->id, true,
DICT_ERR_IGNORE_NONE, fk_tables); ignore_err, fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) { while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_sys.load_table( dict_sys.load_table(
{fk_tables.front(), strlen(fk_tables.front())}, {fk_tables.front(), strlen(fk_tables.front())},
DICT_ERR_IGNORE_NONE); ignore_err);
fk_tables.pop_front(); fk_tables.pop_front();
} }
} }
@@ -13109,96 +13116,59 @@ bool create_table_info_t::row_size_is_acceptable(
return true; return true;
} }
/** Update a new table in an InnoDB database. void create_table_info_t::create_table_update_dict(dict_table_t *table,
@return error number */ THD *thd,
int const HA_CREATE_INFO &info,
create_table_info_t::create_table_update_dict() const TABLE &t)
{ {
dict_table_t* innobase_table; ut_ad(dict_sys.locked());
DBUG_ENTER("create_table_update_dict"); DBUG_ASSERT(table->get_ref_count());
if (table->fts)
{
if (!table->fts_doc_id_index)
table->fts_doc_id_index=
dict_table_get_index_on_name(table, FTS_DOC_ID_INDEX_NAME);
else
DBUG_ASSERT(table->fts_doc_id_index ==
dict_table_get_index_on_name(table, FTS_DOC_ID_INDEX_NAME));
}
innobase_table = dict_table_open_on_name( DBUG_ASSERT(!table->fts == !table->fts_doc_id_index);
m_table_name, false, DICT_ERR_IGNORE_NONE);
DBUG_ASSERT(innobase_table != 0); innobase_copy_frm_flags_from_create_info(table, &info);
if (innobase_table->fts != NULL) {
if (innobase_table->fts_doc_id_index == NULL) {
innobase_table->fts_doc_id_index
= dict_table_get_index_on_name(
innobase_table, FTS_DOC_ID_INDEX_NAME);
DBUG_ASSERT(innobase_table->fts_doc_id_index != NULL);
} else {
DBUG_ASSERT(innobase_table->fts_doc_id_index
== dict_table_get_index_on_name(
innobase_table,
FTS_DOC_ID_INDEX_NAME));
}
}
DBUG_ASSERT((innobase_table->fts == NULL) /* Load server stopword into FTS cache */
== (innobase_table->fts_doc_id_index == NULL)); if (table->flags2 & DICT_TF2_FTS &&
innobase_fts_load_stopword(table, nullptr, thd))
fts_optimize_add_table(table);
innobase_copy_frm_flags_from_create_info(innobase_table, m_create_info); if (const Field *ai = t.found_next_number_field)
{
ut_ad(ai->stored_in_db());
ib_uint64_t autoinc= info.auto_increment_value;
if (autoinc == 0)
autoinc= 1;
dict_stats_update(innobase_table, DICT_STATS_EMPTY_TABLE); table->autoinc_mutex.wr_lock();
dict_table_autoinc_initialize(table, autoinc);
/* Load server stopword into FTS cache */ if (!table->is_temporary())
if (m_flags2 & DICT_TF2_FTS) { {
if (!innobase_fts_load_stopword(innobase_table, NULL, m_thd)) { const unsigned col_no= innodb_col_no(ai);
innobase_table->release(); table->persistent_autoinc= static_cast<uint16_t>
DBUG_RETURN(-1); (dict_table_get_nth_col_pos(table, col_no, nullptr) + 1) &
} dict_index_t::MAX_N_FIELDS;
/* Persist the "last used" value, which typically is AUTO_INCREMENT - 1.
In btr_create(), the value 0 was already written. */
if (--autoinc)
btr_write_autoinc(dict_table_get_first_index(table), autoinc);
}
dict_sys.lock(SRW_LOCK_CALL); table->autoinc_mutex.wr_unlock();
fts_optimize_add_table(innobase_table); }
dict_sys.unlock();
}
if (const Field* ai = m_form->found_next_number_field) { innobase_parse_hint_from_comment(thd, table, t.s);
ut_ad(ai->stored_in_db());
ib_uint64_t autoinc = m_create_info->auto_increment_value;
if (autoinc == 0) {
autoinc = 1;
}
innobase_table->autoinc_mutex.wr_lock();
dict_table_autoinc_initialize(innobase_table, autoinc);
if (innobase_table->is_temporary()) {
/* AUTO_INCREMENT is not persistent for
TEMPORARY TABLE. Temporary tables are never
evicted. Keep the counter in memory only. */
} else {
const unsigned col_no = innodb_col_no(ai);
innobase_table->persistent_autoinc
= static_cast<uint16_t>(
dict_table_get_nth_col_pos(
innobase_table, col_no, NULL)
+ 1)
& dict_index_t::MAX_N_FIELDS;
/* Persist the "last used" value, which
typically is AUTO_INCREMENT - 1.
In btr_create(), the value 0 was already written. */
if (--autoinc) {
btr_write_autoinc(
dict_table_get_first_index(
innobase_table),
autoinc);
}
}
innobase_table->autoinc_mutex.wr_unlock();
}
innobase_parse_hint_from_comment(m_thd, innobase_table, m_form->s);
dict_table_close(innobase_table);
DBUG_RETURN(0);
} }
/** Allocate a new trx. */ /** Allocate a new trx. */
@@ -13215,89 +13185,80 @@ create_table_info_t::allocate_trx()
@param[in] create_info Create info (including create statement string). @param[in] create_info Create info (including create statement string).
@param[in] file_per_table whether to create .ibd file @param[in] file_per_table whether to create .ibd file
@param[in,out] trx dictionary transaction, or NULL to create new @param[in,out] trx dictionary transaction, or NULL to create new
@return 0 if success else error number. */ @return error code
inline int @retval 0 on success */
ha_innobase::create( int
const char* name, ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info,
TABLE* form, bool file_per_table, trx_t *trx= nullptr)
HA_CREATE_INFO* create_info,
bool file_per_table,
trx_t* trx)
{ {
char norm_name[FN_REFLEN]; /* {database}/{tablename} */ char norm_name[FN_REFLEN]; /* {database}/{tablename} */
char remote_path[FN_REFLEN]; /* Absolute path of table */ char remote_path[FN_REFLEN]; /* Absolute path of table */
DBUG_ENTER("ha_innobase::create"); DBUG_ENTER("ha_innobase::create");
DBUG_ASSERT(form->s == table_share);
DBUG_ASSERT(table_share->table_type == TABLE_TYPE_SEQUENCE ||
table_share->table_type == TABLE_TYPE_NORMAL);
DBUG_ASSERT(form->s == table_share); create_table_info_t info(ha_thd(), form, create_info, norm_name,
DBUG_ASSERT(table_share->table_type == TABLE_TYPE_SEQUENCE remote_path, file_per_table, trx);
|| table_share->table_type == TABLE_TYPE_NORMAL);
create_table_info_t info(ha_thd(), int error= info.initialize();
form, if (!error)
create_info, error= info.prepare_create_table(name, !trx);
norm_name, if (error)
remote_path, DBUG_RETURN(error);
file_per_table, trx);
{ const bool own_trx= !trx;
int error = info.initialize(); if (own_trx)
if (!error) { {
error = info.prepare_create_table(name, !trx); info.allocate_trx();
} trx= info.trx();
if (error) { DBUG_ASSERT(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
if (trx) {
trx_rollback_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx);
}
DBUG_RETURN(error);
}
}
const bool own_trx = !trx; if (!(info.flags2() & DICT_TF2_TEMPORARY))
int error = 0; {
trx_start_for_ddl(trx);
if (dberr_t err= lock_sys_tables(trx))
error= convert_error_code_to_mysql(err, 0, nullptr);
}
row_mysql_lock_data_dictionary(trx);
}
if (own_trx) { if (!error)
info.allocate_trx(); error= info.create_table(own_trx);
trx = info.trx();
DBUG_ASSERT(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
}
if (own_trx && !(info.flags2() & DICT_TF2_TEMPORARY)) {
trx_start_for_ddl(trx);
if (dberr_t err = lock_sys_tables(trx)) {
error = convert_error_code_to_mysql(err, 0, nullptr);
}
}
if (own_trx) {
row_mysql_lock_data_dictionary(trx);
}
if (!error) { if (own_trx || (info.flags2() & DICT_TF2_TEMPORARY))
error = info.create_table(own_trx); {
} if (error)
trx_rollback_for_mysql(trx);
else
{
std::vector<pfs_os_file_t> deleted;
trx->commit(deleted);
ut_ad(deleted.empty());
info.table()->acquire();
info.create_table_update_dict(info.table(), info.thd(),
*create_info, *form);
}
if (error) { if (own_trx)
/* Rollback will drop the being-created table. */ {
trx_rollback_for_mysql(trx); row_mysql_unlock_data_dictionary(trx);
row_mysql_unlock_data_dictionary(trx);
} else {
/* When this is invoked as part of ha_innobase::truncate(),
the old copy of the table will be deleted here. */
std::vector<pfs_os_file_t> deleted;
trx->commit(deleted);
row_mysql_unlock_data_dictionary(trx);
for (pfs_os_file_t d : deleted) os_file_close(d);
error = info.create_table_update_dict();
if (!(info.flags2() & DICT_TF2_TEMPORARY)) {
log_write_up_to(trx->commit_lsn, true);
}
}
if (own_trx) { if (!error)
trx->free(); {
} dict_stats_update(info.table(), DICT_STATS_EMPTY_TABLE);
if (!info.table()->is_temporary())
log_write_up_to(trx->commit_lsn, true);
info.table()->release();
}
trx->free();
}
}
else if (!error && m_prebuilt)
m_prebuilt->table= info.table();
DBUG_RETURN(error); DBUG_RETURN(error);
} }
/** Create a new table to an InnoDB database. /** Create a new table to an InnoDB database.
@@ -13305,13 +13266,10 @@ ha_innobase::create(
@param[in] form Table format; columns and index information. @param[in] form Table format; columns and index information.
@param[in] create_info Create info (including create statement string). @param[in] create_info Create info (including create statement string).
@return 0 if success else error number. */ @return 0 if success else error number. */
int int ha_innobase::create(const char *name, TABLE *form,
ha_innobase::create( HA_CREATE_INFO *create_info)
const char* name,
TABLE* form,
HA_CREATE_INFO* create_info)
{ {
return create(name, form, create_info, srv_file_per_table); return create(name, form, create_info, srv_file_per_table);
} }
/*****************************************************************//** /*****************************************************************//**
@@ -13840,229 +13798,247 @@ static dberr_t innobase_rename_table(trx_t *trx, const char *from,
@retval 0 on success */ @retval 0 on success */
int ha_innobase::truncate() int ha_innobase::truncate()
{ {
DBUG_ENTER("ha_innobase::truncate"); DBUG_ENTER("ha_innobase::truncate");
update_thd(); update_thd();
if (is_read_only()) { if (is_read_only())
DBUG_RETURN(HA_ERR_TABLE_READONLY); DBUG_RETURN(HA_ERR_TABLE_READONLY);
}
HA_CREATE_INFO info; HA_CREATE_INFO info;
dict_table_t* ib_table = m_prebuilt->table; dict_table_t *ib_table= m_prebuilt->table;
info.init(); info.init();
update_create_info_from_table(&info, table); update_create_info_from_table(&info, table);
switch (dict_tf_get_rec_format(ib_table->flags)) { switch (dict_tf_get_rec_format(ib_table->flags)) {
case REC_FORMAT_REDUNDANT: case REC_FORMAT_REDUNDANT:
info.row_type = ROW_TYPE_REDUNDANT; info.row_type= ROW_TYPE_REDUNDANT;
break; break;
case REC_FORMAT_COMPACT: case REC_FORMAT_COMPACT:
info.row_type = ROW_TYPE_COMPACT; info.row_type= ROW_TYPE_COMPACT;
break; break;
case REC_FORMAT_COMPRESSED: case REC_FORMAT_COMPRESSED:
info.row_type = ROW_TYPE_COMPRESSED; info.row_type= ROW_TYPE_COMPRESSED;
break; break;
case REC_FORMAT_DYNAMIC: case REC_FORMAT_DYNAMIC:
info.row_type = ROW_TYPE_DYNAMIC; info.row_type= ROW_TYPE_DYNAMIC;
break; break;
} }
const auto stored_lock = m_prebuilt->stored_select_lock_type; const auto stored_lock= m_prebuilt->stored_select_lock_type;
trx_t* trx = innobase_trx_allocate(m_user_thd); trx_t *trx= innobase_trx_allocate(m_user_thd);
trx_start_for_ddl(trx); trx_start_for_ddl(trx);
if (ib_table->is_temporary()) { if (ib_table->is_temporary())
info.options|= HA_LEX_CREATE_TMP_TABLE; {
btr_drop_temporary_table(*ib_table); info.options|= HA_LEX_CREATE_TMP_TABLE;
m_prebuilt->table = nullptr; btr_drop_temporary_table(*ib_table);
row_prebuilt_free(m_prebuilt); m_prebuilt->table= nullptr;
m_prebuilt = nullptr; row_prebuilt_free(m_prebuilt);
my_free(m_upd_buf); m_prebuilt= nullptr;
m_upd_buf = nullptr; my_free(m_upd_buf);
m_upd_buf_size = 0; m_upd_buf= nullptr;
m_upd_buf_size= 0;
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
ib_table->release(); ib_table->release();
dict_sys.remove(ib_table, false, true); dict_sys.remove(ib_table, false, true);
int err= create(ib_table->name.m_name, table, &info, true, trx);
row_mysql_unlock_data_dictionary(trx);
int err = create(ib_table->name.m_name, table, &info, true, ut_ad(!err);
trx); if (!err)
ut_ad(!err); {
if (!err) { err= open(ib_table->name.m_name, 0, 0);
err = open(ib_table->name.m_name, 0, 0); m_prebuilt->table->release();
m_prebuilt->stored_select_lock_type = stored_lock; m_prebuilt->stored_select_lock_type= stored_lock;
} }
trx->free(); trx->free();
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (UT_LIST_GET_LEN(ib_table->freed_indexes)) { if (UT_LIST_GET_LEN(ib_table->freed_indexes))
ib_table->vc_templ = nullptr; {
ib_table->id = 0; ib_table->vc_templ= nullptr;
DBUG_RETURN(err); ib_table->id= 0;
} }
else
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
dict_mem_table_free(ib_table);
dict_mem_table_free(ib_table); DBUG_RETURN(err);
DBUG_RETURN(err); }
}
mem_heap_t* heap = mem_heap_create(1000); mem_heap_t *heap= mem_heap_create(1000);
dict_get_and_save_data_dir_path(ib_table); dict_get_and_save_data_dir_path(ib_table);
info.data_file_name = ib_table->data_dir_path; info.data_file_name= ib_table->data_dir_path;
const char* temp_name = dict_mem_create_temporary_tablename( const char *temp_name=
heap, ib_table->name.m_name, ib_table->id); dict_mem_create_temporary_tablename(heap,
const char* name = mem_heap_strdup(heap, ib_table->name.m_name); ib_table->name.m_name, ib_table->id);
const char *name= mem_heap_strdup(heap, ib_table->name.m_name);
dict_table_t *table_stats = nullptr, *index_stats = nullptr; dict_table_t *table_stats = nullptr, *index_stats = nullptr;
MDL_ticket *mdl_table = nullptr, *mdl_index = nullptr; MDL_ticket *mdl_table = nullptr, *mdl_index = nullptr;
dberr_t error = DB_SUCCESS; dberr_t error= DB_SUCCESS;
dict_sys.freeze(SRW_LOCK_CALL); dict_sys.freeze(SRW_LOCK_CALL);
for (const dict_foreign_t* f : ib_table->referenced_set) { for (const dict_foreign_t *f : ib_table->referenced_set)
if (dict_table_t* child = f->foreign_table) { if (dict_table_t *child= f->foreign_table)
error = lock_table_for_trx(child, trx, LOCK_X); if ((error= lock_table_for_trx(child, trx, LOCK_X)) != DB_SUCCESS)
if (error != DB_SUCCESS) { break;
break; dict_sys.unfreeze();
}
}
}
dict_sys.unfreeze();
if (error == DB_SUCCESS) { if (error == DB_SUCCESS)
error = lock_table_for_trx(ib_table, trx, LOCK_X); error= lock_table_for_trx(ib_table, trx, LOCK_X);
}
const bool fts = error == DB_SUCCESS const bool fts= error == DB_SUCCESS &&
&& ib_table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS); ib_table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS);
if (fts) { if (fts)
fts_optimize_remove_table(ib_table); {
purge_sys.stop_FTS(*ib_table); fts_optimize_remove_table(ib_table);
error = fts_lock_tables(trx, *ib_table); purge_sys.stop_FTS(*ib_table);
} error= fts_lock_tables(trx, *ib_table);
}
/* Wait for purge threads to stop using the table. */ /* Wait for purge threads to stop using the table. */
for (uint n = 15; ib_table->get_ref_count() > 1; ) { for (uint n = 15; ib_table->get_ref_count() > 1; )
if (!--n) { {
error = DB_LOCK_WAIT_TIMEOUT; if (!--n)
break; {
} error= DB_LOCK_WAIT_TIMEOUT;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
std::this_thread::sleep_for(std::chrono::milliseconds(50)); if (error == DB_SUCCESS && dict_stats_is_persistent_enabled(ib_table) &&
} !ib_table->is_stats_table())
{
table_stats= dict_table_open_on_name(TABLE_STATS_NAME, false,
DICT_ERR_IGNORE_NONE);
if (table_stats)
{
dict_sys.freeze(SRW_LOCK_CALL);
table_stats= dict_acquire_mdl_shared<false>(table_stats, m_user_thd,
&mdl_table);
dict_sys.unfreeze();
}
index_stats= dict_table_open_on_name(INDEX_STATS_NAME, false,
DICT_ERR_IGNORE_NONE);
if (index_stats)
{
dict_sys.freeze(SRW_LOCK_CALL);
index_stats= dict_acquire_mdl_shared<false>(index_stats, m_user_thd,
&mdl_index);
dict_sys.unfreeze();
}
if (error == DB_SUCCESS && dict_stats_is_persistent_enabled(ib_table) if (table_stats && index_stats &&
&& !ib_table->is_stats_table()) { !strcmp(table_stats->name.m_name, TABLE_STATS_NAME) &&
table_stats= dict_table_open_on_name(TABLE_STATS_NAME, false, !strcmp(index_stats->name.m_name, INDEX_STATS_NAME) &&
DICT_ERR_IGNORE_NONE); !(error= lock_table_for_trx(table_stats, trx, LOCK_X)))
if (table_stats) { error= lock_table_for_trx(index_stats, trx, LOCK_X);
dict_sys.freeze(SRW_LOCK_CALL); }
table_stats = dict_acquire_mdl_shared<false>(
table_stats, m_user_thd, &mdl_table);
dict_sys.unfreeze();
}
index_stats = dict_table_open_on_name(INDEX_STATS_NAME, false,
DICT_ERR_IGNORE_NONE);
if (index_stats) {
dict_sys.freeze(SRW_LOCK_CALL);
index_stats = dict_acquire_mdl_shared<false>(
index_stats, m_user_thd, &mdl_index);
dict_sys.unfreeze();
}
if (table_stats && index_stats if (error == DB_SUCCESS)
&& !strcmp(table_stats->name.m_name, TABLE_STATS_NAME) error= lock_sys_tables(trx);
&& !strcmp(index_stats->name.m_name, INDEX_STATS_NAME) &&
!(error = lock_table_for_trx(table_stats, trx, LOCK_X))) {
error = lock_table_for_trx(index_stats, trx, LOCK_X);
}
}
if (error == DB_SUCCESS) { std::vector<pfs_os_file_t> deleted;
error = lock_sys_tables(trx);
}
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
if (error == DB_SUCCESS) { if (error == DB_SUCCESS)
error = innobase_rename_table(trx, ib_table->name.m_name, {
temp_name, false); error= innobase_rename_table(trx, ib_table->name.m_name, temp_name, false);
if (error == DB_SUCCESS)
error= trx->drop_table(*ib_table);
}
if (error == DB_SUCCESS) { int err = convert_error_code_to_mysql(error, ib_table->flags, m_user_thd);
error = trx->drop_table(*ib_table); const auto update_time = ib_table->update_time;
}
}
int err = convert_error_code_to_mysql(error, ib_table->flags, if (err)
m_user_thd); {
if (err) { trx_rollback_for_mysql(trx);
trx_rollback_for_mysql(trx); if (fts)
if (fts) { fts_optimize_add_table(ib_table);
fts_optimize_add_table(ib_table); }
purge_sys.resume_FTS(); else
} {
row_mysql_unlock_data_dictionary(trx); const auto def_trx_id= ib_table->def_trx_id;
} else { ib_table->release();
const auto update_time = ib_table->update_time; m_prebuilt->table= nullptr;
const auto stored_lock = m_prebuilt->stored_select_lock_type;
const auto def_trx_id = ib_table->def_trx_id;
ib_table->release();
m_prebuilt->table = nullptr;
err = create(name, table, &info, err= create(name, table, &info, dict_table_is_file_per_table(ib_table),
dict_table_is_file_per_table(ib_table), trx); trx);
/* On success, create() durably committed trx. */ if (!err)
if (fts) { {
purge_sys.resume_FTS(); m_prebuilt->table->acquire();
} create_table_info_t::create_table_update_dict(m_prebuilt->table,
m_user_thd, info, *table);
trx->commit(deleted);
}
else
{
trx_rollback_for_mysql(trx);
m_prebuilt->table= dict_table_open_on_name(name, true,
DICT_ERR_IGNORE_FK_NOKEY);
m_prebuilt->table->def_trx_id= def_trx_id;
}
dict_names_t fk_tables;
dict_load_foreigns(m_prebuilt->table->name.m_name, nullptr, 1, true,
DICT_ERR_IGNORE_FK_NOKEY, fk_tables);
for (const char *f : fk_tables)
dict_sys.load_table({f, strlen(f)});
}
if (err) { if (fts)
reload: purge_sys.resume_FTS();
m_prebuilt->table = dict_table_open_on_name(
name, false, DICT_ERR_IGNORE_NONE);
m_prebuilt->table->def_trx_id = def_trx_id;
} else {
row_prebuilt_t* prebuilt = m_prebuilt;
uchar* upd_buf = m_upd_buf;
ulint upd_buf_size = m_upd_buf_size;
/* Mimic ha_innobase::close(). */
m_prebuilt = nullptr;
m_upd_buf = nullptr;
m_upd_buf_size = 0;
err = open(name, 0, 0); row_mysql_unlock_data_dictionary(trx);
for (pfs_os_file_t d : deleted) os_file_close(d);
if (!err) { if (!err)
m_prebuilt->stored_select_lock_type {
= stored_lock; dict_stats_update(m_prebuilt->table, DICT_STATS_EMPTY_TABLE);
m_prebuilt->table->update_time = update_time; log_write_up_to(trx->commit_lsn, true);
row_prebuilt_free(prebuilt); row_prebuilt_t *prebuilt= m_prebuilt;
my_free(upd_buf); uchar *upd_buf= m_upd_buf;
} else { ulint upd_buf_size= m_upd_buf_size;
/* Revert to the old table. */ /* Mimic ha_innobase::close(). */
m_prebuilt = prebuilt; m_prebuilt= nullptr;
m_upd_buf = upd_buf; m_upd_buf= nullptr;
m_upd_buf_size = upd_buf_size; m_upd_buf_size= 0;
goto reload;
}
}
}
trx->free(); err= open(name, 0, 0);
if (!err)
{
m_prebuilt->stored_select_lock_type= stored_lock;
m_prebuilt->table->update_time= update_time;
row_prebuilt_free(prebuilt);
my_free(upd_buf);
}
else
{
/* Revert to the old table. */
m_prebuilt= prebuilt;
m_upd_buf= upd_buf;
m_upd_buf_size= upd_buf_size;
}
}
mem_heap_free(heap); trx->free();
if (table_stats) { mem_heap_free(heap);
dict_table_close(table_stats, false, m_user_thd, mdl_table);
}
if (index_stats) {
dict_table_close(index_stats, false, m_user_thd, mdl_index);
}
DBUG_RETURN(err); if (table_stats)
dict_table_close(table_stats, false, m_user_thd, mdl_table);
if (index_stats)
dict_table_close(index_stats, false, m_user_thd, mdl_index);
DBUG_RETURN(err);
} }
/*********************************************************************//** /*********************************************************************//**
@@ -15656,30 +15632,12 @@ delete is then allowed internally to resolve a duplicate key conflict in
REPLACE, not an update. REPLACE, not an update.
@return > 0 if referenced by a FOREIGN KEY */ @return > 0 if referenced by a FOREIGN KEY */
uint uint ha_innobase::referenced_by_foreign_key()
ha_innobase::referenced_by_foreign_key(void)
/*========================================*/
{ {
if (dict_table_is_referenced_by_foreign_key(m_prebuilt->table)) { dict_sys.freeze(SRW_LOCK_CALL);
const bool empty= m_prebuilt->table->referenced_set.empty();
return(1); dict_sys.unfreeze();
} return !empty;
return(0);
}
/*******************************************************************//**
Frees the foreign key create info for a table stored in InnoDB, if it is
non-NULL. */
void
ha_innobase::free_foreign_key_create_info(
/*======================================*/
char* str) /*!< in, own: create info string to free */
{
if (str != NULL) {
my_free(str);
}
} }
/*******************************************************************//** /*******************************************************************//**

View File

@@ -190,12 +190,12 @@ public:
void update_create_info(HA_CREATE_INFO* create_info) override; void update_create_info(HA_CREATE_INFO* create_info) override;
inline int create( int create(
const char* name, const char* name,
TABLE* form, TABLE* form,
HA_CREATE_INFO* create_info, HA_CREATE_INFO* create_info,
bool file_per_table, bool file_per_table,
trx_t* trx = NULL); trx_t* trx);
int create( int create(
const char* name, const char* name,
@@ -225,7 +225,7 @@ public:
uint referenced_by_foreign_key() override; uint referenced_by_foreign_key() override;
void free_foreign_key_create_info(char* str) override; void free_foreign_key_create_info(char* str) override { my_free(str); }
uint lock_count(void) const override; uint lock_count(void) const override;
@@ -639,8 +639,9 @@ public:
@param create_fk whether to add FOREIGN KEY constraints */ @param create_fk whether to add FOREIGN KEY constraints */
int create_table(bool create_fk = true); int create_table(bool create_fk = true);
/** Update the internal data dictionary. */ static void create_table_update_dict(dict_table_t* table, THD* thd,
int create_table_update_dict(); const HA_CREATE_INFO& info,
const TABLE& t);
/** Validates the create options. Checks that the options /** Validates the create options. Checks that the options
KEY_BLOCK_SIZE, ROW_FORMAT, DATA DIRECTORY, TEMPORARY & TABLESPACE KEY_BLOCK_SIZE, ROW_FORMAT, DATA DIRECTORY, TEMPORARY & TABLESPACE
@@ -700,12 +701,13 @@ public:
trx_t* trx() const trx_t* trx() const
{ return(m_trx); } { return(m_trx); }
/** Return table name. */ /** @return table name */
const char* table_name() const const char* table_name() const { return(m_table_name); }
{ return(m_table_name); }
THD* thd() const /** @return the created table */
{ return(m_thd); } dict_table_t *table() const { return m_table; }
THD* thd() const { return(m_thd); }
private: private:
/** Parses the table name into normal name and either temp path or /** Parses the table name into normal name and either temp path or

View File

@@ -7286,13 +7286,10 @@ error_handling_drop_uncached:
goto error_handling; goto error_handling;
} }
ctx->new_table->fts->dict_locked = true;
error = innobase_fts_load_stopword( error = innobase_fts_load_stopword(
ctx->new_table, ctx->trx, ctx->new_table, ctx->trx,
ctx->prebuilt->trx->mysql_thd) ctx->prebuilt->trx->mysql_thd)
? DB_SUCCESS : DB_ERROR; ? DB_SUCCESS : DB_ERROR;
ctx->new_table->fts->dict_locked = false;
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
goto error_handling; goto error_handling;
@@ -9882,7 +9879,7 @@ innobase_update_foreign_cache(
err = dict_load_foreigns(user_table->name.m_name, err = dict_load_foreigns(user_table->name.m_name,
ctx->col_names, 1, true, ctx->col_names, 1, true,
DICT_ERR_IGNORE_NONE, DICT_ERR_IGNORE_FK_NOKEY,
fk_tables); fk_tables);
if (err == DB_CANNOT_ADD_CONSTRAINT) { if (err == DB_CANNOT_ADD_CONSTRAINT) {

View File

@@ -421,14 +421,6 @@ dict_foreign_add_to_cache(
dict_err_ignore_t ignore_err) dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */ /*!< in: error to be ignored */
MY_ATTRIBUTE((nonnull(1), warn_unused_result)); MY_ATTRIBUTE((nonnull(1), warn_unused_result));
/*********************************************************************//**
Checks if a table is referenced by foreign keys.
@return TRUE if table is referenced by a foreign key */
ibool
dict_table_is_referenced_by_foreign_key(
/*====================================*/
const dict_table_t* table) /*!< in: InnoDB table */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/**********************************************************************//** /**********************************************************************//**
Replace the index passed in with another equivalent index in the Replace the index passed in with another equivalent index in the
foreign key lists of the table. foreign key lists of the table.
@@ -1329,7 +1321,7 @@ class dict_sys_t
alignas(CPU_LEVEL1_DCACHE_LINESIZE) srw_lock latch; alignas(CPU_LEVEL1_DCACHE_LINESIZE) srw_lock latch;
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/** whether latch is being held in exclusive mode (by any thread) */ /** whether latch is being held in exclusive mode (by any thread) */
bool latch_ex; Atomic_relaxed<pthread_t> latch_ex;
/** number of S-latch holders */ /** number of S-latch holders */
Atomic_counter<uint32_t> latch_readers; Atomic_counter<uint32_t> latch_readers;
#endif #endif
@@ -1503,11 +1495,12 @@ public:
/** @return whether any thread (not necessarily the current thread) /** @return whether any thread (not necessarily the current thread)
is holding the latch; that is, this check may return false is holding the latch; that is, this check may return false
positives */ positives */
bool frozen() const { return latch_readers || locked(); } bool frozen() const { return latch_readers || latch_ex; }
/** @return whether any thread (not necessarily the current thread) /** @return whether any thread (not necessarily the current thread)
is holding the exclusive latch; that is, this check may return false is holding a shared latch */
positives */ bool frozen_not_locked() const { return latch_readers; }
bool locked() const { return latch_ex; } /** @return whether the current thread holds the exclusive latch */
bool locked() const { return latch_ex == pthread_self(); }
#endif #endif
private: private:
/** Acquire the exclusive latch */ /** Acquire the exclusive latch */
@@ -1526,7 +1519,7 @@ public:
{ {
ut_ad(!latch_readers); ut_ad(!latch_readers);
ut_ad(!latch_ex); ut_ad(!latch_ex);
ut_d(latch_ex= true); ut_d(latch_ex= pthread_self());
} }
else else
lock_wait(SRW_LOCK_ARGS(file, line)); lock_wait(SRW_LOCK_ARGS(file, line));
@@ -1543,9 +1536,9 @@ public:
/** Unlock the data dictionary cache. */ /** Unlock the data dictionary cache. */
void unlock() void unlock()
{ {
ut_ad(latch_ex); ut_ad(latch_ex == pthread_self());
ut_ad(!latch_readers); ut_ad(!latch_readers);
ut_d(latch_ex= false); ut_d(latch_ex= 0);
latch.wr_unlock(); latch.wr_unlock();
} }
/** Acquire a shared lock on the dictionary cache. */ /** Acquire a shared lock on the dictionary cache. */

View File

@@ -100,7 +100,7 @@ dict_load_foreigns(
which must be loaded which must be loaded
subsequently to load all the subsequently to load all the
foreign key constraints. */ foreign key constraints. */
MY_ATTRIBUTE((nonnull(1), warn_unused_result)); MY_ATTRIBUTE((nonnull(1)));
/********************************************************************//** /********************************************************************//**
This function opens a system table, and return the first record. This function opens a system table, and return the first record.

View File

@@ -3934,8 +3934,7 @@ void lock_release(trx_t *trx)
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
std::set<table_id_t> to_evict; std::set<table_id_t> to_evict;
if (innodb_evict_tables_on_commit_debug && if (innodb_evict_tables_on_commit_debug &&
!trx->is_recovered && !trx->dict_operation && !trx->is_recovered && !dict_sys.locked())
!trx->dict_operation_lock_mode)
for (const auto& p : trx->mod_tables) for (const auto& p : trx->mod_tables)
if (!p.first->is_temporary()) if (!p.first->is_temporary())
to_evict.emplace(p.first->id); to_evict.emplace(p.first->id);