MDEV-23722 InnoDB: Assertion: result != FTS_INVALID in fts_trx_row_get_new_state

Marking of deletion of row in fts index happens twice in
self-referential foreign key relation. So while performing
referential checks of foreign key, InnoDB can avoid updating
of fts index if the foreign key has self-referential relationship.

Reviewed-by: Marko Mäkelä
parent 874942a0
...@@ -244,3 +244,38 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction ...@@ -244,3 +244,38 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction
XA END 'xid'; XA END 'xid';
XA ROLLBACK 'xid'; XA ROLLBACK 'xid';
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (pk INT PRIMARY KEY,
f1 VARCHAR(10), f2 VARCHAR(10),
f3 VARCHAR(10), f4 VARCHAR(10),
f5 VARCHAR(10), f6 VARCHAR(10),
f7 VARCHAR(10), f8 VARCHAR(10),
INDEX(f1), INDEX(f2), INDEX(f3), INDEX(f4),
INDEX(f5), INDEX(f6), INDEX(f7), INDEX(f8)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(2, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(3, 'innodb', 'innodb', 'innodb', 'innodb',
'innodb', 'innodb', 'innodb', 'innodb');
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t1 (f2) ON DELETE SET NULL;
START TRANSACTION;
DELETE FROM t1 where f1='mariadb';
SELECT * FROM t1;
pk f1 f2 f3 f4 f5 f6 f7 f8
2 NULL mariadb mariadb mariadb mariadb mariadb mariadb mariadb
3 innodb innodb innodb innodb innodb innodb innodb innodb
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f4) ON DELETE CASCADE;
START TRANSACTION;
DELETE FROM t1 where f3='mariadb';
SELECT * FROM t1;
pk f1 f2 f3 f4 f5 f6 f7 f8
3 innodb innodb innodb innodb innodb innodb innodb innodb
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f5) REFERENCES t1 (f6) ON UPDATE SET NULL;
UPDATE t1 SET f6='update';
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `t1_ibfk_3` FOREIGN KEY (`f5`) REFERENCES `t1` (`f6`) ON UPDATE SET NULL)
ALTER TABLE t1 ADD FOREIGN KEY (f7) REFERENCES t1 (f8) ON UPDATE CASCADE;
UPDATE t1 SET f6='cascade';
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `t1_ibfk_3` FOREIGN KEY (`f5`) REFERENCES `t1` (`f6`) ON UPDATE SET NULL)
DROP TABLE t1;
...@@ -299,6 +299,41 @@ ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;# Cleanu ...@@ -299,6 +299,41 @@ ALTER TABLE t1 ADD FOREIGN KEY f (a) REFERENCES t1 (pk), LOCK=EXCLUSIVE;# Cleanu
XA END 'xid'; XA END 'xid';
XA ROLLBACK 'xid'; XA ROLLBACK 'xid';
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (pk INT PRIMARY KEY,
f1 VARCHAR(10), f2 VARCHAR(10),
f3 VARCHAR(10), f4 VARCHAR(10),
f5 VARCHAR(10), f6 VARCHAR(10),
f7 VARCHAR(10), f8 VARCHAR(10),
INDEX(f1), INDEX(f2), INDEX(f3), INDEX(f4),
INDEX(f5), INDEX(f6), INDEX(f7), INDEX(f8)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(2, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(3, 'innodb', 'innodb', 'innodb', 'innodb',
'innodb', 'innodb', 'innodb', 'innodb');
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t1 (f2) ON DELETE SET NULL;
START TRANSACTION;
DELETE FROM t1 where f1='mariadb';
SELECT * FROM t1;
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f4) ON DELETE CASCADE;
START TRANSACTION;
DELETE FROM t1 where f3='mariadb';
SELECT * FROM t1;
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f5) REFERENCES t1 (f6) ON UPDATE SET NULL;
--error ER_ROW_IS_REFERENCED_2
UPDATE t1 SET f6='update';
ALTER TABLE t1 ADD FOREIGN KEY (f7) REFERENCES t1 (f8) ON UPDATE CASCADE;
--error ER_ROW_IS_REFERENCED_2
UPDATE t1 SET f6='cascade';
DROP TABLE t1;
# #
# End of 10.1 tests # End of 10.1 tests
# #
...@@ -913,4 +913,46 @@ DROP TABLE t1; ...@@ -913,4 +913,46 @@ DROP TABLE t1;
DROP TABLE t2; DROP TABLE t2;
DROP TABLE t3; DROP TABLE t3;
DROP TABLE t4; DROP TABLE t4;
#
# InnoDB: Failing assertion: result != FTS_INVALID in
# fts_trx_row_get_new_state
#
SET FOREIGN_KEY_CHECKS=1;
CREATE TABLE t1 (pk INT PRIMARY KEY,
f1 VARCHAR(10), f2 VARCHAR(10),
f3 VARCHAR(10), f4 VARCHAR(10),
f5 VARCHAR(10), f6 VARCHAR(10),
f7 VARCHAR(10), f8 VARCHAR(10),
FULLTEXT(f1), FULLTEXT(f2), FULLTEXT(f3), FULLTEXT(f4),
FULLTEXT(f5), FULLTEXT(f6), FULLTEXT(f7), FULLTEXT(f8),
INDEX(f1), INDEX(f2), INDEX(f3), INDEX(f4),
INDEX(f5), INDEX(f6), INDEX(f7), INDEX(f8)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(2, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(3, 'innodb', 'innodb', 'innodb', 'innodb',
'innodb', 'innodb', 'innodb', 'innodb');
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t1 (f2) ON DELETE SET NULL;
START TRANSACTION;
DELETE FROM t1 where f1='mariadb';
SELECT * FROM t1;
pk f1 f2 f3 f4 f5 f6 f7 f8
2 NULL mariadb mariadb mariadb mariadb mariadb mariadb mariadb
3 innodb innodb innodb innodb innodb innodb innodb innodb
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f4) ON DELETE CASCADE;
START TRANSACTION;
DELETE FROM t1 where f3='mariadb';
SELECT * FROM t1;
pk f1 f2 f3 f4 f5 f6 f7 f8
3 innodb innodb innodb innodb innodb innodb innodb innodb
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f5) REFERENCES t1 (f6) ON UPDATE SET NULL;
UPDATE t1 SET f6='update';
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `t1_ibfk_3` FOREIGN KEY (`f5`) REFERENCES `t1` (`f6`) ON UPDATE SET NULL)
ALTER TABLE t1 ADD FOREIGN KEY (f7) REFERENCES t1 (f8) ON UPDATE CASCADE;
UPDATE t1 SET f6='cascade';
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `t1_ibfk_3` FOREIGN KEY (`f5`) REFERENCES `t1` (`f6`) ON UPDATE SET NULL)
DROP TABLE t1;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
...@@ -907,4 +907,46 @@ DROP TABLE t2; ...@@ -907,4 +907,46 @@ DROP TABLE t2;
DROP TABLE t3; DROP TABLE t3;
DROP TABLE t4; DROP TABLE t4;
--echo #
--echo # InnoDB: Failing assertion: result != FTS_INVALID in
--echo # fts_trx_row_get_new_state
--echo #
SET FOREIGN_KEY_CHECKS=1;
CREATE TABLE t1 (pk INT PRIMARY KEY,
f1 VARCHAR(10), f2 VARCHAR(10),
f3 VARCHAR(10), f4 VARCHAR(10),
f5 VARCHAR(10), f6 VARCHAR(10),
f7 VARCHAR(10), f8 VARCHAR(10),
FULLTEXT(f1), FULLTEXT(f2), FULLTEXT(f3), FULLTEXT(f4),
FULLTEXT(f5), FULLTEXT(f6), FULLTEXT(f7), FULLTEXT(f8),
INDEX(f1), INDEX(f2), INDEX(f3), INDEX(f4),
INDEX(f5), INDEX(f6), INDEX(f7), INDEX(f8)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(2, 'mariadb', 'mariadb', 'mariadb', 'mariadb',
'mariadb', 'mariadb', 'mariadb', 'mariadb'),
(3, 'innodb', 'innodb', 'innodb', 'innodb',
'innodb', 'innodb', 'innodb', 'innodb');
ALTER TABLE t1 ADD FOREIGN KEY (f1) REFERENCES t1 (f2) ON DELETE SET NULL;
START TRANSACTION;
DELETE FROM t1 where f1='mariadb';
SELECT * FROM t1;
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f4) ON DELETE CASCADE;
START TRANSACTION;
DELETE FROM t1 where f3='mariadb';
SELECT * FROM t1;
ROLLBACK;
ALTER TABLE t1 ADD FOREIGN KEY (f5) REFERENCES t1 (f6) ON UPDATE SET NULL;
--error ER_ROW_IS_REFERENCED_2
UPDATE t1 SET f6='update';
ALTER TABLE t1 ADD FOREIGN KEY (f7) REFERENCES t1 (f8) ON UPDATE CASCADE;
--error ER_ROW_IS_REFERENCED_2
UPDATE t1 SET f6='cascade';
DROP TABLE t1;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
...@@ -852,3 +852,22 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set) ...@@ -852,3 +852,22 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set)
return(out); return(out);
} }
/** Check whether fulltext index gets affected by foreign
key constraint. */
bool dict_foreign_t::affects_fulltext() const
{
if (foreign_table == referenced_table || !foreign_table->fts)
return false;
for (ulint i = 0; i < n_fields; i++)
{
if (dict_table_is_fts_column(
foreign_table->fts->indexes,
dict_index_get_nth_col_no(foreign_index, i))
!= ULINT_UNDEFINED)
return true;
}
return false;
}
...@@ -848,6 +848,10 @@ struct dict_foreign_t{ ...@@ -848,6 +848,10 @@ struct dict_foreign_t{
does not generate new indexes does not generate new indexes
implicitly */ implicitly */
dict_index_t* referenced_index;/*!< referenced index */ dict_index_t* referenced_index;/*!< referenced index */
/** Check whether the fulltext index gets affected by
foreign key constraint */
bool affects_fulltext() const;
}; };
std::ostream& std::ostream&
......
...@@ -510,7 +510,7 @@ row_ins_cascade_calc_update_vec( ...@@ -510,7 +510,7 @@ row_ins_cascade_calc_update_vec(
n_fields_updated = 0; n_fields_updated = 0;
*fts_col_affected = FALSE; *fts_col_affected = foreign->affects_fulltext();
if (table->fts) { if (table->fts) {
doc_id_pos = dict_table_get_nth_col_pos( doc_id_pos = dict_table_get_nth_col_pos(
...@@ -631,16 +631,6 @@ row_ins_cascade_calc_update_vec( ...@@ -631,16 +631,6 @@ row_ins_cascade_calc_update_vec(
padded_data, min_size); padded_data, min_size);
} }
/* Check whether the current column has
FTS index on it */
if (table->fts
&& dict_table_is_fts_column(
table->fts->indexes,
dict_col_get_no(col))
!= ULINT_UNDEFINED) {
*fts_col_affected = TRUE;
}
/* If Doc ID is updated, check whether the /* If Doc ID is updated, check whether the
Doc ID is valid */ Doc ID is valid */
if (table->fts if (table->fts
...@@ -977,7 +967,6 @@ row_ins_foreign_check_on_constraint( ...@@ -977,7 +967,6 @@ row_ins_foreign_check_on_constraint(
upd_t* update; upd_t* update;
ulint n_to_update; ulint n_to_update;
dberr_t err; dberr_t err;
ulint i;
trx_t* trx; trx_t* trx;
mem_heap_t* tmp_heap = NULL; mem_heap_t* tmp_heap = NULL;
doc_id_t doc_id = FTS_NULL_DOC_ID; doc_id_t doc_id = FTS_NULL_DOC_ID;
...@@ -1191,7 +1180,7 @@ row_ins_foreign_check_on_constraint( ...@@ -1191,7 +1180,7 @@ row_ins_foreign_check_on_constraint(
UNIV_MEM_INVALID(update->fields, UNIV_MEM_INVALID(update->fields,
update->n_fields * sizeof *update->fields); update->n_fields * sizeof *update->fields);
for (i = 0; i < foreign->n_fields; i++) { for (ulint i = 0; i < foreign->n_fields; i++) {
upd_field_t* ufield = &update->fields[i]; upd_field_t* ufield = &update->fields[i];
ufield->field_no = dict_table_get_nth_col_pos( ufield->field_no = dict_table_get_nth_col_pos(
...@@ -1200,33 +1189,15 @@ row_ins_foreign_check_on_constraint( ...@@ -1200,33 +1189,15 @@ row_ins_foreign_check_on_constraint(
ufield->orig_len = 0; ufield->orig_len = 0;
ufield->exp = NULL; ufield->exp = NULL;
dfield_set_null(&ufield->new_val); dfield_set_null(&ufield->new_val);
if (table->fts && dict_table_is_fts_column(
table->fts->indexes,
dict_index_get_nth_col_no(index, i))
!= ULINT_UNDEFINED) {
fts_col_affacted = TRUE;
}
} }
if (fts_col_affacted) { if (foreign->affects_fulltext()) {
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL); fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
} }
} else if (table->fts && cascade->is_delete) { } else if (table->fts && cascade->is_delete
/* DICT_FOREIGN_ON_DELETE_CASCADE case */ && foreign->affects_fulltext()) {
for (i = 0; i < foreign->n_fields; i++) {
if (table->fts && dict_table_is_fts_column(
table->fts->indexes,
dict_index_get_nth_col_no(index, i))
!= ULINT_UNDEFINED) {
fts_col_affacted = TRUE;
}
}
if (fts_col_affacted) {
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL); fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
} }
}
if (!node->is_delete if (!node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) { && (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) {
......
...@@ -853,3 +853,22 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set) ...@@ -853,3 +853,22 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set)
return(out); return(out);
} }
/** Check whether fulltext index gets affected by foreign
key constraint. */
bool dict_foreign_t::affects_fulltext() const
{
if (foreign_table == referenced_table || !foreign_table->fts)
return false;
for (ulint i = 0; i < n_fields; i++)
{
if (dict_table_is_fts_column(
foreign_table->fts->indexes,
dict_index_get_nth_col_no(foreign_index, i))
!= ULINT_UNDEFINED)
return true;
}
return false;
}
...@@ -864,6 +864,10 @@ struct dict_foreign_t{ ...@@ -864,6 +864,10 @@ struct dict_foreign_t{
does not generate new indexes does not generate new indexes
implicitly */ implicitly */
dict_index_t* referenced_index;/*!< referenced index */ dict_index_t* referenced_index;/*!< referenced index */
/** Check whether the fulltext index gets affected by
foreign key constraint */
bool affects_fulltext() const;
}; };
std::ostream& std::ostream&
......
...@@ -516,7 +516,7 @@ row_ins_cascade_calc_update_vec( ...@@ -516,7 +516,7 @@ row_ins_cascade_calc_update_vec(
n_fields_updated = 0; n_fields_updated = 0;
*fts_col_affected = FALSE; *fts_col_affected = foreign->affects_fulltext();
if (table->fts) { if (table->fts) {
doc_id_pos = dict_table_get_nth_col_pos( doc_id_pos = dict_table_get_nth_col_pos(
...@@ -637,16 +637,6 @@ row_ins_cascade_calc_update_vec( ...@@ -637,16 +637,6 @@ row_ins_cascade_calc_update_vec(
padded_data, min_size); padded_data, min_size);
} }
/* Check whether the current column has
FTS index on it */
if (table->fts
&& dict_table_is_fts_column(
table->fts->indexes,
dict_col_get_no(col))
!= ULINT_UNDEFINED) {
*fts_col_affected = TRUE;
}
/* If Doc ID is updated, check whether the /* If Doc ID is updated, check whether the
Doc ID is valid */ Doc ID is valid */
if (table->fts if (table->fts
...@@ -983,7 +973,6 @@ row_ins_foreign_check_on_constraint( ...@@ -983,7 +973,6 @@ row_ins_foreign_check_on_constraint(
upd_t* update; upd_t* update;
ulint n_to_update; ulint n_to_update;
dberr_t err; dberr_t err;
ulint i;
trx_t* trx; trx_t* trx;
mem_heap_t* tmp_heap = NULL; mem_heap_t* tmp_heap = NULL;
doc_id_t doc_id = FTS_NULL_DOC_ID; doc_id_t doc_id = FTS_NULL_DOC_ID;
...@@ -1197,7 +1186,7 @@ row_ins_foreign_check_on_constraint( ...@@ -1197,7 +1186,7 @@ row_ins_foreign_check_on_constraint(
UNIV_MEM_INVALID(update->fields, UNIV_MEM_INVALID(update->fields,
update->n_fields * sizeof *update->fields); update->n_fields * sizeof *update->fields);
for (i = 0; i < foreign->n_fields; i++) { for (ulint i = 0; i < foreign->n_fields; i++) {
upd_field_t* ufield = &update->fields[i]; upd_field_t* ufield = &update->fields[i];
ufield->field_no = dict_table_get_nth_col_pos( ufield->field_no = dict_table_get_nth_col_pos(
...@@ -1206,33 +1195,15 @@ row_ins_foreign_check_on_constraint( ...@@ -1206,33 +1195,15 @@ row_ins_foreign_check_on_constraint(
ufield->orig_len = 0; ufield->orig_len = 0;
ufield->exp = NULL; ufield->exp = NULL;
dfield_set_null(&ufield->new_val); dfield_set_null(&ufield->new_val);
if (table->fts && dict_table_is_fts_column(
table->fts->indexes,
dict_index_get_nth_col_no(index, i))
!= ULINT_UNDEFINED) {
fts_col_affacted = TRUE;
}
} }
if (fts_col_affacted) { if (foreign->affects_fulltext()) {
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL); fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
} }
} else if (table->fts && cascade->is_delete) { } else if (table->fts && cascade->is_delete
/* DICT_FOREIGN_ON_DELETE_CASCADE case */ && foreign->affects_fulltext()) {
for (i = 0; i < foreign->n_fields; i++) {
if (table->fts && dict_table_is_fts_column(
table->fts->indexes,
dict_index_get_nth_col_no(index, i))
!= ULINT_UNDEFINED) {
fts_col_affacted = TRUE;
}
}
if (fts_col_affacted) {
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL); fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
} }
}
if (!node->is_delete if (!node->is_delete
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) { && (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment