Commit 10590dd3 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-15199 Referential integrity broken in ON DELETE CASCADE

MDEV-14222 Unnecessary 'cascade' memory allocation for every updated row
when there is no FOREIGN KEY

This reverts the MySQL 5.7.2 change
https://github.com/mysql/mysql-server/commit/377774689bf6a16af74182753fe950d514c2c6dd
which introduced these problems. MariaDB 10.2.2 inherited these problems
in commit 2e814d47.

The FOREIGN KEY CASCADE and SET NULL operations implemented as
procedural recursion are consuming more than 8 kilobytes of stack
(9 stack frames) per iteration in a non-debug GNU/Linux AMD64 build.
This is why we need to limit the maximum recursion depth to 15 steps
instead of the 255 that it used to be in MySQL 5.7 and MariaDB 10.2.

A corresponding change was made in MySQL 5.7.21 in
https://github.com/mysql/mysql-server/commit/7b26dc98a624d5cdf79cd5eee455cb03e3bccb5a
parent 5d7e9fd4
......@@ -263,3 +263,46 @@ SELECT * FROM t2;
id ref_id f
2 2 20
DROP TABLE t2, t1;
#
# MDEV-15199 Referential integrity broken in ON DELETE CASCADE
#
CREATE TABLE member (id int AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO member VALUES (1);
CREATE TABLE address (
id int AUTO_INCREMENT PRIMARY KEY,
member_id int NOT NULL,
KEY address_FI_1 (member_id),
CONSTRAINT address_FK_1 FOREIGN KEY (member_id) REFERENCES member (id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO address VALUES (2,1);
CREATE TABLE payment_method (
id int AUTO_INCREMENT PRIMARY KEY,
member_id int NOT NULL,
cardholder_address_id int DEFAULT NULL,
KEY payment_method_FI_1 (member_id),
KEY payment_method_FI_2 (cardholder_address_id),
CONSTRAINT payment_method_FK_1 FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT payment_method_FK_2 FOREIGN KEY (cardholder_address_id) REFERENCES address (id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO payment_method VALUES (3,1,2);
BEGIN;
UPDATE member SET id=42;
SELECT * FROM member;
id
42
SELECT * FROM address;
id member_id
2 42
SELECT * FROM payment_method;
id member_id cardholder_address_id
3 42 2
DELETE FROM member;
COMMIT;
SELECT * FROM member;
id
SELECT * FROM address;
id member_id
SELECT * FROM payment_method;
id member_id cardholder_address_id
DROP TABLE payment_method,address,member;
......@@ -86,6 +86,7 @@ child CREATE TABLE `child` (
DELETE FROM parent where a = 1;
SELECT * FROM child;
a1 a2
1 NULL
2 3
10 20
SET foreign_key_checks = 0;
......
......@@ -1320,80 +1320,17 @@ INSERT INTO t2 VALUES (10, 'old'), (20, 'other');
UPDATE t1 SET c1 = 'other' WHERE c1 = 'old';
ERROR 23000: Foreign key constraint for table 't1', record 'other-somevalu' would lead to a duplicate entry in table 't2', key 'c1'
DROP TABLE t2,t1;
call mtr.add_suppression("Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 255. Please drop excessive foreign constraints and try again");
call mtr.add_suppression("Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 15\\. Please drop excessive foreign constraints and try again");
create table t1(
id int primary key,
pid int,
index(pid),
foreign key(pid) references t1(id) on delete cascade) engine=innodb;
insert into t1 values
( 0, 0), ( 1, 0), ( 2, 1), ( 3, 2),
( 4, 3), ( 5, 4), ( 6, 5), ( 7, 6),
( 8, 7), ( 9, 8), ( 10, 9), ( 11, 10),
( 12, 11), ( 13, 12), ( 14, 13), ( 15, 14),
( 16, 15), ( 17, 16), ( 18, 17), ( 19, 18),
( 20, 19), ( 21, 20), ( 22, 21), ( 23, 22),
( 24, 23), ( 25, 24), ( 26, 25), ( 27, 26),
( 28, 27), ( 29, 28), ( 30, 29), ( 31, 30),
( 32, 31), ( 33, 32), ( 34, 33), ( 35, 34),
( 36, 35), ( 37, 36), ( 38, 37), ( 39, 38),
( 40, 39), ( 41, 40), ( 42, 41), ( 43, 42),
( 44, 43), ( 45, 44), ( 46, 45), ( 47, 46),
( 48, 47), ( 49, 48), ( 50, 49), ( 51, 50),
( 52, 51), ( 53, 52), ( 54, 53), ( 55, 54),
( 56, 55), ( 57, 56), ( 58, 57), ( 59, 58),
( 60, 59), ( 61, 60), ( 62, 61), ( 63, 62),
( 64, 63), ( 65, 64), ( 66, 65), ( 67, 66),
( 68, 67), ( 69, 68), ( 70, 69), ( 71, 70),
( 72, 71), ( 73, 72), ( 74, 73), ( 75, 74),
( 76, 75), ( 77, 76), ( 78, 77), ( 79, 78),
( 80, 79), ( 81, 80), ( 82, 81), ( 83, 82),
( 84, 83), ( 85, 84), ( 86, 85), ( 87, 86),
( 88, 87), ( 89, 88), ( 90, 89), ( 91, 90),
( 92, 91), ( 93, 92), ( 94, 93), ( 95, 94),
( 96, 95), ( 97, 96), ( 98, 97), ( 99, 98),
(100, 99), (101, 100), (102, 101), (103, 102),
(104, 103), (105, 104), (106, 105), (107, 106),
(108, 107), (109, 108), (110, 109), (111, 110),
(112, 111), (113, 112), (114, 113), (115, 114),
(116, 115), (117, 116), (118, 117), (119, 118),
(120, 119), (121, 120), (122, 121), (123, 122),
(124, 123), (125, 124), (126, 125), (127, 126),
(128, 127), (129, 128), (130, 129), (131, 130),
(132, 131), (133, 132), (134, 133), (135, 134),
(136, 135), (137, 136), (138, 137), (139, 138),
(140, 139), (141, 140), (142, 141), (143, 142),
(144, 143), (145, 144), (146, 145), (147, 146),
(148, 147), (149, 148), (150, 149), (151, 150),
(152, 151), (153, 152), (154, 153), (155, 154),
(156, 155), (157, 156), (158, 157), (159, 158),
(160, 159), (161, 160), (162, 161), (163, 162),
(164, 163), (165, 164), (166, 165), (167, 166),
(168, 167), (169, 168), (170, 169), (171, 170),
(172, 171), (173, 172), (174, 173), (175, 174),
(176, 175), (177, 176), (178, 177), (179, 178),
(180, 179), (181, 180), (182, 181), (183, 182),
(184, 183), (185, 184), (186, 185), (187, 186),
(188, 187), (189, 188), (190, 189), (191, 190),
(192, 191), (193, 192), (194, 193), (195, 194),
(196, 195), (197, 196), (198, 197), (199, 198),
(200, 199), (201, 200), (202, 201), (203, 202),
(204, 203), (205, 204), (206, 205), (207, 206),
(208, 207), (209, 208), (210, 209), (211, 210),
(212, 211), (213, 212), (214, 213), (215, 214),
(216, 215), (217, 216), (218, 217), (219, 218),
(220, 219), (221, 220), (222, 221), (223, 222),
(224, 223), (225, 224), (226, 225), (227, 226),
(228, 227), (229, 228), (230, 229), (231, 230),
(232, 231), (233, 232), (234, 233), (235, 234),
(236, 235), (237, 236), (238, 237), (239, 238),
(240, 239), (241, 240), (242, 241), (243, 242),
(244, 243), (245, 244), (246, 245), (247, 246),
(248, 247), (249, 248), (250, 249), (251, 250),
(252, 251), (253, 252), (254, 253), (255, 254);
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
delete from t1 where id=0;
Got one of the listed errors
delete from t1 where id=255;
delete from t1 where id=15;
delete from t1 where id=0;
drop table t1;
CREATE TABLE t1 (col1 int(1))ENGINE=InnoDB;
......@@ -1758,10 +1695,10 @@ variable_value
16384
SELECT variable_value - @innodb_rows_deleted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_deleted';
variable_value - @innodb_rows_deleted_orig
311
71
SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted';
variable_value - @innodb_rows_inserted_orig
1204
964
SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated';
variable_value - @innodb_rows_updated_orig
866
......
......@@ -176,7 +176,6 @@ set session transaction isolation level read uncommitted;
start transaction;
select f1, right(f2, 20) as p2 from t1;
f1 p2
10 --------------------
select f1, right(f2, 20) as p2 from t2;
f1 p2
select f1, right(f2, 20) as p2 from t3;
......@@ -266,7 +265,6 @@ set session transaction isolation level read uncommitted;
start transaction;
select f1, f2 from t1;
f1 f2
2 28
select f1, f2 from t2;
f1 f2
select f1, f2 from t3;
......
......@@ -236,4 +236,44 @@ DELETE FROM t1 WHERE id = 1;
SELECT * FROM t2;
DROP TABLE t2, t1;
--echo #
--echo # MDEV-15199 Referential integrity broken in ON DELETE CASCADE
--echo #
CREATE TABLE member (id int AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO member VALUES (1);
CREATE TABLE address (
id int AUTO_INCREMENT PRIMARY KEY,
member_id int NOT NULL,
KEY address_FI_1 (member_id),
CONSTRAINT address_FK_1 FOREIGN KEY (member_id) REFERENCES member (id)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO address VALUES (2,1);
CREATE TABLE payment_method (
id int AUTO_INCREMENT PRIMARY KEY,
member_id int NOT NULL,
cardholder_address_id int DEFAULT NULL,
KEY payment_method_FI_1 (member_id),
KEY payment_method_FI_2 (cardholder_address_id),
CONSTRAINT payment_method_FK_1 FOREIGN KEY (member_id) REFERENCES member (id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT payment_method_FK_2 FOREIGN KEY (cardholder_address_id) REFERENCES address (id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB;
INSERT INTO payment_method VALUES (3,1,2);
BEGIN;
UPDATE member SET id=42;
SELECT * FROM member;
SELECT * FROM address;
SELECT * FROM payment_method;
DELETE FROM member;
COMMIT;
SELECT * FROM member;
SELECT * FROM address;
SELECT * FROM payment_method;
DROP TABLE payment_method,address,member;
--source include/wait_until_count_sessions.inc
......@@ -1058,82 +1058,18 @@ DROP TABLE t2,t1;
#
# test for FK cascade depth limit
#
call mtr.add_suppression("Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 255. Please drop excessive foreign constraints and try again");
call mtr.add_suppression("Cannot delete/update rows with cascading foreign key constraints that exceed max depth of 15\\. Please drop excessive foreign constraints and try again");
create table t1(
id int primary key,
pid int,
index(pid),
foreign key(pid) references t1(id) on delete cascade) engine=innodb;
insert into t1 values
( 0, 0), ( 1, 0), ( 2, 1), ( 3, 2),
( 4, 3), ( 5, 4), ( 6, 5), ( 7, 6),
( 8, 7), ( 9, 8), ( 10, 9), ( 11, 10),
( 12, 11), ( 13, 12), ( 14, 13), ( 15, 14),
( 16, 15), ( 17, 16), ( 18, 17), ( 19, 18),
( 20, 19), ( 21, 20), ( 22, 21), ( 23, 22),
( 24, 23), ( 25, 24), ( 26, 25), ( 27, 26),
( 28, 27), ( 29, 28), ( 30, 29), ( 31, 30),
( 32, 31), ( 33, 32), ( 34, 33), ( 35, 34),
( 36, 35), ( 37, 36), ( 38, 37), ( 39, 38),
( 40, 39), ( 41, 40), ( 42, 41), ( 43, 42),
( 44, 43), ( 45, 44), ( 46, 45), ( 47, 46),
( 48, 47), ( 49, 48), ( 50, 49), ( 51, 50),
( 52, 51), ( 53, 52), ( 54, 53), ( 55, 54),
( 56, 55), ( 57, 56), ( 58, 57), ( 59, 58),
( 60, 59), ( 61, 60), ( 62, 61), ( 63, 62),
( 64, 63), ( 65, 64), ( 66, 65), ( 67, 66),
( 68, 67), ( 69, 68), ( 70, 69), ( 71, 70),
( 72, 71), ( 73, 72), ( 74, 73), ( 75, 74),
( 76, 75), ( 77, 76), ( 78, 77), ( 79, 78),
( 80, 79), ( 81, 80), ( 82, 81), ( 83, 82),
( 84, 83), ( 85, 84), ( 86, 85), ( 87, 86),
( 88, 87), ( 89, 88), ( 90, 89), ( 91, 90),
( 92, 91), ( 93, 92), ( 94, 93), ( 95, 94),
( 96, 95), ( 97, 96), ( 98, 97), ( 99, 98),
(100, 99), (101, 100), (102, 101), (103, 102),
(104, 103), (105, 104), (106, 105), (107, 106),
(108, 107), (109, 108), (110, 109), (111, 110),
(112, 111), (113, 112), (114, 113), (115, 114),
(116, 115), (117, 116), (118, 117), (119, 118),
(120, 119), (121, 120), (122, 121), (123, 122),
(124, 123), (125, 124), (126, 125), (127, 126),
(128, 127), (129, 128), (130, 129), (131, 130),
(132, 131), (133, 132), (134, 133), (135, 134),
(136, 135), (137, 136), (138, 137), (139, 138),
(140, 139), (141, 140), (142, 141), (143, 142),
(144, 143), (145, 144), (146, 145), (147, 146),
(148, 147), (149, 148), (150, 149), (151, 150),
(152, 151), (153, 152), (154, 153), (155, 154),
(156, 155), (157, 156), (158, 157), (159, 158),
(160, 159), (161, 160), (162, 161), (163, 162),
(164, 163), (165, 164), (166, 165), (167, 166),
(168, 167), (169, 168), (170, 169), (171, 170),
(172, 171), (173, 172), (174, 173), (175, 174),
(176, 175), (177, 176), (178, 177), (179, 178),
(180, 179), (181, 180), (182, 181), (183, 182),
(184, 183), (185, 184), (186, 185), (187, 186),
(188, 187), (189, 188), (190, 189), (191, 190),
(192, 191), (193, 192), (194, 193), (195, 194),
(196, 195), (197, 196), (198, 197), (199, 198),
(200, 199), (201, 200), (202, 201), (203, 202),
(204, 203), (205, 204), (206, 205), (207, 206),
(208, 207), (209, 208), (210, 209), (211, 210),
(212, 211), (213, 212), (214, 213), (215, 214),
(216, 215), (217, 216), (218, 217), (219, 218),
(220, 219), (221, 220), (222, 221), (223, 222),
(224, 223), (225, 224), (226, 225), (227, 226),
(228, 227), (229, 228), (230, 229), (231, 230),
(232, 231), (233, 232), (234, 233), (235, 234),
(236, 235), (237, 236), (238, 237), (239, 238),
(240, 239), (241, 240), (242, 241), (243, 242),
(244, 243), (245, 244), (246, 245), (247, 246),
(248, 247), (249, 248), (250, 249), (251, 250),
(252, 251), (253, 252), (254, 253), (255, 254);
insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),
(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);
--error ER_GET_ERRMSG,ER_ROW_IS_REFERENCED_2
delete from t1 where id=0;
delete from t1 where id=255;
--error 0,ER_ROW_IS_REFERENCED_2
delete from t1 where id=15;
delete from t1 where id=0;
drop table t1;
......
......@@ -300,7 +300,7 @@ result in recursive cascading calls. This defines the maximum number of
such cascading deletes/updates allowed. When exceeded, the delete from
parent table will fail, and user has to drop excessive foreign constraint
before proceeds. */
#define FK_MAX_CASCADE_DEL 255
#define FK_MAX_CASCADE_DEL 15
/**********************************************************************//**
Creates a table memory object.
......
......@@ -297,6 +297,18 @@ row_create_update_node_for_mysql(
/*=============================*/
dict_table_t* table, /*!< in: table to update */
mem_heap_t* heap); /*!< in: mem heap from which allocated */
/**********************************************************************//**
Does a cascaded delete or set null in a foreign key operation.
@return error code or DB_SUCCESS */
dberr_t
row_update_cascade_for_mysql(
/*=========================*/
que_thr_t* thr, /*!< in: query thread */
upd_node_t* node, /*!< in: update node used in the cascade
or set null operation */
dict_table_t* table) /*!< in: table where we do the operation */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*********************************************************************//**
Locks the data dictionary exclusively for performing a table create or other
data dictionary modification operation. */
......
......@@ -32,20 +32,10 @@ Created 12/27/1996 Heikki Tuuri
#include "btr0types.h"
#include "dict0types.h"
#include "trx0types.h"
#include <stack>
#include "btr0pcur.h"
#include "que0types.h"
#include "pars0types.h"
/** The std::deque to store cascade update nodes, that uses mem_heap_t
as allocator. */
typedef std::deque<upd_node_t*, mem_heap_allocator<upd_node_t*> >
deque_mem_heap_t;
/** Double-ended queue of update nodes to be processed for cascade
operations */
typedef deque_mem_heap_t upd_cascade_t;
/*********************************************************************//**
Creates an update vector object.
@return own: update vector object */
......@@ -523,38 +513,12 @@ struct upd_node_t{
dict_foreign_t* foreign;/* NULL or pointer to a foreign key
constraint if this update node is used in
doing an ON DELETE or ON UPDATE operation */
bool cascade_top;
/*!< true if top level in cascade */
upd_cascade_t* cascade_upd_nodes;
/*!< Queue of update nodes to handle the
cascade of update and delete operations in an
iterative manner. Their parent/child
relations are properly maintained. All update
nodes point to this same queue. All these
nodes are allocated in heap pointed to by
upd_node_t::cascade_heap. */
upd_cascade_t* new_upd_nodes;
/*!< Intermediate list of update nodes in a
cascading update/delete operation. After
processing one update node, this will be
concatenated to cascade_upd_nodes. This extra
list is needed so that retry because of
DB_LOCK_WAIT works corrrectly. */
upd_cascade_t* processed_cascades;
/*!< List of processed update nodes in a
cascading update/delete operation. All the
cascade nodes are stored here, so that memory
can be freed. */
upd_node_t* cascade_node;/* NULL or an update node template which
is used to implement ON DELETE/UPDATE CASCADE
or ... SET NULL for foreign keys */
mem_heap_t* cascade_heap;
/*!< NULL or a mem heap where cascade_upd_nodes
are created. This heap is owned by the node
that has cascade_top=true. */
/*!< NULL or a mem heap where cascade
node is created.*/
sel_node_t* select; /*!< query graph subtree implementing a base
table cursor: the rows returned will be
updated */
......@@ -601,25 +565,8 @@ struct upd_node_t{
sym_node_t* table_sym;/* table node in symbol table */
que_node_t* col_assign_list;
/* column assignment list */
doc_id_t fts_doc_id;
/* The FTS doc id of the row that is now
pointed to by the pcur. */
doc_id_t fts_next_doc_id;
/* The new fts doc id that will be used
in update operation */
ulint magic_n;
#ifndef DBUG_OFF
/** Print information about this object into the trace log file. */
void dbug_trace();
/** Ensure that the member cascade_upd_nodes has only one update node
for each of the tables. This is useful for testing purposes. */
void check_cascade_only_once();
#endif /* !DBUG_OFF */
};
#define UPD_NODE_MAGIC_N 1579975
......
......@@ -482,20 +482,17 @@ que_graph_free_recursive(
case QUE_NODE_UPDATE:
upd = static_cast<upd_node_t*>(node);
DBUG_PRINT("que_graph_free_recursive",
("QUE_NODE_UPDATE: %p, processed_cascades: %p",
upd, upd->processed_cascades));
if (upd->in_mysql_interface) {
btr_pcur_free_for_mysql(upd->pcur);
upd->in_mysql_interface = FALSE;
}
if (upd->cascade_top) {
que_graph_free_recursive(upd->cascade_node);
if (upd->cascade_heap) {
mem_heap_free(upd->cascade_heap);
upd->cascade_heap = NULL;
upd->cascade_top = false;
}
que_graph_free_recursive(upd->select);
......
......@@ -475,9 +475,9 @@ row_ins_cascade_calc_update_vec(
type is != 0 */
mem_heap_t* heap, /*!< in: memory heap to use as
temporary storage */
trx_t* trx, /*!< in: update transaction */
upd_node_t* cascade) /*!< in: cascade update node */
trx_t* trx) /*!< in: update transaction */
{
upd_node_t* cascade = node->cascade_node;
dict_table_t* table = foreign->foreign_table;
dict_index_t* index = foreign->foreign_index;
upd_t* update;
......@@ -702,13 +702,13 @@ row_ins_cascade_calc_update_vec(
fts_get_next_doc_id(table, next_doc_id);
doc_id = fts_update_doc_id(table, ufield, next_doc_id);
n_fields_updated++;
cascade->fts_next_doc_id = doc_id;
fts_trx_add_op(trx, table, doc_id, FTS_INSERT, NULL);
} else {
if (doc_id_updated) {
ut_ad(new_doc_id);
cascade->fts_next_doc_id = new_doc_id;
fts_trx_add_op(trx, table, new_doc_id,
FTS_INSERT, NULL);
} else {
cascade->fts_next_doc_id = FTS_NULL_DOC_ID;
ib::error() << "FTS Doc ID must be updated"
" along with FTS indexed column for"
" table " << table->name;
......@@ -1121,18 +1121,14 @@ row_ins_foreign_check_on_constraint(
DBUG_RETURN(DB_ROW_IS_REFERENCED);
}
cascade = row_create_update_node_for_mysql(table, node->cascade_heap);
que_node_set_parent(cascade, node);
/* For the cascaded operation, all the update nodes are allocated in
the same heap. All the update nodes will point to the same heap.
This heap is owned by the first update node. And it must be freed
only in the first update node */
cascade->cascade_heap = node->cascade_heap;
cascade->cascade_upd_nodes = node->cascade_upd_nodes;
cascade->new_upd_nodes = node->new_upd_nodes;
cascade->processed_cascades = node->processed_cascades;
if (node->cascade_node == NULL) {
node->cascade_heap = mem_heap_create(128);
node->cascade_node = row_create_update_node_for_mysql(
table, node->cascade_heap);
que_node_set_parent(node->cascade_node, node);
}
cascade = node->cascade_node;
cascade->table = table;
cascade->foreign = foreign;
if (!(cascade->is_delete = node->is_delete
......@@ -1274,17 +1270,8 @@ row_ins_foreign_check_on_constraint(
if (node->is_delete
? (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
: (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)) {
/* Build the appropriate update vector which sets
foreign->n_fields first fields in rec to SQL NULL */
if (table->fts) {
/* For the clause ON DELETE SET NULL, the cascade
operation is actually an update operation with the new
values being null. For FTS, this means that the old
values be deleted and no new values to be added.*/
cascade->fts_next_doc_id = FTS_NULL_DOC_ID;
}
update = cascade->update;
......@@ -1323,7 +1310,7 @@ row_ins_foreign_check_on_constraint(
}
if (affects_fulltext) {
cascade->fts_doc_id = doc_id;
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
if (foreign->v_cols != NULL
......@@ -1353,7 +1340,7 @@ row_ins_foreign_check_on_constraint(
}
if (affects_fulltext) {
cascade->fts_doc_id = doc_id;
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
}
......@@ -1364,7 +1351,7 @@ row_ins_foreign_check_on_constraint(
foreign->n_fields first fields in rec to new values */
bool affects_fulltext = row_ins_cascade_calc_update_vec(
node, foreign, cascade->cascade_heap, trx, cascade);
node, foreign, tmp_heap, trx);
if (foreign->v_cols && !foreign->v_cols->empty()) {
row_ins_foreign_fill_virtual(
......@@ -1403,7 +1390,7 @@ row_ins_foreign_check_on_constraint(
/* Mark the old Doc ID as deleted */
if (affects_fulltext) {
ut_ad(table->fts);
cascade->fts_doc_id = doc_id;
fts_trx_add_op(trx, table, doc_id, FTS_DELETE, NULL);
}
}
......@@ -1432,11 +1419,8 @@ row_ins_foreign_check_on_constraint(
"WSREP: foreign key append failed: %d\n", err);
} else
#endif /* WITH_WSREP */
node->new_upd_nodes->push_back(cascade);
my_atomic_addlint(&table->n_foreign_key_checks_running, 1);
ut_ad(foreign->foreign_table->n_foreign_key_checks_running > 0);
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);
/* Release the data dictionary latch for a while, so that we do not
starve other threads from doing CREATE TABLE etc. if we have a huge
......@@ -1463,7 +1447,6 @@ row_ins_foreign_check_on_constraint(
DBUG_RETURN(err);
nonstandard_exit_func:
que_graph_free_recursive(cascade);
if (tmp_heap) {
mem_heap_free(tmp_heap);
......@@ -1948,6 +1931,12 @@ row_ins_check_foreign_constraints(
row_mysql_freeze_data_dictionary(trx);
}
if (referenced_table) {
my_atomic_addlint(
&foreign->foreign_table
->n_foreign_key_checks_running, 1);
}
/* NOTE that if the thread ends up waiting for a lock
we will release dict_operation_lock temporarily!
But the counter on the table protects the referenced
......@@ -1956,6 +1945,12 @@ row_ins_check_foreign_constraints(
err = row_ins_check_foreign_constraint(
TRUE, foreign, table, entry, thr);
if (referenced_table) {
my_atomic_addlint(
&foreign->foreign_table
->n_foreign_key_checks_running, -1);
}
if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx);
}
......
This diff is collapsed.
......@@ -319,9 +319,20 @@ row_upd_check_references_constraints(
But the counter on the table protects 'foreign' from
being dropped while the check is running. */
if (foreign_table) {
my_atomic_addlint(
&foreign_table->n_foreign_key_checks_running,
1);
}
err = row_ins_check_foreign_constraint(
FALSE, foreign, table, entry, thr);
if (foreign_table) {
my_atomic_addlint(
&foreign_table->n_foreign_key_checks_running,
-1);
}
if (ref_table != NULL) {
dict_table_close(ref_table, FALSE, FALSE);
}
......@@ -342,11 +353,6 @@ row_upd_check_references_constraints(
mem_heap_free(heap);
DEBUG_SYNC_C("foreign_constraint_check_for_update_done");
DBUG_EXECUTE_IF("row_upd_cascade_lock_wait_err",
err = DB_LOCK_WAIT;
DBUG_SET("-d,row_upd_cascade_lock_wait_err"););
DBUG_RETURN(err);
}
......@@ -469,9 +475,8 @@ wsrep_must_process_fk(const upd_node_t* node, const trx_t* trx)
return false;
}
const upd_node_t* parent = static_cast<const upd_node_t*>(node->common.parent);
return parent->cascade_upd_nodes->empty();
return static_cast<upd_node_t*>(node->common.parent)->cascade_node
== node;
}
#endif /* WITH_WSREP */
......@@ -2151,7 +2156,6 @@ row_upd_store_v_row(
cascade update. And virtual
column can't be affected,
so it is Ok to set it to NULL */
ut_ad(!node->cascade_top);
dfield_set_null(dfield);
} else {
dfield_t* vfield
......@@ -3426,56 +3430,3 @@ row_upd_step(
DBUG_RETURN(thr);
}
#ifndef DBUG_OFF
/** Ensure that the member cascade_upd_nodes has only one update node
for each of the tables. This is useful for testing purposes. */
void upd_node_t::check_cascade_only_once()
{
DBUG_ENTER("upd_node_t::check_cascade_only_once");
dbug_trace();
for (upd_cascade_t::const_iterator i = cascade_upd_nodes->begin();
i != cascade_upd_nodes->end(); ++i) {
const upd_node_t* update_node = *i;
std::string table_name(update_node->table->name.m_name);
ulint count = 0;
for (upd_cascade_t::const_iterator j
= cascade_upd_nodes->begin();
j != cascade_upd_nodes->end(); ++j) {
const upd_node_t* node = *j;
if (table_name == node->table->name.m_name) {
DBUG_ASSERT(count++ == 0);
}
}
}
DBUG_VOID_RETURN;
}
/** Print information about this object into the trace log file. */
void upd_node_t::dbug_trace()
{
DBUG_ENTER("upd_node_t::dbug_trace");
for (upd_cascade_t::const_iterator i = cascade_upd_nodes->begin();
i != cascade_upd_nodes->end(); ++i) {
DBUG_LOG("upd_node_t", "cascade_upd_nodes: Cascade to table: "
<< (*i)->table->name);
}
for (upd_cascade_t::const_iterator j = new_upd_nodes->begin();
j != new_upd_nodes->end(); ++j) {
DBUG_LOG("upd_node_t", "new_upd_nodes: Cascade to table: "
<< (*j)->table->name);
}
DBUG_VOID_RETURN;
}
#endif /* !DBUG_OFF */
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