1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +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;
length(table_name)
79
79
79
79
drop table extralongname_extralongname_extralongname_extralongname_ext;
# End of 10.0 tests
set sql_mode=default;

View File

@ -33,11 +33,19 @@ b bigint unsigned NOT NULL,
d1 date NOT NULL,
PRIMARY KEY (b,d1)
) 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")
show warnings;
Level Code Message
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`
set foreign_key_checks = 0;
DROP TABLE IF EXISTS d;
Warnings:
Note 1051 Unknown table 'bug_fk.d'

View File

@ -2531,9 +2531,19 @@ disconnect b;
set foreign_key_checks=0;
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;
ERROR HY000: Can't create table `test`.`t1` (errno: 150 "Foreign key constraint is incorrectly formed")
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;
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
drop table t1;
set foreign_key_checks=0;
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;

View File

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

View File

@ -80,9 +80,19 @@ SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY;
INSERT INTO t1 VALUES (1,1);
LOCK TABLES t1 WRITE;
SET FOREIGN_KEY_CHECKS=1;
TRUNCATE t1;
ERROR HY000: Cannot add foreign key constraint for `t1`
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;
pk a
1 1

View File

@ -46,7 +46,15 @@ show create table c;
#
# 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 (
b bigint unsigned NOT NULL,
d1 date NOT NULL,
@ -54,6 +62,7 @@ CREATE TABLE b (
) ENGINE=InnoDB;
show warnings;
set foreign_key_checks = 0;
DROP TABLE IF EXISTS d;

View File

@ -1598,12 +1598,22 @@ disconnect b;
set foreign_key_checks=0;
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
--replace_result $MYSQLTEST_VARDIR . master-data/ ''
--error ER_CANT_CREATE_TABLE
create table t1(a char(10) primary key, b varchar(20)) engine = innodb;
set foreign_key_checks=1;
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
# when f_k_c is 0

View File

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

View File

@ -685,8 +685,7 @@ dict_acquire_mdl_shared(dict_table_t *table,
}
else
{
ut_ad(dict_sys.frozen());
ut_ad(!dict_sys.locked());
ut_ad(dict_sys.frozen_not_locked());
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);
ut_ad(!latch_readers);
ut_ad(!latch_ex);
ut_d(latch_ex= true);
ut_d(latch_ex= pthread_self());
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));
ut_ad(!latch_readers);
ut_ad(!latch_ex);
ut_d(latch_ex= true);
ut_d(latch_ex= pthread_self());
}
#ifdef UNIV_PFS_RWLOCK
ATTRIBUTE_NOINLINE void dict_sys_t::unlock()
{
ut_ad(latch_ex);
ut_ad(latch_ex == pthread_self());
ut_ad(!latch_readers);
ut_d(latch_ex= false);
ut_d(latch_ex= 0);
latch.wr_unlock();
}
@ -2749,17 +2748,6 @@ dict_index_build_internal_fts(
}
/*====================== 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. */
void

View File

@ -11473,6 +11473,8 @@ innobase_fts_load_stopword(
trx_t* trx, /*!< in: transaction */
THD* thd) /*!< in: current thread */
{
ut_ad(dict_sys.locked());
const char *stopword_table= THDVAR(thd, ft_user_stopword_table);
if (!stopword_table)
{
@ -11482,9 +11484,11 @@ innobase_fts_load_stopword(
mysql_mutex_unlock(&LOCK_global_system_variables);
}
return !high_level_read_only &&
fts_load_stopword(table, trx, stopword_table,
table->fts->dict_locked= true;
bool success= fts_load_stopword(table, trx, stopword_table,
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.
@ -12820,15 +12824,18 @@ int create_table_info_t::create_table(bool create_fk)
dberr_t err = create_fk ? create_foreign_keys() : 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 */
dict_names_t fk_tables;
err = dict_load_foreigns(m_table_name, nullptr,
m_trx->id, true,
DICT_ERR_IGNORE_NONE, fk_tables);
ignore_err, fk_tables);
while (err == DB_SUCCESS && !fk_tables.empty()) {
dict_sys.load_table(
{fk_tables.front(), strlen(fk_tables.front())},
DICT_ERR_IGNORE_NONE);
ignore_err);
fk_tables.pop_front();
}
}
@ -13109,96 +13116,59 @@ bool create_table_info_t::row_size_is_acceptable(
return true;
}
/** Update a new table in an InnoDB database.
@return error number */
int
create_table_info_t::create_table_update_dict()
void create_table_info_t::create_table_update_dict(dict_table_t *table,
THD *thd,
const HA_CREATE_INFO &info,
const TABLE &t)
{
dict_table_t* innobase_table;
ut_ad(dict_sys.locked());
DBUG_ENTER("create_table_update_dict");
innobase_table = dict_table_open_on_name(
m_table_name, false, DICT_ERR_IGNORE_NONE);
DBUG_ASSERT(innobase_table != 0);
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(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));
}
DBUG_ASSERT((innobase_table->fts == NULL)
== (innobase_table->fts_doc_id_index == NULL));
DBUG_ASSERT(!table->fts == !table->fts_doc_id_index);
innobase_copy_frm_flags_from_create_info(innobase_table, m_create_info);
dict_stats_update(innobase_table, DICT_STATS_EMPTY_TABLE);
innobase_copy_frm_flags_from_create_info(table, &info);
/* Load server stopword into FTS cache */
if (m_flags2 & DICT_TF2_FTS) {
if (!innobase_fts_load_stopword(innobase_table, NULL, m_thd)) {
innobase_table->release();
DBUG_RETURN(-1);
}
if (table->flags2 & DICT_TF2_FTS &&
innobase_fts_load_stopword(table, nullptr, thd))
fts_optimize_add_table(table);
dict_sys.lock(SRW_LOCK_CALL);
fts_optimize_add_table(innobase_table);
dict_sys.unlock();
}
if (const Field* ai = m_form->found_next_number_field) {
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;
ib_uint64_t autoinc = m_create_info->auto_increment_value;
table->autoinc_mutex.wr_lock();
dict_table_autoinc_initialize(table, autoinc);
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.
if (!table->is_temporary())
{
const unsigned col_no= innodb_col_no(ai);
table->persistent_autoinc= static_cast<uint16_t>
(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(
innobase_table),
autoinc);
}
if (--autoinc)
btr_write_autoinc(dict_table_get_first_index(table), autoinc);
}
innobase_table->autoinc_mutex.wr_unlock();
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);
innobase_parse_hint_from_comment(thd, table, t.s);
}
/** Allocate a new trx. */
@ -13215,87 +13185,78 @@ create_table_info_t::allocate_trx()
@param[in] create_info Create info (including create statement string).
@param[in] file_per_table whether to create .ibd file
@param[in,out] trx dictionary transaction, or NULL to create new
@return 0 if success else error number. */
inline int
ha_innobase::create(
const char* name,
TABLE* form,
HA_CREATE_INFO* create_info,
bool file_per_table,
trx_t* trx)
@return error code
@retval 0 on success */
int
ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info,
bool file_per_table, trx_t *trx= nullptr)
{
char norm_name[FN_REFLEN]; /* {database}/{tablename} */
char remote_path[FN_REFLEN]; /* Absolute path of table */
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(table_share->table_type == TABLE_TYPE_SEQUENCE ||
table_share->table_type == TABLE_TYPE_NORMAL);
create_table_info_t info(ha_thd(),
form,
create_info,
norm_name,
remote_path,
file_per_table, trx);
create_table_info_t info(ha_thd(), form, create_info, norm_name,
remote_path, file_per_table, trx);
{
int error = info.initialize();
if (!error) {
error = info.prepare_create_table(name, !trx);
}
if (error) {
if (trx) {
trx_rollback_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx);
}
int error= info.initialize();
if (!error)
error= info.prepare_create_table(name, !trx);
if (error)
DBUG_RETURN(error);
}
}
const bool own_trx = !trx;
int error = 0;
if (own_trx) {
const bool own_trx= !trx;
if (own_trx)
{
info.allocate_trx();
trx = info.trx();
trx= info.trx();
DBUG_ASSERT(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
}
if (own_trx && !(info.flags2() & DICT_TF2_TEMPORARY)) {
if (!(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 (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) {
error = info.create_table(own_trx);
}
if (!error)
error= info.create_table(own_trx);
if (error) {
/* Rollback will drop the being-created table. */
if (own_trx || (info.flags2() & DICT_TF2_TEMPORARY))
{
if (error)
trx_rollback_for_mysql(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. */
else
{
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);
}
ut_ad(deleted.empty());
info.table()->acquire();
info.create_table_update_dict(info.table(), info.thd(),
*create_info, *form);
}
if (own_trx) {
if (own_trx)
{
row_mysql_unlock_data_dictionary(trx);
if (!error)
{
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);
}
@ -13305,11 +13266,8 @@ ha_innobase::create(
@param[in] form Table format; columns and index information.
@param[in] create_info Create info (including create statement string).
@return 0 if success else error number. */
int
ha_innobase::create(
const char* name,
TABLE* form,
HA_CREATE_INFO* create_info)
int ha_innobase::create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info)
{
return create(name, form, create_info, srv_file_per_table);
}
@ -13844,210 +13802,230 @@ int ha_innobase::truncate()
update_thd();
if (is_read_only()) {
if (is_read_only())
DBUG_RETURN(HA_ERR_TABLE_READONLY);
}
HA_CREATE_INFO info;
dict_table_t* ib_table = m_prebuilt->table;
dict_table_t *ib_table= m_prebuilt->table;
info.init();
update_create_info_from_table(&info, table);
switch (dict_tf_get_rec_format(ib_table->flags)) {
case REC_FORMAT_REDUNDANT:
info.row_type = ROW_TYPE_REDUNDANT;
info.row_type= ROW_TYPE_REDUNDANT;
break;
case REC_FORMAT_COMPACT:
info.row_type = ROW_TYPE_COMPACT;
info.row_type= ROW_TYPE_COMPACT;
break;
case REC_FORMAT_COMPRESSED:
info.row_type = ROW_TYPE_COMPRESSED;
info.row_type= ROW_TYPE_COMPRESSED;
break;
case REC_FORMAT_DYNAMIC:
info.row_type = ROW_TYPE_DYNAMIC;
info.row_type= ROW_TYPE_DYNAMIC;
break;
}
const auto stored_lock = m_prebuilt->stored_select_lock_type;
trx_t* trx = innobase_trx_allocate(m_user_thd);
const auto stored_lock= m_prebuilt->stored_select_lock_type;
trx_t *trx= innobase_trx_allocate(m_user_thd);
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);
m_prebuilt->table = nullptr;
m_prebuilt->table= nullptr;
row_prebuilt_free(m_prebuilt);
m_prebuilt = nullptr;
m_prebuilt= nullptr;
my_free(m_upd_buf);
m_upd_buf = nullptr;
m_upd_buf_size = 0;
m_upd_buf= nullptr;
m_upd_buf_size= 0;
row_mysql_lock_data_dictionary(trx);
ib_table->release();
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,
trx);
ut_ad(!err);
if (!err) {
err = open(ib_table->name.m_name, 0, 0);
m_prebuilt->stored_select_lock_type = stored_lock;
if (!err)
{
err= open(ib_table->name.m_name, 0, 0);
m_prebuilt->table->release();
m_prebuilt->stored_select_lock_type= stored_lock;
}
trx->free();
#ifdef BTR_CUR_HASH_ADAPT
if (UT_LIST_GET_LEN(ib_table->freed_indexes)) {
ib_table->vc_templ = nullptr;
ib_table->id = 0;
DBUG_RETURN(err);
if (UT_LIST_GET_LEN(ib_table->freed_indexes))
{
ib_table->vc_templ= nullptr;
ib_table->id= 0;
}
else
#endif /* BTR_CUR_HASH_ADAPT */
dict_mem_table_free(ib_table);
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);
info.data_file_name = ib_table->data_dir_path;
const char* temp_name = dict_mem_create_temporary_tablename(
heap, ib_table->name.m_name, ib_table->id);
const char* name = mem_heap_strdup(heap, ib_table->name.m_name);
info.data_file_name= ib_table->data_dir_path;
const char *temp_name=
dict_mem_create_temporary_tablename(heap,
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;
MDL_ticket *mdl_table = nullptr, *mdl_index = nullptr;
dberr_t error = DB_SUCCESS;
dberr_t error= DB_SUCCESS;
dict_sys.freeze(SRW_LOCK_CALL);
for (const dict_foreign_t* f : ib_table->referenced_set) {
if (dict_table_t* child = f->foreign_table) {
error = lock_table_for_trx(child, trx, LOCK_X);
if (error != DB_SUCCESS) {
for (const dict_foreign_t *f : ib_table->referenced_set)
if (dict_table_t *child= f->foreign_table)
if ((error= lock_table_for_trx(child, trx, LOCK_X)) != DB_SUCCESS)
break;
}
}
}
dict_sys.unfreeze();
if (error == DB_SUCCESS) {
error = lock_table_for_trx(ib_table, trx, LOCK_X);
}
if (error == DB_SUCCESS)
error= lock_table_for_trx(ib_table, trx, LOCK_X);
const bool fts = error == DB_SUCCESS
&& ib_table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS);
const bool fts= error == DB_SUCCESS &&
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);
error = fts_lock_tables(trx, *ib_table);
error= fts_lock_tables(trx, *ib_table);
}
/* Wait for purge threads to stop using the table. */
for (uint n = 15; ib_table->get_ref_count() > 1; ) {
if (!--n) {
error = DB_LOCK_WAIT_TIMEOUT;
for (uint n = 15; ib_table->get_ref_count() > 1; )
{
if (!--n)
{
error= DB_LOCK_WAIT_TIMEOUT;
break;
}
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()) {
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) {
if (table_stats)
{
dict_sys.freeze(SRW_LOCK_CALL);
table_stats = dict_acquire_mdl_shared<false>(
table_stats, m_user_thd, &mdl_table);
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,
index_stats= dict_table_open_on_name(INDEX_STATS_NAME, false,
DICT_ERR_IGNORE_NONE);
if (index_stats) {
if (index_stats)
{
dict_sys.freeze(SRW_LOCK_CALL);
index_stats = dict_acquire_mdl_shared<false>(
index_stats, m_user_thd, &mdl_index);
index_stats= dict_acquire_mdl_shared<false>(index_stats, m_user_thd,
&mdl_index);
dict_sys.unfreeze();
}
if (table_stats && index_stats
&& !strcmp(table_stats->name.m_name, TABLE_STATS_NAME)
&& !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 (table_stats && index_stats &&
!strcmp(table_stats->name.m_name, TABLE_STATS_NAME) &&
!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) {
error = lock_sys_tables(trx);
}
if (error == DB_SUCCESS)
error= lock_sys_tables(trx);
std::vector<pfs_os_file_t> deleted;
row_mysql_lock_data_dictionary(trx);
if (error == DB_SUCCESS) {
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)
{
error= innobase_rename_table(trx, ib_table->name.m_name, temp_name, false);
if (error == DB_SUCCESS)
error= trx->drop_table(*ib_table);
}
int err = convert_error_code_to_mysql(error, ib_table->flags,
m_user_thd);
if (err) {
trx_rollback_for_mysql(trx);
if (fts) {
fts_optimize_add_table(ib_table);
purge_sys.resume_FTS();
}
row_mysql_unlock_data_dictionary(trx);
} else {
int err = convert_error_code_to_mysql(error, ib_table->flags, m_user_thd);
const auto update_time = ib_table->update_time;
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,
dict_table_is_file_per_table(ib_table), trx);
/* On success, create() durably committed trx. */
if (fts) {
purge_sys.resume_FTS();
if (err)
{
trx_rollback_for_mysql(trx);
if (fts)
fts_optimize_add_table(ib_table);
}
else
{
const auto def_trx_id= ib_table->def_trx_id;
ib_table->release();
m_prebuilt->table= nullptr;
err= create(name, table, &info, dict_table_is_file_per_table(ib_table),
trx);
if (!err)
{
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) {
reload:
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;
if (fts)
purge_sys.resume_FTS();
row_mysql_unlock_data_dictionary(trx);
for (pfs_os_file_t d : deleted) os_file_close(d);
if (!err)
{
dict_stats_update(m_prebuilt->table, DICT_STATS_EMPTY_TABLE);
log_write_up_to(trx->commit_lsn, true);
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;
m_prebuilt= nullptr;
m_upd_buf= nullptr;
m_upd_buf_size= 0;
err = open(name, 0, 0);
if (!err) {
m_prebuilt->stored_select_lock_type
= stored_lock;
m_prebuilt->table->update_time = update_time;
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;
goto reload;
}
else
{
/* Revert to the old table. */
m_prebuilt= prebuilt;
m_upd_buf= upd_buf;
m_upd_buf_size= upd_buf_size;
}
}
@ -14055,12 +14033,10 @@ reload:
mem_heap_free(heap);
if (table_stats) {
if (table_stats)
dict_table_close(table_stats, false, m_user_thd, mdl_table);
}
if (index_stats) {
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.
@return > 0 if referenced by a FOREIGN KEY */
uint
ha_innobase::referenced_by_foreign_key(void)
/*========================================*/
uint ha_innobase::referenced_by_foreign_key()
{
if (dict_table_is_referenced_by_foreign_key(m_prebuilt->table)) {
return(1);
}
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);
}
dict_sys.freeze(SRW_LOCK_CALL);
const bool empty= m_prebuilt->table->referenced_set.empty();
dict_sys.unfreeze();
return !empty;
}
/*******************************************************************//**

View File

@ -190,12 +190,12 @@ public:
void update_create_info(HA_CREATE_INFO* create_info) override;
inline int create(
int create(
const char* name,
TABLE* form,
HA_CREATE_INFO* create_info,
bool file_per_table,
trx_t* trx = NULL);
trx_t* trx);
int create(
const char* name,
@ -225,7 +225,7 @@ public:
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;
@ -639,8 +639,9 @@ public:
@param create_fk whether to add FOREIGN KEY constraints */
int create_table(bool create_fk = true);
/** Update the internal data dictionary. */
int create_table_update_dict();
static void create_table_update_dict(dict_table_t* table, THD* thd,
const HA_CREATE_INFO& info,
const TABLE& t);
/** Validates the create options. Checks that the options
KEY_BLOCK_SIZE, ROW_FORMAT, DATA DIRECTORY, TEMPORARY & TABLESPACE
@ -700,12 +701,13 @@ public:
trx_t* trx() const
{ return(m_trx); }
/** Return table name. */
const char* table_name() const
{ return(m_table_name); }
/** @return table name */
const char* table_name() const { return(m_table_name); }
THD* thd() const
{ return(m_thd); }
/** @return the created table */
dict_table_t *table() const { return m_table; }
THD* thd() const { return(m_thd); }
private:
/** 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;
}
ctx->new_table->fts->dict_locked = true;
error = innobase_fts_load_stopword(
ctx->new_table, ctx->trx,
ctx->prebuilt->trx->mysql_thd)
? DB_SUCCESS : DB_ERROR;
ctx->new_table->fts->dict_locked = false;
if (error != DB_SUCCESS) {
goto error_handling;
@ -9882,7 +9879,7 @@ innobase_update_foreign_cache(
err = dict_load_foreigns(user_table->name.m_name,
ctx->col_names, 1, true,
DICT_ERR_IGNORE_NONE,
DICT_ERR_IGNORE_FK_NOKEY,
fk_tables);
if (err == DB_CANNOT_ADD_CONSTRAINT) {

View File

@ -421,14 +421,6 @@ dict_foreign_add_to_cache(
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
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
foreign key lists of the table.
@ -1329,7 +1321,7 @@ class dict_sys_t
alignas(CPU_LEVEL1_DCACHE_LINESIZE) srw_lock latch;
#ifdef UNIV_DEBUG
/** 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 */
Atomic_counter<uint32_t> latch_readers;
#endif
@ -1503,11 +1495,12 @@ public:
/** @return whether any thread (not necessarily the current thread)
is holding the latch; that is, this check may return false
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)
is holding the exclusive latch; that is, this check may return false
positives */
bool locked() const { return latch_ex; }
is holding a shared latch */
bool frozen_not_locked() const { return latch_readers; }
/** @return whether the current thread holds the exclusive latch */
bool locked() const { return latch_ex == pthread_self(); }
#endif
private:
/** Acquire the exclusive latch */
@ -1526,7 +1519,7 @@ public:
{
ut_ad(!latch_readers);
ut_ad(!latch_ex);
ut_d(latch_ex= true);
ut_d(latch_ex= pthread_self());
}
else
lock_wait(SRW_LOCK_ARGS(file, line));
@ -1543,9 +1536,9 @@ public:
/** Unlock the data dictionary cache. */
void unlock()
{
ut_ad(latch_ex);
ut_ad(latch_ex == pthread_self());
ut_ad(!latch_readers);
ut_d(latch_ex= false);
ut_d(latch_ex= 0);
latch.wr_unlock();
}
/** Acquire a shared lock on the dictionary cache. */

View File

@ -100,7 +100,7 @@ dict_load_foreigns(
which must be loaded
subsequently to load all the
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.

View File

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