Merge 10.2 into 10.3

This commit is contained in:
Marko Mäkelä 2020-01-18 16:56:16 +02:00
commit 6373ec3ec7
36 changed files with 2729 additions and 358 deletions

View file

@ -14,7 +14,6 @@ MW-286 : MDEV-18464 Killing thread can cause mutex deadlock if done concurrently
MW-328A : MDEV-21483 galera.MW-328A galera.MW-328B
MW-328B : MDEV-21483 galera.MW-328A galera.MW-328B
MW-329 : MDEV-19962 Galera test failure on MW-329
MW-336 : MDEV-17062 Test failure on galera.MW-336
galera.galera_defaults : MDEV-21494 Galera test sporadic failure on galera.galera_defaults
galera_account_management : MariaDB 10.0 does not support ALTER USER
galera_as_master_gtid : Requires MySQL GTID

View file

@ -0,0 +1,119 @@
SET GLOBAL innodb_adaptive_hash_index = false;
SET GLOBAL innodb_stats_persistent = false;
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connect con2,localhost,root,,;
CREATE TABLE t1 (
a00 CHAR(255) NOT NULL DEFAULT 'a',
a01 CHAR(255) NOT NULL DEFAULT 'a',
a02 CHAR(255) NOT NULL DEFAULT 'a',
b INT NOT NULL DEFAULT 0,
PRIMARY KEY(a00, a01, a02)
) charset latin1 ENGINE = InnoDB COMMENT='MERGE_THRESHOLD=45';
SET GLOBAL innodb_limit_optimistic_insert_debug = 3;
CREATE PROCEDURE data_load_t1()
BEGIN
DECLARE c1 INT DEFAULT 97;
DECLARE c2 INT DEFAULT 97;
DECLARE c3 INT DEFAULT 97;
WHILE c1 < 102 DO
WHILE c2 < 123 DO
WHILE c3 < 123 DO
INSERT INTO t1 (a00) VALUES (CHAR(c1,c2,c3));
SET c3 = c3 + 1;
END WHILE;
SET c3 = 97;
SET c2 = c2 + 1;
END WHILE;
SET c2 = 97;
SET c1 = c1 + 1;
END WHILE;
END |
call data_load_t1();
DROP PROCEDURE data_load_t1;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
1856
connection con2;
DELETE FROM t1 WHERE a00 = 'cnm';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'cnm';
connection purge_control;
COMMIT;
connection con2;
SET GLOBAL innodb_limit_optimistic_insert_debug = 0;
ROLLBACK;
# Test start
connection purge_control;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection con2;
DELETE FROM t1 WHERE a00 = 'bii';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'bii';
SET DEBUG_SYNC = 'rollback_undo_pk SIGNAL roll1_wait WAIT_FOR roll2';
SET DEBUG_SYNC = 'rollback_purge_clust SIGNAL rollback_waiting WAIT_FOR resume';
ROLLBACK;
connection purge_control;
SET DEBUG_SYNC = 'now WAIT_FOR roll1_wait';
COMMIT;
SET DEBUG_SYNC = 'now SIGNAL roll2';
connect con1,localhost,root,,;
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting';
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
SELECT a00 FROM t1 WHERE a00 = 'bii';
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now SIGNAL resume';
connection con1;
a00
connection con2;
connection default;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
1856
DELETE FROM t1 WHERE a00 = 'dpn';
COMMIT;
INSERT INTO t1 SET a00 = 'dpn';
ROLLBACK;
ALTER TABLE t1 COMMENT='MERGE_THRESHOLD=35';
connection purge_control;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection con2;
DELETE FROM t1 WHERE a00 = 'cnd';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'cnd';
SET DEBUG_SYNC = 'rollback_undo_pk SIGNAL roll1_wait WAIT_FOR roll2';
SET DEBUG_SYNC = 'rollback_purge_clust SIGNAL rollback_waiting WAIT_FOR resume EXECUTE 2';
ROLLBACK;
connection purge_control;
SET DEBUG_SYNC = 'now WAIT_FOR roll1_wait';
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SET DEBUG_SYNC = 'now SIGNAL roll2';
connection con1;
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting TIMEOUT 1';
SET DEBUG_SYNC = 'now SIGNAL resume';
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting TIMEOUT 1';
disconnect purge_control;
connection default;
SET DEBUG_SYNC = 'now SIGNAL resume';
disconnect con1;
connection con2;
disconnect con2;
connection default;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
1856
SET DEBUG_SYNC = 'RESET';
DROP TABLE t1;

View file

@ -0,0 +1,372 @@
DROP TABLE IF EXISTS t1;
SET GLOBAL innodb_adaptive_hash_index = false;
SET GLOBAL innodb_stats_persistent = false;
connect con1,localhost,root,,;
connect con2,localhost,root,,;
connect con3,localhost,root,,;
CREATE TABLE t1 (
a00 CHAR(255) NOT NULL DEFAULT 'a',
a01 CHAR(255) NOT NULL DEFAULT 'a',
a02 CHAR(255) NOT NULL DEFAULT 'a',
a03 CHAR(255) NOT NULL DEFAULT 'a',
a04 CHAR(255) NOT NULL DEFAULT 'a',
a05 CHAR(255) NOT NULL DEFAULT 'a',
a06 CHAR(255) NOT NULL DEFAULT 'a',
b INT NOT NULL DEFAULT 0
) ENGINE = InnoDB;
ALTER TABLE t1 ADD PRIMARY KEY(
a00,
a01,
a02,
a03,
a04,
a05,
a06
);
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
1
SET GLOBAL innodb_limit_optimistic_insert_debug = 7;
INSERT INTO t1 (a00) VALUES ('aa');
INSERT INTO t1 (a00) VALUES ('ab');
INSERT INTO t1 (a00) VALUES ('ac');
INSERT INTO t1 (a00) VALUES ('ad');
INSERT INTO t1 (a00) VALUES ('ae');
INSERT INTO t1 (a00) VALUES ('af');
INSERT INTO t1 (a00) VALUES ('ag');
INSERT INTO t1 (a00) VALUES ('ah');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
3
INSERT INTO t1 (a00) VALUES ('ai');
INSERT INTO t1 (a00) VALUES ('aj');
INSERT INTO t1 (a00) VALUES ('ak');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
4
INSERT INTO t1 (a00) VALUES ('al');
INSERT INTO t1 (a00) VALUES ('am');
INSERT INTO t1 (a00) VALUES ('an');
INSERT INTO t1 (a00) VALUES ('ao');
INSERT INTO t1 (a00) VALUES ('ap');
INSERT INTO t1 (a00) VALUES ('aq');
INSERT INTO t1 (a00) VALUES ('ar');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
5
INSERT INTO t1 (a00) VALUES ('as');
INSERT INTO t1 (a00) VALUES ('at');
INSERT INTO t1 (a00) VALUES ('au');
INSERT INTO t1 (a00) VALUES ('av');
INSERT INTO t1 (a00) VALUES ('aw');
INSERT INTO t1 (a00) VALUES ('ax');
INSERT INTO t1 (a00) VALUES ('ay');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
6
INSERT INTO t1 (a00) VALUES ('az');
INSERT INTO t1 (a00) VALUES ('ba');
INSERT INTO t1 (a00) VALUES ('bb');
INSERT INTO t1 (a00) VALUES ('bc');
INSERT INTO t1 (a00) VALUES ('bd');
INSERT INTO t1 (a00) VALUES ('be');
INSERT INTO t1 (a00) VALUES ('bf');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
7
INSERT INTO t1 (a00) VALUES ('bg');
INSERT INTO t1 (a00) VALUES ('bh');
INSERT INTO t1 (a00) VALUES ('bi');
INSERT INTO t1 (a00) VALUES ('bj');
INSERT INTO t1 (a00) VALUES ('bk');
INSERT INTO t1 (a00) VALUES ('bl');
INSERT INTO t1 (a00) VALUES ('bm');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
8
INSERT INTO t1 (a00) VALUES ('bn');
INSERT INTO t1 (a00) VALUES ('bo');
INSERT INTO t1 (a00) VALUES ('bp');
INSERT INTO t1 (a00) VALUES ('bq');
INSERT INTO t1 (a00) VALUES ('br');
INSERT INTO t1 (a00) VALUES ('bs');
INSERT INTO t1 (a00) VALUES ('bt');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
11
INSERT INTO t1 (a00) VALUES ('bu');
INSERT INTO t1 (a00) VALUES ('bv');
INSERT INTO t1 (a00) VALUES ('bw');
INSERT INTO t1 (a00) VALUES ('bx');
INSERT INTO t1 (a00) VALUES ('by');
INSERT INTO t1 (a00) VALUES ('bz');
INSERT INTO t1 (a00) VALUES ('ca');
INSERT INTO t1 (a00) VALUES ('cb');
INSERT INTO t1 (a00) VALUES ('cc');
INSERT INTO t1 (a00) VALUES ('cd');
INSERT INTO t1 (a00) VALUES ('ce');
INSERT INTO t1 (a00) VALUES ('cf');
INSERT INTO t1 (a00) VALUES ('cg');
INSERT INTO t1 (a00) VALUES ('ch');
INSERT INTO t1 (a00) VALUES ('ci');
INSERT INTO t1 (a00) VALUES ('cj');
INSERT INTO t1 (a00) VALUES ('ck');
INSERT INTO t1 (a00) VALUES ('cl');
INSERT INTO t1 (a00) VALUES ('cm');
INSERT INTO t1 (a00) VALUES ('cn');
INSERT INTO t1 (a00) VALUES ('co');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
15
INSERT INTO t1 (a00) VALUES ('cp');
INSERT INTO t1 (a00) VALUES ('cq');
INSERT INTO t1 (a00) VALUES ('cr');
INSERT INTO t1 (a00) VALUES ('cs');
INSERT INTO t1 (a00) VALUES ('ct');
INSERT INTO t1 (a00) VALUES ('cu');
INSERT INTO t1 (a00) VALUES ('cv');
INSERT INTO t1 (a00) VALUES ('cw');
INSERT INTO t1 (a00) VALUES ('cx');
INSERT INTO t1 (a00) VALUES ('cy');
INSERT INTO t1 (a00) VALUES ('cz');
INSERT INTO t1 (a00) VALUES ('da');
INSERT INTO t1 (a00) VALUES ('db');
INSERT INTO t1 (a00) VALUES ('dc');
INSERT INTO t1 (a00) VALUES ('dd');
INSERT INTO t1 (a00) VALUES ('de');
INSERT INTO t1 (a00) VALUES ('df');
INSERT INTO t1 (a00) VALUES ('dg');
INSERT INTO t1 (a00) VALUES ('dh');
INSERT INTO t1 (a00) VALUES ('di');
INSERT INTO t1 (a00) VALUES ('dj');
INSERT INTO t1 (a00) VALUES ('dk');
INSERT INTO t1 (a00) VALUES ('dl');
INSERT INTO t1 (a00) VALUES ('dm');
INSERT INTO t1 (a00) VALUES ('dn');
INSERT INTO t1 (a00) VALUES ('do');
INSERT INTO t1 (a00) VALUES ('dp');
INSERT INTO t1 (a00) VALUES ('dq');
INSERT INTO t1 (a00) VALUES ('dr');
INSERT INTO t1 (a00) VALUES ('ds');
INSERT INTO t1 (a00) VALUES ('dt');
INSERT INTO t1 (a00) VALUES ('du');
INSERT INTO t1 (a00) VALUES ('dv');
INSERT INTO t1 (a00) VALUES ('dw');
INSERT INTO t1 (a00) VALUES ('dx');
INSERT INTO t1 (a00) VALUES ('dy');
INSERT INTO t1 (a00) VALUES ('dz');
INSERT INTO t1 (a00) VALUES ('ea');
INSERT INTO t1 (a00) VALUES ('eb');
INSERT INTO t1 (a00) VALUES ('ec');
INSERT INTO t1 (a00) VALUES ('ed');
INSERT INTO t1 (a00) VALUES ('ee');
INSERT INTO t1 (a00) VALUES ('ef');
INSERT INTO t1 (a00) VALUES ('eg');
INSERT INTO t1 (a00) VALUES ('eh');
INSERT INTO t1 (a00) VALUES ('ei');
INSERT INTO t1 (a00) VALUES ('ej');
INSERT INTO t1 (a00) VALUES ('ek');
INSERT INTO t1 (a00) VALUES ('el');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
23
INSERT INTO t1 (a00) VALUES ('em');
INSERT INTO t1 (a00) VALUES ('en');
INSERT INTO t1 (a00) VALUES ('eo');
INSERT INTO t1 (a00) VALUES ('ep');
INSERT INTO t1 (a00) VALUES ('eq');
INSERT INTO t1 (a00) VALUES ('er');
INSERT INTO t1 (a00) VALUES ('es');
INSERT INTO t1 (a00) VALUES ('et');
INSERT INTO t1 (a00) VALUES ('eu');
INSERT INTO t1 (a00) VALUES ('ev');
INSERT INTO t1 (a00) VALUES ('ew');
INSERT INTO t1 (a00) VALUES ('ex');
INSERT INTO t1 (a00) VALUES ('ey');
INSERT INTO t1 (a00) VALUES ('ez');
INSERT INTO t1 (a00) VALUES ('fa');
INSERT INTO t1 (a00) VALUES ('fb');
INSERT INTO t1 (a00) VALUES ('fc');
INSERT INTO t1 (a00) VALUES ('fd');
INSERT INTO t1 (a00) VALUES ('fe');
INSERT INTO t1 (a00) VALUES ('ff');
INSERT INTO t1 (a00) VALUES ('fg');
INSERT INTO t1 (a00) VALUES ('fh');
INSERT INTO t1 (a00) VALUES ('fi');
INSERT INTO t1 (a00) VALUES ('fj');
INSERT INTO t1 (a00) VALUES ('fk');
INSERT INTO t1 (a00) VALUES ('fl');
INSERT INTO t1 (a00) VALUES ('fm');
INSERT INTO t1 (a00) VALUES ('fn');
INSERT INTO t1 (a00) VALUES ('fo');
INSERT INTO t1 (a00) VALUES ('fp');
INSERT INTO t1 (a00) VALUES ('fq');
INSERT INTO t1 (a00) VALUES ('fr');
INSERT INTO t1 (a00) VALUES ('fs');
INSERT INTO t1 (a00) VALUES ('ft');
INSERT INTO t1 (a00) VALUES ('fu');
INSERT INTO t1 (a00) VALUES ('fv');
INSERT INTO t1 (a00) VALUES ('fw');
INSERT INTO t1 (a00) VALUES ('fx');
INSERT INTO t1 (a00) VALUES ('fy');
INSERT INTO t1 (a00) VALUES ('fz');
INSERT INTO t1 (a00) VALUES ('ga');
INSERT INTO t1 (a00) VALUES ('gb');
INSERT INTO t1 (a00) VALUES ('gc');
INSERT INTO t1 (a00) VALUES ('gd');
INSERT INTO t1 (a00) VALUES ('ge');
INSERT INTO t1 (a00) VALUES ('gf');
INSERT INTO t1 (a00) VALUES ('gg');
INSERT INTO t1 (a00) VALUES ('gh');
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
29
SET GLOBAL innodb_limit_optimistic_insert_debug = 0;
# Test start
SET DEBUG_SYNC = 'RESET';
INSERT INTO t1 (a00) VALUES ('bfa');
connection con1;
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
INSERT INTO t1 (a00) VALUES ('bfb');
connection con2;
SET DEBUG_SYNC = 'now WAIT_FOR reached';
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
a00 a01
aa a
SELECT a00,a01 FROM t1 WHERE a00 = 'aq';
a00 a01
aq a
SELECT a00,a01 FROM t1 WHERE a00 = 'cp';
a00 a01
cp a
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
a00 a01
el a
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
SELECT a00,a01 FROM t1 WHERE a00 = 'ar';
connection con3;
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
SELECT a00,a01 FROM t1 WHERE a00 = 'cn';
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
connection con1;
connection con2;
a00 a01
ar a
connection con3;
a00 a01
cn a
connection default;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
30
SET DEBUG_SYNC = 'RESET';
INSERT INTO t1 (a00) VALUES ('coa');
connection con1;
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
INSERT INTO t1 (a00) VALUES ('cob');
connection con2;
SET DEBUG_SYNC = 'now WAIT_FOR reached';
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
connection con3;
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
connection con1;
connection con2;
a00 a01
aa a
connection con3;
a00 a01
el a
connection default;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
31
SET DEBUG_SYNC = 'RESET';
INSERT INTO t1 (a00) VALUES ('gba');
connection con1;
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
INSERT INTO t1 (a00) VALUES ('gbb');
connection con2;
SET DEBUG_SYNC = 'now WAIT_FOR reached';
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
a00 a01
aa a
SELECT a00,a01 FROM t1 WHERE a00 = 'ek';
a00 a01
ek a
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
connection con3;
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
SELECT a00,a01 FROM t1 WHERE a00 = 'gb';
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
connection con1;
connection con2;
a00 a01
el a
connection con3;
a00 a01
gb a
connection default;
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status OK
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
CLUST_INDEX_SIZE
32
SET DEBUG_SYNC = 'RESET';
connection default;
disconnect con1;
disconnect con2;
disconnect con3;
DROP TABLE t1;

View file

@ -0,0 +1,449 @@
CREATE SCHEMA my_schema;
USE my_schema;
CREATE FUNCTION f_thread_id (i INT) RETURNS CHAR(4) DETERMINISTIC
RETURN CONCAT(LPAD(CAST(i AS CHAR),3,'_'),'_') ;
SELECT CONCAT('->', f_thread_id( 1), '<-');
CONCAT('->', f_thread_id( 1), '<-')
->__1_<-
SELECT CONCAT('->', f_thread_id(12), '<-');
CONCAT('->', f_thread_id(12), '<-')
->_12_<-
SET @extra_int = 1;
SET @extra_string = f_thread_id(@extra_int);
SELECT @extra_int , @extra_string;
@extra_int @extra_string
1 __1_
CREATE FUNCTION f_col_int1 (i INT) RETURNS INT(20) DETERMINISTIC
RETURN i * 1000 + @extra_int ;
SELECT f_col_int1(my_col) AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
1001
12001
123001
1234001
12345001
CREATE FUNCTION f_col_int2 (i INT) RETURNS INT(20) DETERMINISTIC
RETURN @extra_int * 10000000 + i ;
SELECT f_col_int2(my_col) AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
10000001
10000012
10000123
10001234
10012345
CREATE FUNCTION f_col_int3 (i INT) RETURNS INT(20) DETERMINISTIC
RETURN @extra_int ;
SELECT f_col_int3(my_col) AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
1
1
1
1
1
CREATE FUNCTION f_col_blob (i INT) RETURNS BLOB DETERMINISTIC
RETURN RPAD(@extra_string,(@@innodb_page_size / 2 ) + 1,'a');
SELECT CONCAT('->', SUBSTR(f_col_blob(my_col) FROM 1 FOR 10),
'<-.....->', SUBSTR(f_col_blob(my_col) FROM -10 FOR 10), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
->__1_aaaaaa<-.....->aaaaaaaaaa<-
->__1_aaaaaa<-.....->aaaaaaaaaa<-
->__1_aaaaaa<-.....->aaaaaaaaaa<-
->__1_aaaaaa<-.....->aaaaaaaaaa<-
->__1_aaaaaa<-.....->aaaaaaaaaa<-
CREATE FUNCTION f_col_char0 (i INT) RETURNS CHAR(255) DETERMINISTIC
RETURN LPAD(CAST(i AS CHAR),255,' ');
SELECT CONCAT('->', f_col_char0(my_col), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
-> 1<-
-> 12<-
-> 123<-
-> 1234<-
-> 12345<-
CREATE FUNCTION f_col_char1 (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',
LPAD(SUBSTR(CAST(i AS CHAR),1,(LENGTH(CAST(i AS CHAR)) DIV 2)),10,' '),
@extra_string,
RPAD(SUBSTR(CAST(i AS CHAR), -((LENGTH(CAST(i AS CHAR)) + 1) DIV 2)),10,' '),
'E') ;
SELECT CONCAT('->', f_col_char1(my_col), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
->B __1_1 E<-
->B 1__1_2 E<-
->B 1__1_23 E<-
->B 12__1_34 E<-
->B 12__1_345 E<-
CREATE FUNCTION f_col_char2 (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',
RPAD(SUBSTR(CAST(i AS CHAR),1,(LENGTH(CAST(i AS CHAR)) DIV 2)),10,' '),
@extra_string,
LPAD(SUBSTR(CAST(i AS CHAR), -((LENGTH(CAST(i AS CHAR)) + 1) DIV 2)),10,' '),
'E');
SELECT CONCAT('->', f_col_char2(my_col), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
->B __1_ 1E<-
->B1 __1_ 2E<-
->B1 __1_ 23E<-
->B12 __1_ 34E<-
->B12 __1_ 345E<-
CREATE FUNCTION f_col_char3 (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',@extra_string,LPAD(CAST(i AS CHAR),20,' '),'E');
SELECT CONCAT('->', f_col_char3(my_col), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
->B__1_ 1E<-
->B__1_ 12E<-
->B__1_ 123E<-
->B__1_ 1234E<-
->B__1_ 12345E<-
CREATE FUNCTION f_col_char4 (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',RPAD(CAST(i AS CHAR),20,' '),@extra_string,'E');
SELECT CONCAT('->', f_col_char4(my_col), '<-') AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
my_result
->B1 __1_E<-
->B12 __1_E<-
->B123 __1_E<-
->B1234 __1_E<-
->B12345 __1_E<-
CREATE TABLE my_metrics LIKE information_schema.innodb_metrics;
ALTER TABLE my_metrics ADD COLUMN phase ENUM('after', 'before'),
DROP COLUMN SUBSYSTEM, DROP COLUMN TYPE, DROP COLUMN COMMENT,
ADD PRIMARY KEY (NAME,phase);
CREATE TABLE t1 (
col_int0 BIGINT,
col_int1 BIGINT,
col_int2 BIGINT,
col_int3 BIGINT,
col_blob BLOB,
col_char0 VARCHAR(255),
col_char1 VARCHAR(30),
col_char2 VARCHAR(30),
col_char3 VARCHAR(30),
col_char4 VARCHAR(30)
) ENGINE = InnoDB;
ALTER TABLE t1 ADD UNIQUE KEY uidx_col_int0 (col_int0),
ADD UNIQUE KEY uidx1 (col_int1, col_char0),
ADD UNIQUE KEY uidx2 (col_int2, col_char0, col_int1),
ADD UNIQUE KEY uidx3 (col_int3, col_int2, col_char0),
ADD UNIQUE KEY uidx4 (col_char1, col_char0),
ADD UNIQUE KEY uidx5 (col_char2, col_char0, col_char1),
ADD UNIQUE KEY uidx6 (col_char3, col_char2, col_char0),
ADD UNIQUE KEY uidx7 (col_int1, col_int2, col_int3, col_char4,
col_char1, col_char2, col_char3, col_char0),
ADD KEY idx8 (col_blob(10), col_char4);
CREATE PROCEDURE proc_fill_t1 (max_row_count INT, load_unit INT)
BEGIN
DECLARE my_count INTEGER DEFAULT 0;
DECLARE max_load_count INTEGER DEFAULT 0;
DROP TABLE IF EXISTS t0;
CREATE TEMPORARY TABLE t0 (col_int0 BIGINT, PRIMARY KEY(col_int0));
WHILE (my_count < load_unit ) DO
SET my_count = my_count + 1;
INSERT INTO t0 SET col_int0 = my_count;
END WHILE;
SET max_load_count = (SELECT (max_row_count DIV load_unit) + 1 );
SELECT COUNT(col_int0) INTO @val FROM t1;
SET my_count = 0;
REPEAT
INSERT INTO t1 (col_int0, col_int1, col_int2, col_int3, col_blob,
col_char0, col_char1, col_char2,col_char3,col_char4)
SELECT col_int0 + @val,
f_col_int1(col_int0 + @val),
f_col_int2(col_int0 + @val),
f_col_int3(col_int0 + @val),
f_col_blob(col_int0 + @val),
f_col_char0(col_int0 + @val),
f_col_char1(col_int0 + @val),
f_col_char2(col_int0 + @val),
f_col_char3(col_int0 + @val),
f_col_char4(col_int0 + @val)
FROM t0;
COMMIT;
SELECT MAX(col_int0) INTO @val FROM t1;
SET my_count = my_count + 1;
UNTIL( my_count > max_load_count OR @val >= max_row_count )
END REPEAT;
DROP TEMPORARY TABLE t0;
END|
CREATE PROCEDURE proc_dml (max_duration INT, t1_stripe_half INT)
BEGIN
DECLARE aux INTEGER DEFAULT 0;
DECLARE start_time INT;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND BEGIN END;
SET @extra_int = CONNECTION_ID();
SET @extra_string = f_thread_id(@extra_int);
SELECT ROUND(MAX(col_int0) / 2 ) INTO @t1_half FROM t1;
# The user lock 'Blocker' should be already set by some other session S1.
# S1 starts the race by releasing that lock.
# Wait till the lock is released and the lock can be obtained.
# In order to prevent endless waiting in case of non foreseen problems
# limit the timespan to 30 seconds.
SELECT GET_LOCK('Blocker', 30) INTO @aux;
# Release the lock immediate so that the other "runner" sessions start too.
SELECT RELEASE_LOCK('Blocker') INTO @aux;
SET start_time = UNIX_TIMESTAMP();
WHILE (UNIX_TIMESTAMP() - start_time < max_duration) DO
SET @aux = @t1_half - t1_stripe_half + ROUND(RAND() * t1_stripe_half * 2);
UPDATE t1 SET
col_int1 = f_col_int1(col_int0),
col_int2 = f_col_int2(col_int0),
col_int3 = f_col_int3(col_int0),
col_blob = f_col_blob(col_int0),
col_char0 = f_col_char0(col_int0),
col_char1 = f_col_char1(col_int0),
col_char2 = f_col_char2(col_int0),
col_char3 = f_col_char3(col_int0),
col_char4 = f_col_char4(col_int0)
WHERE col_int0 = @aux;
COMMIT;
END WHILE;
END|
SET GLOBAL innodb_monitor_disable = "innodb_rwlock_sx_%";
SET @pre_reset_ts = NOW();
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
SET @pre_enable_ts = NOW();
SET GLOBAL innodb_monitor_enable = "innodb_rwlock_sx_%";
SET @pre_collect_ts = NOW();
DELETE FROM my_metrics;
INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'before'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
# TC-01 There are exact three entries "innodb_rwlock_sx_%" with the
# with the name which follow in innodb_metrics.
# pass
SELECT COUNT(*) INTO @sx_count FROM my_metrics;
# TC-02 Counting is now enabled. ALL = @sx_count entries show that.
# pass
# TC-03 @pre_reset_ts < TIME_RESET. ALL = @sx_count entries show that.
# pass
# TC-04 @pre_enable_ts < TIME_ENABLED. ALL = @sx_count entries show that.
# pass
# TC-05 TIME_RESET < TIME_ENABLED AND TIME_ENABLED < @pre_collect_ts
# AND TIME_ELAPSED > 0. ALL = @sx_count entries show that.
# pass
# TC-06 COUNT_RESET = MAX_COUNT_RESET. ALL = @sx_count entries show that.
# pass
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
SHOW ENGINE INNODB STATUS;
DELETE FROM my_metrics;
INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'before'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
SET @extra_string = '__0_';
SET @extra_int = 0;
# TC-07 One session inserts some significant amount of rows into t1.
# The system MUST survive that.
SET @max_row_count = <max_row_count>;
SET @load_unit = <load_unit>;
SET @start_time = UNIX_TIMESTAMP();
SET AUTOCOMMIT = OFF;
CALL proc_fill_t1 (@max_row_count, @load_unit);
# pass
SET AUTOCOMMIT = ON;
SELECT col_int0 INTO @t1_half FROM t1
WHERE col_int0 >= (@val DIV 2) ORDER BY col_int0 LIMIT 1;
SHOW ENGINE INNODB STATUS;
SELECT col_int0, col_int1, col_int2, col_int3,
CONCAT('->', SUBSTR(col_blob FROM 1 FOR 10),
'<-.....->', SUBSTR(col_blob FROM -10 FOR 10), '<-') AS col_blobx,
CONCAT('->',col_char0,'<-') AS col_char0x,
CONCAT('->',col_char1,'<-') AS col_char1x,
CONCAT('->',col_char2,'<-') AS col_char2x,
CONCAT('->',col_char3,'<-') AS col_char3x,
CONCAT('->',col_char4,'<-') AS col_char4x
FROM t1 WHERE col_int0 between 98 AND 102;
col_int0 98
col_int1 98000
col_int2 98
col_int3 0
col_blobx ->__0_aaaaaa<-.....->aaaaaaaaaa<-
col_char0x -> 98<-
col_char1x ->B 9__0_8 E<-
col_char2x ->B9 __0_ 8E<-
col_char3x ->B__0_ 98E<-
col_char4x ->B98 __0_E<-
col_int0 99
col_int1 99000
col_int2 99
col_int3 0
col_blobx ->__0_aaaaaa<-.....->aaaaaaaaaa<-
col_char0x -> 99<-
col_char1x ->B 9__0_9 E<-
col_char2x ->B9 __0_ 9E<-
col_char3x ->B__0_ 99E<-
col_char4x ->B99 __0_E<-
col_int0 100
col_int1 100000
col_int2 100
col_int3 0
col_blobx ->__0_aaaaaa<-.....->aaaaaaaaaa<-
col_char0x -> 100<-
col_char1x ->B 1__0_00 E<-
col_char2x ->B1 __0_ 00E<-
col_char3x ->B__0_ 100E<-
col_char4x ->B100 __0_E<-
col_int0 101
col_int1 101000
col_int2 101
col_int3 0
col_blobx ->__0_aaaaaa<-.....->aaaaaaaaaa<-
col_char0x -> 101<-
col_char1x ->B 1__0_01 E<-
col_char2x ->B1 __0_ 01E<-
col_char3x ->B__0_ 101E<-
col_char4x ->B101 __0_E<-
col_int0 102
col_int1 102000
col_int2 102
col_int3 0
col_blobx ->__0_aaaaaa<-.....->aaaaaaaaaa<-
col_char0x -> 102<-
col_char1x ->B 1__0_02 E<-
col_char2x ->B1 __0_ 02E<-
col_char3x ->B__0_ 102E<-
col_char4x ->B102 __0_E<-
# TC-11 Several concurrent sessions perform updates in t1 like mad.
# The system MUST survive this.
# Printing of statements is partially suppressed.
SET @start_time = UNIX_TIMESTAMP();
SELECT 1 FROM t1 WHERE col_int0 = @t1_half FOR UPDATE;
1
1
SELECT GET_LOCK('Blocker', 1000) ;
GET_LOCK('Blocker', 1000)
1
RELEASE_LOCK('Blocker')
1
# pass
SHOW ENGINE INNODB STATUS;
# TC-13 One session performs ALTER TABLE t1 ADD KEY ... on the fat table t1.
# The system MUST survive this.
SET @start_time = UNIX_TIMESTAMP();
ALTER TABLE t1 ADD KEY idx_col_char4_col_char0 (col_char4,col_char0);
SHOW ENGINE INNODB STATUS;
# pass
# TC-15 One session performs a fat update on the fat table t1.
# The system MUST survive this.
SET @start_time = UNIX_TIMESTAMP();
SET @extra_int = 13;
SET @extra_string = f_thread_id(@extra_int);
UPDATE t1 SET
col_int1 = f_col_int1(col_int0), col_int2 = f_col_int2(col_int0),
col_int3 = f_col_int3(col_int0), col_blob = f_col_blob(col_int0),
col_char0 = f_col_char0(col_int0), col_char1 = f_col_char1(col_int0),
col_char2 = f_col_char2(col_int0), col_char3 = f_col_char3(col_int0),
col_char4 = f_col_char4(col_int0)
WHERE col_int0 BETWEEN @t1_half - 2500 AND @t1_half + 2500;
COMMIT;
SHOW ENGINE INNODB STATUS;
# pass
INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'after'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
# TC-16 The following activities happend after reset in innodb_metrics
# - Insert some significant amount of rows into t1.
# - Several concurrent users perform excessive updates in t1.
# - ALTER TABLE ... ADD KEY <sufficient big enough structure>
# - One UPDATE statement modifying a huge slice of t1.
# Any of them causes heavy use of SX lock and therefore COUNT_RESET
# must have grown for ALL = @sx_count entries.
# pass
# TC-09 Heavy activity after reset.
# COUNT_RESET = MAX_COUNT_RESET for ALL = @sx_count entries
# needs to stay valid though he counters will have grown.
# pass
DELETE FROM my_metrics;
INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'before'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'after'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
# TC-08 There was a reset. COUNT_RESET = MAX_COUNT_RESET for ALL
# = @sx_count entries.
# pass
# TC-17 We had heavy activity causing big counters and after that a reset.
# Reset causes COUNT > COUNT_RESET AND MAX_COUNT > MAX_COUNT_RESET
# for ALL @sx_count entries.
# pass
# TC-18 We had some reset but this must not decrease COUNT or MAX_COUNT
# after.COUNT >= before.COUNT AND
# after.MAX_COUNT >= before.MAX_COUNT for ALL @sx_count entries.
# pass
# TC-19 We had some reset after heavy activity and this must cause
# after.COUNT_RESET < before.COUNT_RESET
# AND after.MAX_COUNT_RESET < before.MAX_COUNT_RESET AND
# for ALL @sx_count entries.
# pass
connection con10;
disconnect con10;
connection con9;
disconnect con9;
connection con8;
disconnect con8;
connection con7;
disconnect con7;
connection con6;
disconnect con6;
connection con5;
disconnect con5;
connection con4;
disconnect con4;
connection con3;
disconnect con3;
connection con2;
disconnect con2;
connection con1;
disconnect con1;
connection default;
USE test;
DROP SCHEMA my_schema;
SET GLOBAL innodb_monitor_disable = all;
SET GLOBAL innodb_monitor_reset_all = all;
SET GLOBAL innodb_monitor_enable = default;
SET GLOBAL innodb_monitor_disable = default;
SET GLOBAL innodb_monitor_reset = default;
SET GLOBAL innodb_monitor_reset_all = default;
SET GLOBAL innodb_monitor_disable = "innodb_rwlock_sx_%";
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";

View file

@ -0,0 +1 @@
--innodb-sys-tablestats

View file

@ -0,0 +1,236 @@
#
# Test for Bug#30113362 : BTR_CUR_WILL_MODIFY_TREE() IS INSUFFICIENT FOR HIGHER TREE LEVEL
#
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb_16k.inc
--disable_query_log
SET @old_innodb_limit_optimistic_insert_debug = @@innodb_limit_optimistic_insert_debug;
SET @old_innodb_adaptive_hash_index = @@innodb_adaptive_hash_index;
SET @old_innodb_stats_persistent = @@innodb_stats_persistent;
--enable_query_log
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
SET GLOBAL innodb_adaptive_hash_index = false;
SET GLOBAL innodb_stats_persistent = false;
connect (purge_control,localhost,root,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
--connect (con2,localhost,root,,)
CREATE TABLE t1 (
a00 CHAR(255) NOT NULL DEFAULT 'a',
a01 CHAR(255) NOT NULL DEFAULT 'a',
a02 CHAR(255) NOT NULL DEFAULT 'a',
b INT NOT NULL DEFAULT 0,
PRIMARY KEY(a00, a01, a02)
) charset latin1 ENGINE = InnoDB COMMENT='MERGE_THRESHOLD=45';
#
# Prepare primary key index tree to be used for this test.
#
SET GLOBAL innodb_limit_optimistic_insert_debug = 3;
delimiter |;
CREATE PROCEDURE data_load_t1()
BEGIN
DECLARE c1 INT DEFAULT 97;
DECLARE c2 INT DEFAULT 97;
DECLARE c3 INT DEFAULT 97;
WHILE c1 < 102 DO
WHILE c2 < 123 DO
WHILE c3 < 123 DO
INSERT INTO t1 (a00) VALUES (CHAR(c1,c2,c3));
SET c3 = c3 + 1;
END WHILE;
SET c3 = 97;
SET c2 = c2 + 1;
END WHILE;
SET c2 = 97;
SET c1 = c1 + 1;
END WHILE;
END |
delimiter ;|
call data_load_t1();
DROP PROCEDURE data_load_t1;
# all node pages are sparse (max 3 node_ptrs)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
connection con2;
DELETE FROM t1 WHERE a00 = 'cnm';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'cnm';
# causes "domino falling" merges to upper level
connection purge_control;
COMMIT;
connection con2;
SET GLOBAL innodb_limit_optimistic_insert_debug = 0;
ROLLBACK;
# at this moment, in the tree,
# ...
# level 4: ...(ast,avw,ayz)(bcc,bff,bii,bll,boo,brr,buu,bxx,cba,ced,cqp,cts)(cwv,czy,ddb)...
# ...
--echo # Test start
# (1) Similar case to the first reported corefile at bug#30113362
# - Deleting 'bii' causes "domino falling" merges and the node_ptr becomes left_most of level 4.
# So, the operation needs upper level pages' X-latch, though doesn't cause merge more.
connection purge_control;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection con2;
DELETE FROM t1 WHERE a00 = 'bii';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'bii';
SET DEBUG_SYNC = 'rollback_undo_pk SIGNAL roll1_wait WAIT_FOR roll2';
SET DEBUG_SYNC = 'rollback_purge_clust SIGNAL rollback_waiting WAIT_FOR resume';
send ROLLBACK;
connection purge_control;
SET DEBUG_SYNC = 'now WAIT_FOR roll1_wait';
COMMIT;
SET DEBUG_SYNC = 'now SIGNAL roll2';
connect (con1,localhost,root,,);
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting';
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
send SELECT a00 FROM t1 WHERE a00 = 'bii';
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
# bug#30113362 caused deadlock
SET DEBUG_SYNC = 'now SIGNAL resume';
connection con1;
reap;
connection con2;
reap;
connection default;
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# (2) Confirm blocking domain caused by DELETE modify_tree for tall index tree
# at this moment, in the tree,
# ...
# level 4: ...(ajk,amn,apq)(ast,avw,ayz,bll,boo,brr,buu,bxx,cba,ced,cqp,cts)(cwv,czy,ddb)(dge,djh,dmk)(dpn,dsq,dvt)(dyw,ebz,efc)...
# ...
# makes >17 records in level4 [(2^(4-1))*2 + 1]. (causes never left_most records)
DELETE FROM t1 WHERE a00 = 'dpn';
COMMIT;
INSERT INTO t1 SET a00 = 'dpn';
ROLLBACK;
# at this moment, in the tree,
# (* before "]" and after "[" records are treated as left_most possible records)
# ...
# level 4: ...(ajk,amn,apq)(ast,avw,ayz,bll,boo,brr,buu,bxx],cba,ced,[cqp,cts,cwv,czy,ddb,dge,dsq,dvt)(dyw,ebz,efc)...
# level 3: ...(cba,ccb,cdc)(ced,cfe,cgf,chg],cih,cji,[ckj,clk,con,cpo)(cqp,crq,csr)...
# level 2: ...(ckj,cks,clb)(clk,clt],cmc,cml,cmu,cnd,[cnv,coe)(con,cow,cpf)...
# level 1: ...(cmu,cmx,cna)(cnd],cng,cnj,cnp,[cns)(cnv,cny,cob)...
# level 0: ...(cnd,cne,cnf)(cng,cnh,cni)(cnj,cnk,cnl,cnn,cno)(cnp,cnq,cnr)...
# deletes just 'ced' node_ptr only from level 4. doesn't cause merge and never left_most.
# adjusts MERGE_THRESHOLD to do so.
ALTER TABLE t1 COMMENT='MERGE_THRESHOLD=35';
connection purge_control;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection con2;
DELETE FROM t1 WHERE a00 = 'cnd';
COMMIT;
BEGIN;
INSERT INTO t1 SET a00 = 'cnd';
SET DEBUG_SYNC = 'rollback_undo_pk SIGNAL roll1_wait WAIT_FOR roll2';
SET DEBUG_SYNC = 'rollback_purge_clust SIGNAL rollback_waiting WAIT_FOR resume EXECUTE 2';
send ROLLBACK;
connection purge_control;
SET DEBUG_SYNC = 'now WAIT_FOR roll1_wait';
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SET DEBUG_SYNC = 'now SIGNAL roll2';
connection con1;
# FIXME: For some reason, we will not always receive these signals!
--disable_warnings
# An optimistic row_undo_mod_remove_clust_low() will fail.
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting TIMEOUT 1';
SET DEBUG_SYNC = 'now SIGNAL resume';
# Wait for the pessimistic row_undo_mod_remove_clust_low() attempt.
SET DEBUG_SYNC = 'now WAIT_FOR rollback_waiting TIMEOUT 1';
--enable_warnings
disconnect purge_control;
# The expectation should be...
# level 0: (#cnd#,cne,cnf): causes merge
# level 1: (#cnd#],cng,cnj,cnp,[cns): left_most
# level 2: (clk,clt],cmc,cml,cmu,#cnd#,[cnv,coe): causes merge
# level 3: (ced,cfe,cgf,chg],cih,cji,[ckj,#clk#,con,cpo): left_most possible (not cause merge)
# level 4: (ast,avw,ayz,bll,boo,brr,buu,bxx],cba,#ced#,[cqp,cts,cwv,czy,ddb,dge,dsq,dvt): no merge, not left_most possible
# So, the top X-latch page is at level4. (ast~dvt)
# blocking domain based on whether its ancestor is latched or not.
# (*[]: ancestor is X-latched)
# level 0: ...(asq,asr,ass) [(ast,asu,asv)...(dyt,dyu,dyv)] (dyw,dyx,dyy)...
# Not blocked searches
## In MariaDB, both these will block, because we use different DEBUG_SYNC
## instrumentation (in rollback, not purge) and the root page (number 3)
## is being latched in row_undo_mod_remove_clust_low().
## SELECT a00 FROM t1 WHERE a00 = 'ass';
## SELECT a00 FROM t1 WHERE a00 = 'dyx';
## SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
## send SELECT a00 FROM t1 WHERE a00 = 'ast';
## connection con2;
## SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
## send SELECT a00 FROM t1 WHERE a00 = 'dyw';
connection default;
## SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
## SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL resume';
## connection con1;
## reap;
disconnect con1;
connection con2;
reap;
disconnect con2;
connection default;
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# Cleanup
SET DEBUG_SYNC = 'RESET';
DROP TABLE t1;
--disable_query_log
SET GLOBAL innodb_limit_optimistic_insert_debug = @old_innodb_limit_optimistic_insert_debug;
SET GLOBAL innodb_adaptive_hash_index = @old_innodb_adaptive_hash_index;
SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent;
--enable_query_log
--source include/wait_until_count_sessions.inc

View file

@ -0,0 +1 @@
--innodb-sys-tablestats

View file

@ -0,0 +1,500 @@
#
# WL#6326: InnoDB: fix index->lock contention
#
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb_16k.inc
--disable_query_log
SET @old_innodb_limit_optimistic_insert_debug = @@innodb_limit_optimistic_insert_debug;
SET @old_innodb_adaptive_hash_index = @@innodb_adaptive_hash_index;
SET @old_innodb_stats_persistent = @@innodb_stats_persistent;
--enable_query_log
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
SET GLOBAL innodb_adaptive_hash_index = false;
SET GLOBAL innodb_stats_persistent = false;
--connect (con1,localhost,root,,)
--connect (con2,localhost,root,,)
--connect (con3,localhost,root,,)
CREATE TABLE t1 (
a00 CHAR(255) NOT NULL DEFAULT 'a',
a01 CHAR(255) NOT NULL DEFAULT 'a',
a02 CHAR(255) NOT NULL DEFAULT 'a',
a03 CHAR(255) NOT NULL DEFAULT 'a',
a04 CHAR(255) NOT NULL DEFAULT 'a',
a05 CHAR(255) NOT NULL DEFAULT 'a',
a06 CHAR(255) NOT NULL DEFAULT 'a',
b INT NOT NULL DEFAULT 0
) ENGINE = InnoDB;
ALTER TABLE t1 ADD PRIMARY KEY(
a00,
a01,
a02,
a03,
a04,
a05,
a06
);
#
# Prepare primary key index tree to be used for this test.
#
# Only root (1)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# Make the first records sparse artificially,
# not to cause modify_tree by single node_ptr insert operation.
# * (7 - 2) records should be larger than a half of the page size
# * (7 + 2) records should be fit to the page
# (above t1 definition is already adjusted)
SET GLOBAL innodb_limit_optimistic_insert_debug = 7;
INSERT INTO t1 (a00) VALUES ('aa');
INSERT INTO t1 (a00) VALUES ('ab');
INSERT INTO t1 (a00) VALUES ('ac');
INSERT INTO t1 (a00) VALUES ('ad');
INSERT INTO t1 (a00) VALUES ('ae');
INSERT INTO t1 (a00) VALUES ('af');
INSERT INTO t1 (a00) VALUES ('ag');
INSERT INTO t1 (a00) VALUES ('ah');
# Raise root (1-2)
# (aa,ad)
# (aa,ab,ac)(ad,ae,af,ag,ah)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('ai');
INSERT INTO t1 (a00) VALUES ('aj');
INSERT INTO t1 (a00) VALUES ('ak');
# Split leaf (1-3)
# (aa,ad,ak)
# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('al');
INSERT INTO t1 (a00) VALUES ('am');
INSERT INTO t1 (a00) VALUES ('an');
INSERT INTO t1 (a00) VALUES ('ao');
INSERT INTO t1 (a00) VALUES ('ap');
INSERT INTO t1 (a00) VALUES ('aq');
INSERT INTO t1 (a00) VALUES ('ar');
# Split leaf (1-4)
# (aa,ad,ak,ar)
# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('as');
INSERT INTO t1 (a00) VALUES ('at');
INSERT INTO t1 (a00) VALUES ('au');
INSERT INTO t1 (a00) VALUES ('av');
INSERT INTO t1 (a00) VALUES ('aw');
INSERT INTO t1 (a00) VALUES ('ax');
INSERT INTO t1 (a00) VALUES ('ay');
# Split leaf (1-5)
# (aa,ad,ak,ar,ay)
# (aa,ab,ac)(ad,ae,af,ag,ah,ai,aj)(ak,al,am,an,ao,ap,aq)(ar,as,at,au,av,aw,ax)(ay)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('az');
INSERT INTO t1 (a00) VALUES ('ba');
INSERT INTO t1 (a00) VALUES ('bb');
INSERT INTO t1 (a00) VALUES ('bc');
INSERT INTO t1 (a00) VALUES ('bd');
INSERT INTO t1 (a00) VALUES ('be');
INSERT INTO t1 (a00) VALUES ('bf');
# Split leaf (1-6)
# (aa,ad,ak,ar,ay,bf)
# (aa,ab,ac)(ad..)(ak..)(ar,as,at,au,av,aw,ax)(ay,az,ba,bb,bc,bd,be)(bf)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('bg');
INSERT INTO t1 (a00) VALUES ('bh');
INSERT INTO t1 (a00) VALUES ('bi');
INSERT INTO t1 (a00) VALUES ('bj');
INSERT INTO t1 (a00) VALUES ('bk');
INSERT INTO t1 (a00) VALUES ('bl');
INSERT INTO t1 (a00) VALUES ('bm');
# Split leaf (1-7)
# (aa,ad,ak,ar,ay,bf,bm)
# (aa,ab,ac)(ad..)(ak..)(ar..)(ay,az,ba,bb,bc,bd,be)(bf,bg,bh,bi,bj,bk,bl)(bm)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('bn');
INSERT INTO t1 (a00) VALUES ('bo');
INSERT INTO t1 (a00) VALUES ('bp');
INSERT INTO t1 (a00) VALUES ('bq');
INSERT INTO t1 (a00) VALUES ('br');
INSERT INTO t1 (a00) VALUES ('bs');
INSERT INTO t1 (a00) VALUES ('bt');
# Raise root (1-2-8)
# (aa,ar)
# (aa,ad,ak) (ar,ay,bf,bm,bt)
# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('bu');
INSERT INTO t1 (a00) VALUES ('bv');
INSERT INTO t1 (a00) VALUES ('bw');
INSERT INTO t1 (a00) VALUES ('bx');
INSERT INTO t1 (a00) VALUES ('by');
INSERT INTO t1 (a00) VALUES ('bz');
INSERT INTO t1 (a00) VALUES ('ca');
INSERT INTO t1 (a00) VALUES ('cb');
INSERT INTO t1 (a00) VALUES ('cc');
INSERT INTO t1 (a00) VALUES ('cd');
INSERT INTO t1 (a00) VALUES ('ce');
INSERT INTO t1 (a00) VALUES ('cf');
INSERT INTO t1 (a00) VALUES ('cg');
INSERT INTO t1 (a00) VALUES ('ch');
INSERT INTO t1 (a00) VALUES ('ci');
INSERT INTO t1 (a00) VALUES ('cj');
INSERT INTO t1 (a00) VALUES ('ck');
INSERT INTO t1 (a00) VALUES ('cl');
INSERT INTO t1 (a00) VALUES ('cm');
INSERT INTO t1 (a00) VALUES ('cn');
INSERT INTO t1 (a00) VALUES ('co');
# Split also at level 1 (1-3-11)
# (aa,ar,co)
# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co)
# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('cp');
INSERT INTO t1 (a00) VALUES ('cq');
INSERT INTO t1 (a00) VALUES ('cr');
INSERT INTO t1 (a00) VALUES ('cs');
INSERT INTO t1 (a00) VALUES ('ct');
INSERT INTO t1 (a00) VALUES ('cu');
INSERT INTO t1 (a00) VALUES ('cv');
INSERT INTO t1 (a00) VALUES ('cw');
INSERT INTO t1 (a00) VALUES ('cx');
INSERT INTO t1 (a00) VALUES ('cy');
INSERT INTO t1 (a00) VALUES ('cz');
INSERT INTO t1 (a00) VALUES ('da');
INSERT INTO t1 (a00) VALUES ('db');
INSERT INTO t1 (a00) VALUES ('dc');
INSERT INTO t1 (a00) VALUES ('dd');
INSERT INTO t1 (a00) VALUES ('de');
INSERT INTO t1 (a00) VALUES ('df');
INSERT INTO t1 (a00) VALUES ('dg');
INSERT INTO t1 (a00) VALUES ('dh');
INSERT INTO t1 (a00) VALUES ('di');
INSERT INTO t1 (a00) VALUES ('dj');
INSERT INTO t1 (a00) VALUES ('dk');
INSERT INTO t1 (a00) VALUES ('dl');
INSERT INTO t1 (a00) VALUES ('dm');
INSERT INTO t1 (a00) VALUES ('dn');
INSERT INTO t1 (a00) VALUES ('do');
INSERT INTO t1 (a00) VALUES ('dp');
INSERT INTO t1 (a00) VALUES ('dq');
INSERT INTO t1 (a00) VALUES ('dr');
INSERT INTO t1 (a00) VALUES ('ds');
INSERT INTO t1 (a00) VALUES ('dt');
INSERT INTO t1 (a00) VALUES ('du');
INSERT INTO t1 (a00) VALUES ('dv');
INSERT INTO t1 (a00) VALUES ('dw');
INSERT INTO t1 (a00) VALUES ('dx');
INSERT INTO t1 (a00) VALUES ('dy');
INSERT INTO t1 (a00) VALUES ('dz');
INSERT INTO t1 (a00) VALUES ('ea');
INSERT INTO t1 (a00) VALUES ('eb');
INSERT INTO t1 (a00) VALUES ('ec');
INSERT INTO t1 (a00) VALUES ('ed');
INSERT INTO t1 (a00) VALUES ('ee');
INSERT INTO t1 (a00) VALUES ('ef');
INSERT INTO t1 (a00) VALUES ('eg');
INSERT INTO t1 (a00) VALUES ('eh');
INSERT INTO t1 (a00) VALUES ('ei');
INSERT INTO t1 (a00) VALUES ('ej');
INSERT INTO t1 (a00) VALUES ('ek');
INSERT INTO t1 (a00) VALUES ('el');
# Split also at level 1 (1-4-18)
# (aa,ar,co,el)
# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el)
# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
INSERT INTO t1 (a00) VALUES ('em');
INSERT INTO t1 (a00) VALUES ('en');
INSERT INTO t1 (a00) VALUES ('eo');
INSERT INTO t1 (a00) VALUES ('ep');
INSERT INTO t1 (a00) VALUES ('eq');
INSERT INTO t1 (a00) VALUES ('er');
INSERT INTO t1 (a00) VALUES ('es');
INSERT INTO t1 (a00) VALUES ('et');
INSERT INTO t1 (a00) VALUES ('eu');
INSERT INTO t1 (a00) VALUES ('ev');
INSERT INTO t1 (a00) VALUES ('ew');
INSERT INTO t1 (a00) VALUES ('ex');
INSERT INTO t1 (a00) VALUES ('ey');
INSERT INTO t1 (a00) VALUES ('ez');
INSERT INTO t1 (a00) VALUES ('fa');
INSERT INTO t1 (a00) VALUES ('fb');
INSERT INTO t1 (a00) VALUES ('fc');
INSERT INTO t1 (a00) VALUES ('fd');
INSERT INTO t1 (a00) VALUES ('fe');
INSERT INTO t1 (a00) VALUES ('ff');
INSERT INTO t1 (a00) VALUES ('fg');
INSERT INTO t1 (a00) VALUES ('fh');
INSERT INTO t1 (a00) VALUES ('fi');
INSERT INTO t1 (a00) VALUES ('fj');
INSERT INTO t1 (a00) VALUES ('fk');
INSERT INTO t1 (a00) VALUES ('fl');
INSERT INTO t1 (a00) VALUES ('fm');
INSERT INTO t1 (a00) VALUES ('fn');
INSERT INTO t1 (a00) VALUES ('fo');
INSERT INTO t1 (a00) VALUES ('fp');
INSERT INTO t1 (a00) VALUES ('fq');
INSERT INTO t1 (a00) VALUES ('fr');
INSERT INTO t1 (a00) VALUES ('fs');
INSERT INTO t1 (a00) VALUES ('ft');
INSERT INTO t1 (a00) VALUES ('fu');
INSERT INTO t1 (a00) VALUES ('fv');
INSERT INTO t1 (a00) VALUES ('fw');
INSERT INTO t1 (a00) VALUES ('fx');
INSERT INTO t1 (a00) VALUES ('fy');
INSERT INTO t1 (a00) VALUES ('fz');
INSERT INTO t1 (a00) VALUES ('ga');
INSERT INTO t1 (a00) VALUES ('gb');
INSERT INTO t1 (a00) VALUES ('gc');
INSERT INTO t1 (a00) VALUES ('gd');
INSERT INTO t1 (a00) VALUES ('ge');
INSERT INTO t1 (a00) VALUES ('gf');
INSERT INTO t1 (a00) VALUES ('gg');
INSERT INTO t1 (a00) VALUES ('gh');
# Current tree form (1-4-24)
# (aa,ar,co,el)
# (aa,ad,ak) (ar,ay,bf,bm,bt,ca,ch) (co,cv,dc,dj,dq,dx,ee) (el..,gb)
# (aa,ab,ac)(ad..)(ak..)(ar..)(ay..)(bf..)(bm..)(bt..)(ca..)(ch..)(co..)(cv..)(dc..)(dj..)(dq..)(dx..)(ee..)(el..)..(gb..)
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# Insert the rest of records normally
SET GLOBAL innodb_limit_optimistic_insert_debug = 0;
--echo # Test start
# (1) Insert records to leaf page (bf..) and cause modify_page.
# - root page is not X latched
# - latched from level 1 page (ar,ay,bf,bm,bt,ca,ch)
SET DEBUG_SYNC = 'RESET';
# Filling leaf page (bf..)
INSERT INTO t1 (a00) VALUES ('bfa');
--connection con1
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
# Cause modify_tree
--send
INSERT INTO t1 (a00) VALUES ('bfb');
--connection con2
SET DEBUG_SYNC = 'now WAIT_FOR reached';
# Not blocked searches
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
SELECT a00,a01 FROM t1 WHERE a00 = 'aq';
# "where a00 = 'co'" is blocked because searching from smaller ('co','a','a',..).
SELECT a00,a01 FROM t1 WHERE a00 = 'cp';
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'ar';
--connection con3
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'cn';
--connection default
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
--connection con1
--reap
--connection con2
--reap
--connection con3
--reap
--connection default
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# (2) Insert records to leaf page (co..) and cause modify_page
# - root page is X latched, because node_ptr for 'co'
# is 1st record for (co,cv,dc,dj,dq,dx,ee)
#
# * ordinary pessimitic insert might be done by pessistic update
# and we should consider possibility node_ptr to be deleted.
SET DEBUG_SYNC = 'RESET';
# Filling leaf page (co..)
INSERT INTO t1 (a00) VALUES ('coa');
--connection con1
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
# Cause modify_tree
--send
INSERT INTO t1 (a00) VALUES ('cob');
--connection con2
SET DEBUG_SYNC = 'now WAIT_FOR reached';
# All searches are blocked because root page is X latched
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
--connection con3
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
--connection default
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
--connection con1
--reap
--connection con2
--reap
--connection con3
--reap
--connection default
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# (3) Insert records to rightmost leaf page (gb..) and cause modify_page
# - root page is not X latched, because node_ptr for 'gb' is the last record
# of the level 1 though it is last record in the page.
# - lathed from level 1 page (el..,gb)
SET DEBUG_SYNC = 'RESET';
# Filling leaf page (gb..)
INSERT INTO t1 (a00) VALUES ('gba');
--connection con1
SET DEBUG_SYNC = 'before_insert_pessimitic_row_ins_clust SIGNAL reached WAIT_FOR continue';
# Cause modify_tree
--send
INSERT INTO t1 (a00) VALUES ('gbb');
--connection con2
SET DEBUG_SYNC = 'now WAIT_FOR reached';
# Not blocked searches
SELECT a00,a01 FROM t1 WHERE a00 = 'aa';
SELECT a00,a01 FROM t1 WHERE a00 = 'ek';
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait1';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'el';
--connection con3
SET DEBUG_SYNC = 'rw_s_lock_waiting SIGNAL lockwait2';
# Blocked
--send
SELECT a00,a01 FROM t1 WHERE a00 = 'gb';
--connection default
SET DEBUG_SYNC = 'now WAIT_FOR lockwait1';
SET DEBUG_SYNC = 'now WAIT_FOR lockwait2';
SET DEBUG_SYNC = 'now SIGNAL continue';
--connection con1
--reap
--connection con2
--reap
--connection con3
--reap
--connection default
ANALYZE TABLE t1;
SELECT CLUST_INDEX_SIZE FROM information_schema.INNODB_SYS_TABLESTATS WHERE NAME = 'test/t1';
# Cleanup
SET DEBUG_SYNC = 'RESET';
--connection default
--disconnect con1
--disconnect con2
--disconnect con3
DROP TABLE t1;
--disable_query_log
SET GLOBAL innodb_limit_optimistic_insert_debug = @old_innodb_limit_optimistic_insert_debug;
SET GLOBAL innodb_adaptive_hash_index = @old_innodb_adaptive_hash_index;
SET GLOBAL innodb_stats_persistent = @old_innodb_stats_persistent;
--enable_query_log
# Wait till all disconnects are completed.
--source include/wait_until_count_sessions.inc

View file

@ -0,0 +1,716 @@
# This is a script for MTR with hybrid use.
# a) As regression test
# Mostly some brute force attempt to stress the internal sx locks of
# InnoDB which were introduced by WL#6326+WL#6363.
# The file with expected results fits to this variant.
# The impact on code coverage is quite good.
# b) As testbed for attempts to extend or improve the RQG test wl6326_sql.yy.
# The MTR based test uses
# - a table t1 with the same layout
# - the same stored functions
# - the same stored procedure proc_fill_t1 for inserting a configurable
# amount of records into t1
# like the RQG test wl6326_sql.yy.
# Feel free to modify parameters like $max_row_count, $max_con,
# $high_load_duration or switch debugging on (let $test_debug= 1).
# But please be aware that MTR will most probably report that the test
# failed because it got a difference to expected results.
# Reasons:
# - In general: The file with expected results fits to a) only.
# - The actual results might dependend on $max_row_count.
# - Additional result sets might be printed.
#
# WL#6326 is about the sx locks (InnoDB feature only).
--source include/have_innodb.inc
# Runtime properties:
# Notebook i5 dual core with HT, MySQL binaries compiled with debug,
# max_row_count=10000 rows
# vardir on tmpfs : ~ 375
# vardir on disk : ~ 546
--source include/big_test.inc
# We go with "--send" and "--reap" and that fails with the embedded server.
--source include/not_embedded.inc
# Its intentional to not take the risk that a run with valgrind times out.
--source include/not_valgrind.inc
# FIXME:
# Increase the code coverage provided by the current test by
# trying "InnoDB Tablespace Monitor" as soon as some bug is fixed
# or wait till the deprecated "InnoDB Tablespace Monitor" is
# removed.
# Setup of some parameters
# ------------------------
# Number of records within every chunk to be added to t1.
let $load_unit= 10000;
#
# Rough number of records in t1 to achieve.
# We add chunks of $load_unit rows till the actual number
# of rows in the table t1 exceeds $max_row_count.
# let $max_row_count= 1000000;
# let $max_row_count= 300000;
# let $max_row_count= 100000;
# let $max_row_count= 30000;
let $max_row_count= 10000; # ~ 322s on tmpfs (NB)
#
# Determine which variant to run.
let $test_debug= 0;
#
# Number of concurrent sessions to be used in the high load test.
let $max_con= 10;
# Duration of the high load test in seconds.
let $high_load_duration= 60;
# Putting all objects into the SCHEMA my_schema makes the final cleanup easier.
# We simply run than DROP SCHEMA my_schema.
CREATE SCHEMA my_schema;
USE my_schema;
CREATE FUNCTION f_thread_id (i INT) RETURNS CHAR(4) DETERMINISTIC
RETURN CONCAT(LPAD(CAST(i AS CHAR),3,'_'),'_') ;
SELECT CONCAT('->', f_thread_id( 1), '<-');
SELECT CONCAT('->', f_thread_id(12), '<-');
# Definition of parameters used in functions.
# We use here a "1" in order to make the impact on the results of the functions
# good visible.
SET @extra_int = 1;
SET @extra_string = f_thread_id(@extra_int);
SELECT @extra_int , @extra_string;
# The different functions are used later when filling t1 and also during
# RQG testing. They serve to generate the difference between column values
# in different rows in different areas of the column.
# Fictional example:
# row 1 col_int0=1 colx='1abcdefgh' coly='abcd1efgh' colz='abcdefgh1'
# row 2 col_int0=2 colx='2abcdefgh' coly='abcd2efgh' colz='abcdefgh2'
# The function f_<pattern> is for the column with the name <pattern>.
# There is a function
# - for every column except col_int0
# - even if the SQL for generating the value is simple.
# The reason for this is the architecture of the RQG test.
let $part= AS my_result
FROM (SELECT 1 AS my_col UNION SELECT 12 UNION SELECT 123
UNION SELECT 1234 UNION SELECT 12345) AS tx;
let $function_name= f_col_int1;
eval CREATE FUNCTION $function_name (i INT) RETURNS INT(20) DETERMINISTIC
RETURN i * 1000 + @extra_int ;
eval SELECT $function_name(my_col) $part;
let $function_name= f_col_int2;
eval CREATE FUNCTION $function_name (i INT) RETURNS INT(20) DETERMINISTIC
RETURN @extra_int * 10000000 + i ;
eval SELECT $function_name(my_col) $part;
let $function_name= f_col_int3;
eval CREATE FUNCTION $function_name (i INT) RETURNS INT(20) DETERMINISTIC
RETURN @extra_int ;
eval SELECT $function_name(my_col) $part;
let $function_name= f_col_blob;
eval CREATE FUNCTION $function_name (i INT) RETURNS BLOB DETERMINISTIC
RETURN RPAD(@extra_string,(@@innodb_page_size / 2 ) + 1,'a');
eval SELECT CONCAT('->', SUBSTR($function_name(my_col) FROM 1 FOR 10),
'<-.....->', SUBSTR($function_name(my_col) FROM -10 FOR 10), '<-') $part;
let $function_name= f_col_char0;
eval CREATE FUNCTION $function_name (i INT) RETURNS CHAR(255) DETERMINISTIC
RETURN LPAD(CAST(i AS CHAR),255,' ');
eval SELECT CONCAT('->', $function_name(my_col), '<-') $part;
let $function_name= f_col_char1;
eval CREATE FUNCTION $function_name (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',
LPAD(SUBSTR(CAST(i AS CHAR),1,(LENGTH(CAST(i AS CHAR)) DIV 2)),10,' '),
@extra_string,
RPAD(SUBSTR(CAST(i AS CHAR), -((LENGTH(CAST(i AS CHAR)) + 1) DIV 2)),10,' '),
'E') ;
eval SELECT CONCAT('->', $function_name(my_col), '<-') $part;
let $function_name= f_col_char2;
eval CREATE FUNCTION $function_name (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',
RPAD(SUBSTR(CAST(i AS CHAR),1,(LENGTH(CAST(i AS CHAR)) DIV 2)),10,' '),
@extra_string,
LPAD(SUBSTR(CAST(i AS CHAR), -((LENGTH(CAST(i AS CHAR)) + 1) DIV 2)),10,' '),
'E');
eval SELECT CONCAT('->', $function_name(my_col), '<-') $part;
let $function_name= f_col_char3;
eval CREATE FUNCTION $function_name (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',@extra_string,LPAD(CAST(i AS CHAR),20,' '),'E');
eval SELECT CONCAT('->', $function_name(my_col), '<-') $part;
let $function_name= f_col_char4;
eval CREATE FUNCTION $function_name (i INT) RETURNS CHAR(26) DETERMINISTIC
RETURN
CONCAT('B',RPAD(CAST(i AS CHAR),20,' '),@extra_string,'E');
eval SELECT CONCAT('->', $function_name(my_col), '<-') $part;
# Auxiliary table for figuring out the impact of scenarios on
# information_schema.innodb_metrics content.
CREATE TABLE my_metrics LIKE information_schema.innodb_metrics;
ALTER TABLE my_metrics ADD COLUMN phase ENUM('after', 'before'),
DROP COLUMN SUBSYSTEM, DROP COLUMN TYPE, DROP COLUMN COMMENT,
ADD PRIMARY KEY (NAME,phase);
let $empty_my_metrics= DELETE FROM my_metrics;
let $before_my_metrics= INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'before'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
let $after_my_metrics= INSERT INTO my_metrics
SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET,
STATUS, 'after'
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%';
let $print_metrics= SELECT NAME, COUNT, MAX_COUNT, MIN_COUNT, AVG_COUNT,
COUNT_RESET, MAX_COUNT_RESET, MIN_COUNT_RESET, AVG_COUNT_RESET,
TIME_ENABLED, TIME_DISABLED, TIME_ELAPSED, TIME_RESET, STATUS
FROM information_schema.innodb_metrics
WHERE NAME LIKE 'innodb_rwlock_sx_%'
ORDER BY NAME;
# The main table for testing.
CREATE TABLE t1 (
col_int0 BIGINT,
col_int1 BIGINT,
col_int2 BIGINT,
col_int3 BIGINT,
col_blob BLOB,
col_char0 VARCHAR(255),
col_char1 VARCHAR(30),
col_char2 VARCHAR(30),
col_char3 VARCHAR(30),
col_char4 VARCHAR(30)
) ENGINE = InnoDB;
# Use many indexes with mostly significant size in order to cause
# some heavy use of sx locks during data generation.
ALTER TABLE t1 ADD UNIQUE KEY uidx_col_int0 (col_int0),
ADD UNIQUE KEY uidx1 (col_int1, col_char0),
ADD UNIQUE KEY uidx2 (col_int2, col_char0, col_int1),
ADD UNIQUE KEY uidx3 (col_int3, col_int2, col_char0),
ADD UNIQUE KEY uidx4 (col_char1, col_char0),
ADD UNIQUE KEY uidx5 (col_char2, col_char0, col_char1),
ADD UNIQUE KEY uidx6 (col_char3, col_char2, col_char0),
ADD UNIQUE KEY uidx7 (col_int1, col_int2, col_int3, col_char4,
col_char1, col_char2, col_char3, col_char0),
ADD KEY idx8 (col_blob(10), col_char4);
delimiter |;
CREATE PROCEDURE proc_fill_t1 (max_row_count INT, load_unit INT)
BEGIN
DECLARE my_count INTEGER DEFAULT 0;
DECLARE max_load_count INTEGER DEFAULT 0;
DROP TABLE IF EXISTS t0;
CREATE TEMPORARY TABLE t0 (col_int0 BIGINT, PRIMARY KEY(col_int0));
WHILE (my_count < load_unit ) DO
SET my_count = my_count + 1;
INSERT INTO t0 SET col_int0 = my_count;
END WHILE;
SET max_load_count = (SELECT (max_row_count DIV load_unit) + 1 );
SELECT COUNT(col_int0) INTO @val FROM t1;
SET my_count = 0;
REPEAT
INSERT INTO t1 (col_int0, col_int1, col_int2, col_int3, col_blob,
col_char0, col_char1, col_char2,col_char3,col_char4)
SELECT col_int0 + @val,
f_col_int1(col_int0 + @val),
f_col_int2(col_int0 + @val),
f_col_int3(col_int0 + @val),
f_col_blob(col_int0 + @val),
f_col_char0(col_int0 + @val),
f_col_char1(col_int0 + @val),
f_col_char2(col_int0 + @val),
f_col_char3(col_int0 + @val),
f_col_char4(col_int0 + @val)
FROM t0;
COMMIT;
SELECT MAX(col_int0) INTO @val FROM t1;
SET my_count = my_count + 1;
UNTIL( my_count > max_load_count OR @val >= max_row_count )
END REPEAT;
DROP TEMPORARY TABLE t0;
END|
delimiter ;|
delimiter |;
CREATE PROCEDURE proc_dml (max_duration INT, t1_stripe_half INT)
BEGIN
DECLARE aux INTEGER DEFAULT 0;
DECLARE start_time INT;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING, NOT FOUND BEGIN END;
SET @extra_int = CONNECTION_ID();
SET @extra_string = f_thread_id(@extra_int);
SELECT ROUND(MAX(col_int0) / 2 ) INTO @t1_half FROM t1;
# The user lock 'Blocker' should be already set by some other session S1.
# S1 starts the race by releasing that lock.
# Wait till the lock is released and the lock can be obtained.
# In order to prevent endless waiting in case of non foreseen problems
# limit the timespan to 30 seconds.
SELECT GET_LOCK('Blocker', 30) INTO @aux;
# Release the lock immediate so that the other "runner" sessions start too.
SELECT RELEASE_LOCK('Blocker') INTO @aux;
SET start_time = UNIX_TIMESTAMP();
WHILE (UNIX_TIMESTAMP() - start_time < max_duration) DO
SET @aux = @t1_half - t1_stripe_half + ROUND(RAND() * t1_stripe_half * 2);
UPDATE t1 SET
col_int1 = f_col_int1(col_int0),
col_int2 = f_col_int2(col_int0),
col_int3 = f_col_int3(col_int0),
col_blob = f_col_blob(col_int0),
col_char0 = f_col_char0(col_int0),
col_char1 = f_col_char1(col_int0),
col_char2 = f_col_char2(col_int0),
col_char3 = f_col_char3(col_int0),
col_char4 = f_col_char4(col_int0)
WHERE col_int0 = @aux;
COMMIT;
END WHILE;
END|
delimiter ;|
SET GLOBAL innodb_monitor_disable = "innodb_rwlock_sx_%";
SET @pre_reset_ts = NOW();
--sleep 1.1
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
SET @pre_enable_ts = NOW();
--sleep 1.1
SET GLOBAL innodb_monitor_enable = "innodb_rwlock_sx_%";
--sleep 1.1
SET @pre_collect_ts = NOW();
eval $empty_my_metrics;
eval $before_my_metrics;
--echo # TC-01 There are exact three entries "innodb_rwlock_sx_%" with the
--echo # with the name which follow in innodb_metrics.
let $check_statement=
SELECT COUNT(*) <> 3 FROM my_metrics
WHERE NAME IN ('innodb_rwlock_sx_spin_waits',
'innodb_rwlock_sx_spin_rounds',
'innodb_rwlock_sx_os_waits');
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
SELECT COUNT(*) INTO @sx_count FROM my_metrics;
--echo # TC-02 Counting is now enabled. ALL = @sx_count entries show that.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE STATUS = 'enabled';
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME, STATUS FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
--echo # TC-03 @pre_reset_ts < TIME_RESET. ALL = @sx_count entries show that.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE @pre_reset_ts < TIME_RESET;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME, @pre_reset_ts, TIME_RESET FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
--echo # TC-04 @pre_enable_ts < TIME_ENABLED. ALL = @sx_count entries show that.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE @pre_enable_ts < TIME_ENABLED;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME, @pre_enable_ts, TIME_ENABLED FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
--echo # TC-05 TIME_RESET < TIME_ENABLED AND TIME_ENABLED < @pre_collect_ts
--echo # AND TIME_ELAPSED > 0. ALL = @sx_count entries show that.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE TIME_RESET < TIME_ENABLED AND TIME_ENABLED < @pre_collect_ts
AND TIME_ELAPSED > 0;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME, @pre_collect_ts, TIME_RESET, TIME_ENABLED, TIME_ELAPSED
FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
--echo # TC-06 COUNT_RESET = MAX_COUNT_RESET. ALL = @sx_count entries show that.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE COUNT_RESET = MAX_COUNT_RESET;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT NAME, COUNT_RESET, MAX_COUNT_RESET FROM my_metrics
ORDER BY NAME;
exit;
}
--echo # pass
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
--disable_result_log
if($test_debug)
{
--enable_result_log
eval $print_metrics;
}
SHOW ENGINE INNODB STATUS;
--enable_result_log
eval $empty_my_metrics;
eval $before_my_metrics;
# These values (the "0") help to identify later if some record is in its
# initial state or already modified.
SET @extra_string = '__0_';
SET @extra_int = 0;
--echo # TC-07 One session inserts some significant amount of rows into t1.
--echo # The system MUST survive that.
--replace_result $max_row_count <max_row_count>
eval SET @max_row_count = $max_row_count;
--replace_result $load_unit <load_unit>
eval SET @load_unit = $load_unit;
SET @start_time = UNIX_TIMESTAMP();
SET AUTOCOMMIT = OFF;
CALL proc_fill_t1 (@max_row_count, @load_unit);
--echo # pass
SET AUTOCOMMIT = ON;
SELECT col_int0 INTO @t1_half FROM t1
WHERE col_int0 >= (@val DIV 2) ORDER BY col_int0 LIMIT 1;
--disable_result_log
if($test_debug)
{
--enable_result_log
SELECT COUNT(*) AS table_row_count,
UNIX_TIMESTAMP() - @start_time AS fill_run_time
FROM t1;
eval $print_metrics;
}
SHOW ENGINE INNODB STATUS;
--enable_result_log
# Show that the value distribution is according to the plan.
--vertical_results
SELECT col_int0, col_int1, col_int2, col_int3,
CONCAT('->', SUBSTR(col_blob FROM 1 FOR 10),
'<-.....->', SUBSTR(col_blob FROM -10 FOR 10), '<-') AS col_blobx,
CONCAT('->',col_char0,'<-') AS col_char0x,
CONCAT('->',col_char1,'<-') AS col_char1x,
CONCAT('->',col_char2,'<-') AS col_char2x,
CONCAT('->',col_char3,'<-') AS col_char3x,
CONCAT('->',col_char4,'<-') AS col_char4x
FROM t1 WHERE col_int0 between 98 AND 102;
--horizontal_results
# For experiments/interest only. Please do not remove that.
if (0)
{
ANALYZE TABLE t1;
SELECT n_rows, clustered_index_size, sum_of_other_index_sizes
FROM mysql.innodb_table_stats;
# SELECT * FROM mysql.innodb_index_stats;
# idx_col_int3_int0 n_diff_pfx01 1 col_int3
# idx_col_int3_int0 n_diff_pfx02 10000 col_int3,col_int0
# idx_col_int3_int0 n_diff_pfx03 10000 col_int3,col_int0,DB_ROW_ID
# idx_col_int3_int0 n_leaf_pages 19 Number of leaf pages in the index
# idx_col_int3_int0 size 20 Number of pages in the index
--vertical_results
SELECT t1.index_name, t1.stat_value AS idx_pages, t2.stat_value AS idx_leaf_pages,
(t1.stat_value - t2.stat_value - 1) / t1.stat_value AS sx_page_ratio
FROM mysql.innodb_index_stats t1, mysql.innodb_index_stats t2
WHERE t1.index_name = t2.index_name
AND t1.stat_name = 'size' AND t2.stat_name = 'n_leaf_pages'
ORDER BY t1.index_name;
--horizontal_results
}
--echo # TC-11 Several concurrent sessions perform updates in t1 like mad.
--echo # The system MUST survive this.
--echo # Printing of statements is partially suppressed.
SET @start_time = UNIX_TIMESTAMP();
SELECT 1 FROM t1 WHERE col_int0 = @t1_half FOR UPDATE;
SELECT GET_LOCK('Blocker', 1000) ;
--disable_query_log
let $num= $max_con;
while ($num)
{
--connect (con$num,localhost,root,,)
USE my_schema;
# The second parameter of the procedure is size of the affected stripe / 2.
# A smaller stripe causes some smaller counter growth but most probably
# also more stress around locking in general.
# Example # (nnnn) = half stripe size
# NAME | COUNT_RESET (5000) | COUNT_RESET (100)
# -----------------------------+--------------------+----------------
# innodb_rwlock_sx_os_waits | 1412 | 486
# innodb_rwlock_sx_spin_rounds | 44061 | 17031
# innodb_rwlock_sx_spin_waits | 996 | 515
--send
eval CALL proc_dml($high_load_duration,@t1_half);
dec $num;
}
--connection default
SELECT RELEASE_LOCK('Blocker') ;
--sleep 3
COMMIT;
let $num= $max_con;
while ($num)
{
--connection con$num
--reap
dec $num;
}
--echo # pass
--connection default
--enable_query_log
# let $wait_timeout= 181;
# --source include/wait_condition.inc
# eval $after_my_metrics;
--disable_result_log
if($test_debug)
{
--enable_result_log
SELECT UNIX_TIMESTAMP() - @start_time AS update_battle_run_time;
eval $print_metrics;
}
SHOW ENGINE INNODB STATUS;
--enable_result_log
--echo # TC-13 One session performs ALTER TABLE t1 ADD KEY ... on the fat table t1.
--echo # The system MUST survive this.
SET @start_time = UNIX_TIMESTAMP();
ALTER TABLE t1 ADD KEY idx_col_char4_col_char0 (col_char4,col_char0);
--disable_result_log
if($test_debug)
{
--enable_result_log
SELECT UNIX_TIMESTAMP() - @start_time AS add_key_run_time;
eval $print_metrics;
}
SHOW ENGINE INNODB STATUS;
--enable_result_log
--echo # pass
--echo # TC-15 One session performs a fat update on the fat table t1.
--echo # The system MUST survive this.
SET @start_time = UNIX_TIMESTAMP();
SET @extra_int = 13;
SET @extra_string = f_thread_id(@extra_int);
eval UPDATE t1 SET
col_int1 = f_col_int1(col_int0), col_int2 = f_col_int2(col_int0),
col_int3 = f_col_int3(col_int0), col_blob = f_col_blob(col_int0),
col_char0 = f_col_char0(col_int0), col_char1 = f_col_char1(col_int0),
col_char2 = f_col_char2(col_int0), col_char3 = f_col_char3(col_int0),
col_char4 = f_col_char4(col_int0)
WHERE col_int0 BETWEEN @t1_half - 2500 AND @t1_half + 2500;
COMMIT;
--disable_result_log
if($test_debug)
{
--enable_result_log
SELECT UNIX_TIMESTAMP() - @start_time AS total_update_run_time;
eval $print_metrics;
}
SHOW ENGINE INNODB STATUS;
--enable_result_log
--echo # pass
# Basically every of the big activities causes some counter growth.
# But caused by
# - the architecture of InnoDB (certain things happen asynchronous)
# - the actual test configuration (server/InnoDB options)
# - conditions like parallel (./mtr --parallel=auto?) load on the testing box
# this might be not fulfilled per single big activity every time except
# we go with huge waits or similar.
# Observation:
# - non debug binaries: expectation frequent not fulfilled
# - debug binaries: expectation rare not fulfilled
#
let $wait_timeout= 121;
let $wait_condition=
SELECT COUNT(*) = @sx_count
FROM information_schema.innodb_metrics t_after
JOIN my_metrics t_before
ON t_after.COUNT_RESET > t_before.COUNT_RESET AND t_after.NAME = t_before.NAME;
--source include/wait_condition.inc
eval $after_my_metrics;
--echo # TC-16 The following activities happend after reset in innodb_metrics
--echo # - Insert some significant amount of rows into t1.
--echo # - Several concurrent users perform excessive updates in t1.
--echo # - ALTER TABLE ... ADD KEY <sufficient big enough structure>
--echo # - One UPDATE statement modifying a huge slice of t1.
--echo # Any of them causes heavy use of SX lock and therefore COUNT_RESET
--echo # must have grown for ALL = @sx_count entries.
# The former testcases TC-10 and TC12 had to be made a part of this testcase
# because their results were unstable.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics t_after JOIN my_metrics t_before
ON t_after.COUNT_RESET > t_before.COUNT_RESET AND t_after.NAME = t_before.NAME
WHERE t_after.phase = 'after' AND t_before.phase = 'before';
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
--echo # TC-09 Heavy activity after reset.
--echo # COUNT_RESET = MAX_COUNT_RESET for ALL = @sx_count entries
--echo # needs to stay valid though he counters will have grown.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE phase = 'after' AND COUNT_RESET = MAX_COUNT_RESET;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
eval $empty_my_metrics;
eval $before_my_metrics;
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";
eval $after_my_metrics;
--echo # TC-08 There was a reset. COUNT_RESET = MAX_COUNT_RESET for ALL
--echo # = @sx_count entries.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE phase = 'before' AND COUNT_RESET = MAX_COUNT_RESET;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
--echo # TC-17 We had heavy activity causing big counters and after that a reset.
--echo # Reset causes COUNT > COUNT_RESET AND MAX_COUNT > MAX_COUNT_RESET
--echo # for ALL @sx_count entries.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics
WHERE phase = 'after'
AND COUNT > COUNT_RESET
AND MAX_COUNT > MAX_COUNT_RESET;
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
--echo # TC-18 We had some reset but this must not decrease COUNT or MAX_COUNT
--echo # after.COUNT >= before.COUNT AND
--echo # after.MAX_COUNT >= before.MAX_COUNT for ALL @sx_count entries.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics t_after JOIN my_metrics t_before
ON t_after.COUNT >= t_before.COUNT AND t_after.MAX_COUNT >= t_before.MAX_COUNT
AND t_after.NAME = t_before.NAME
WHERE t_after.phase = 'after' AND t_before.phase = 'before';
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
--echo # TC-19 We had some reset after heavy activity and this must cause
--echo # after.COUNT_RESET < before.COUNT_RESET
--echo # AND after.MAX_COUNT_RESET < before.MAX_COUNT_RESET AND
--echo # for ALL @sx_count entries.
let $check_statement=
SELECT COUNT(*) <> @sx_count FROM my_metrics t_after JOIN my_metrics t_before
ON t_after.COUNT_RESET < t_before.COUNT_RESET
AND t_after.MAX_COUNT_RESET < t_before.MAX_COUNT_RESET
AND t_after.NAME = t_before.NAME
WHERE t_after.phase = 'after' AND t_before.phase = 'before';
if(`$check_statement`)
{
--echo # fail
eval $check_statement;
SELECT * FROM my_metrics
ORDER BY NAME, phase;
exit;
}
--echo # pass
# Cleanup
let $num= $max_con;
while ($num)
{
--connection con$num
--disconnect con$num
--source include/wait_until_disconnected.inc
dec $num;
}
--connection default
USE test;
DROP SCHEMA my_schema;
SET GLOBAL innodb_monitor_disable = all;
SET GLOBAL innodb_monitor_reset_all = all;
--disable_warnings
SET GLOBAL innodb_monitor_enable = default;
SET GLOBAL innodb_monitor_disable = default;
SET GLOBAL innodb_monitor_reset = default;
SET GLOBAL innodb_monitor_reset_all = default;
--enable_warnings
SET GLOBAL innodb_monitor_disable = "innodb_rwlock_sx_%";
SET GLOBAL innodb_monitor_reset = "innodb_rwlock_sx_%";

View file

@ -14554,7 +14554,7 @@ static int compare_fields_by_table_order(Item *field1,
Item_field *f2= (Item_field *) field2_real;
if (f1->used_tables() & OUTER_REF_TABLE_BIT)
{
outer_ref= -1;
outer_ref= 1;
cmp= -1;
}
if (f2->used_tables() & OUTER_REF_TABLE_BIT)

View file

@ -415,29 +415,46 @@ static void wsrep_replication_process(THD *thd)
DBUG_VOID_RETURN;
}
static bool create_wsrep_THD(wsrep_thread_args* args)
static bool create_wsrep_THD(wsrep_thread_args* args, bool thread_count_lock)
{
mysql_mutex_lock(&LOCK_thread_count);
if (!thread_count_lock)
mysql_mutex_lock(&LOCK_thread_count);
ulong old_wsrep_running_threads= wsrep_running_threads;
DBUG_ASSERT(args->thread_type == WSREP_APPLIER_THREAD ||
args->thread_type == WSREP_ROLLBACKER_THREAD);
bool res= mysql_thread_create(args->thread_type == WSREP_APPLIER_THREAD
? key_wsrep_applier : key_wsrep_rollbacker,
&args->thread_id, &connection_attrib,
start_wsrep_THD, (void*)args);
if (res)
{
WSREP_ERROR("Can't create wsrep thread");
}
/*
if starting a thread on server startup, wait until the this thread's THD
is fully initialized (otherwise a THD initialization code might
try to access a partially initialized server data structure - MDEV-8208).
*/
if (!mysqld_server_initialized)
{
while (old_wsrep_running_threads == wsrep_running_threads)
{
mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
}
}
if (!thread_count_lock)
mysql_mutex_unlock(&LOCK_thread_count);
return res;
}
void wsrep_create_appliers(long threads)
bool wsrep_create_appliers(long threads, bool thread_count_lock)
{
if (!wsrep_connected)
{
@ -449,26 +466,32 @@ void wsrep_create_appliers(long threads)
"connection at '%s'", wsrep_cluster_address);
assert(0);
}
return;
return false;
}
long wsrep_threads=0;
long wsrep_threads= 0;
while (wsrep_threads++ < threads) {
wsrep_thread_args* arg;
if((arg = (wsrep_thread_args*)my_malloc(sizeof(wsrep_thread_args), MYF(0))) == NULL) {
if((arg= (wsrep_thread_args*)my_malloc(sizeof(wsrep_thread_args), MYF(0))) == NULL)
{
WSREP_ERROR("Can't allocate memory for wsrep replication thread %ld\n", wsrep_threads);
assert(0);
}
arg->thread_type = WSREP_APPLIER_THREAD;
arg->processor = wsrep_replication_process;
arg->thread_type= WSREP_APPLIER_THREAD;
arg->processor= wsrep_replication_process;
if (create_wsrep_THD(arg)) {
WSREP_WARN("Can't create thread to manage wsrep replication");
if (create_wsrep_THD(arg, thread_count_lock))
{
WSREP_ERROR("Can't create thread to manage wsrep replication");
my_free(arg);
return;
return true;
}
}
return false;
}
static void wsrep_rollback_process(THD *thd)
@ -564,7 +587,7 @@ void wsrep_create_rollbacker()
arg->processor = wsrep_rollback_process;
/* create rollbacker */
if (create_wsrep_THD(arg)) {
if (create_wsrep_THD(arg, false)) {
WSREP_WARN("Can't create thread to manage wsrep rollback");
my_free(arg);
return;

View file

@ -26,7 +26,7 @@ int wsrep_show_bf_aborts (THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope);
void wsrep_client_rollback(THD *thd);
void wsrep_replay_transaction(THD *thd);
void wsrep_create_appliers(long threads);
bool wsrep_create_appliers(long threads, bool thread_count_lock=false);
void wsrep_create_rollbacker();
int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,

View file

@ -602,16 +602,22 @@ static void wsrep_slave_count_change_update ()
bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type)
{
mysql_mutex_lock(&LOCK_thread_count);
bool res= false;
wsrep_slave_count_change_update();
if (wsrep_slave_count_change > 0)
{
WSREP_DEBUG("Creating %d applier threads, total %ld", wsrep_slave_count_change, wsrep_slave_threads);
wsrep_create_appliers(wsrep_slave_count_change);
res= wsrep_create_appliers(wsrep_slave_count_change, true);
WSREP_DEBUG("Running %lu applier threads", wsrep_running_applier_threads);
wsrep_slave_count_change = 0;
}
return false;
mysql_mutex_unlock(&LOCK_thread_count);
return res;
}
bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)

View file

@ -1,9 +1,9 @@
/*****************************************************************************
Copyright (c) 1994, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@ -804,46 +804,66 @@ btr_cur_will_modify_tree(
const ulint n_recs = page_get_n_recs(page);
if (lock_intention <= BTR_INTENTION_BOTH) {
ulint margin;
compile_time_assert(BTR_INTENTION_DELETE < BTR_INTENTION_BOTH);
compile_time_assert(BTR_INTENTION_BOTH < BTR_INTENTION_INSERT);
/* check delete will cause. (BTR_INTENTION_BOTH
or BTR_INTENTION_DELETE) */
/* first, 2nd, 2nd-last and last records are 4 records */
if (n_recs < 5) {
return(true);
if (!page_has_siblings(page)) {
return true;
}
/* is first, 2nd or last record */
if (page_rec_is_first(rec, page)
|| (page_has_next(page)
&& (page_rec_is_last(rec, page)
|| page_rec_is_second_last(rec, page)))
|| (page_has_prev(page)
&& page_rec_is_second(rec, page))) {
return(true);
}
ulint margin = rec_size;
if (lock_intention == BTR_INTENTION_BOTH) {
ulint level = btr_page_get_level(page);
/* This value is the worst expectation for the node_ptr
records to be deleted from this page. It is used to
expect whether the cursor position can be the left_most
record in this page or not. */
ulint max_nodes_deleted = 0;
/* By modifying tree operations from the under of this
level, logically (2 ^ (level - 1)) opportunities to
deleting records in maximum even unreally rare case. */
if (level > 7) {
/* TODO: adjust this practical limit. */
max_nodes_deleted = 64;
} else if (level > 0) {
max_nodes_deleted = (ulint)1 << (level - 1);
}
/* check delete will cause. (BTR_INTENTION_BOTH
or BTR_INTENTION_DELETE) */
if (n_recs <= max_nodes_deleted * 2
|| page_rec_is_first(rec, page)) {
/* The cursor record can be the left most record
in this page. */
return true;
}
if (page_has_prev(page)
&& page_rec_distance_is_at_most(
page_get_infimum_rec(page), rec,
max_nodes_deleted)) {
return true;
}
if (page_has_next(page)
&& page_rec_distance_is_at_most(
rec, page_get_supremum_rec(page),
max_nodes_deleted)) {
return true;
}
/* Delete at leftmost record in a page causes delete
& insert at its parent page. After that, the delete
might cause btr_compress() and delete record at its
parent page. Thus we should consider max 2 deletes. */
margin = rec_size * 2;
} else {
ut_ad(lock_intention == BTR_INTENTION_DELETE);
margin = rec_size;
parent page. Thus we should consider max deletes. */
margin *= max_nodes_deleted;
}
/* NOTE: call mach_read_from_4() directly to avoid assertion
failure. It is safe because we already have SX latch of the
index tree */
/* Safe because we already have SX latch of the index tree */
if (page_get_data_size(page)
< margin + BTR_CUR_PAGE_COMPRESS_LIMIT(index)
|| (mach_read_from_4(page + FIL_PAGE_NEXT)
== FIL_NULL
&& mach_read_from_4(page + FIL_PAGE_PREV)
== FIL_NULL)) {
< margin + BTR_CUR_PAGE_COMPRESS_LIMIT(index)) {
return(true);
}
}
@ -2025,9 +2045,9 @@ need_opposite_intention:
offsets2 = rec_get_offsets(
first_rec, index, offsets2,
false, ULINT_UNDEFINED, &heap);
cmp_rec_rec_with_match(node_ptr, first_rec,
offsets, offsets2, index, FALSE,
&matched_fields);
cmp_rec_rec(node_ptr, first_rec,
offsets, offsets2, index, false,
&matched_fields);
if (matched_fields
>= rec_offs_n_fields(offsets) - 1) {
@ -2043,10 +2063,10 @@ need_opposite_intention:
offsets2 = rec_get_offsets(
last_rec, index, offsets2,
false, ULINT_UNDEFINED, &heap);
cmp_rec_rec_with_match(
cmp_rec_rec(
node_ptr, last_rec,
offsets, offsets2, index,
FALSE, &matched_fields);
false, &matched_fields);
if (matched_fields
>= rec_offs_n_fields(offsets) - 1) {
detected_same_key_root = true;
@ -4242,7 +4262,6 @@ btr_cur_optimistic_update(
dtuple_t* new_entry;
roll_ptr_t roll_ptr;
ulint i;
ulint n_ext;
block = btr_cur_get_block(cursor);
page = buf_block_get_frame(block);
@ -4319,10 +4338,8 @@ any_extern:
+ DTUPLE_EST_ALLOC(rec_offs_n_fields(*offsets)));
}
new_entry = row_rec_to_index_entry(rec, index, *offsets,
&n_ext, *heap);
/* We checked above that there are no externally stored fields. */
ut_a(!n_ext);
new_entry = row_rec_to_index_entry(rec, index, *offsets, *heap);
ut_ad(!dtuple_get_n_ext(new_entry));
/* The page containing the clustered index record
corresponding to new_entry is latched in mtr.
@ -4586,7 +4603,6 @@ btr_cur_pessimistic_update(
roll_ptr_t roll_ptr;
ibool was_first;
ulint n_reserved = 0;
ulint n_ext;
ulint max_ins_size = 0;
*offsets = NULL;
@ -4651,7 +4667,7 @@ btr_cur_pessimistic_update(
ut_ad(rec_offs_validate(rec, index, *offsets));
dtuple_t* new_entry = row_rec_to_index_entry(
rec, index, *offsets, &n_ext, entry_heap);
rec, index, *offsets, entry_heap);
/* The page containing the clustered index record
corresponding to new_entry is latched in mtr. If the
@ -4671,7 +4687,6 @@ btr_cur_pessimistic_update(
ut_ad(!page_is_comp(page) || !rec_get_node_ptr_flag(rec));
ut_ad(rec_offs_validate(rec, index, *offsets));
n_ext += btr_push_update_extern_fields(new_entry, update, entry_heap);
if ((flags & BTR_NO_UNDO_LOG_FLAG)
&& rec_offs_any_extern(*offsets)) {
@ -4692,6 +4707,8 @@ btr_cur_pessimistic_update(
index, rec, page_zip, *offsets, update, true, mtr);
}
ulint n_ext = dtuple_get_n_ext(new_entry);
if (page_zip_rec_needs_ext(
rec_get_converted_size(index, new_entry, n_ext),
page_is_comp(page),
@ -6722,10 +6739,10 @@ btr_estimate_number_of_different_key_vals(
ULINT_UNDEFINED,
&heap);
cmp_rec_rec_with_match(rec, next_rec,
offsets_rec, offsets_next_rec,
index, stats_null_not_equal,
&matched_fields);
cmp_rec_rec(rec, next_rec,
offsets_rec, offsets_next_rec,
index, stats_null_not_equal,
&matched_fields);
for (j = matched_fields; j < n_cols; j++) {
/* We add one if this index record has
@ -7005,84 +7022,6 @@ btr_cur_unmark_extern_fields(
}
}
/*******************************************************************//**
Flags the data tuple fields that are marked as extern storage in the
update vector. We use this function to remember which fields we must
mark as extern storage in a record inserted for an update.
@return number of flagged external columns */
ulint
btr_push_update_extern_fields(
/*==========================*/
dtuple_t* tuple, /*!< in/out: data tuple */
const upd_t* update, /*!< in: update vector */
mem_heap_t* heap) /*!< in: memory heap */
{
ulint n_pushed = 0;
ulint n;
const upd_field_t* uf;
uf = update->fields;
n = upd_get_n_fields(update);
for (; n--; uf++) {
if (dfield_is_ext(&uf->new_val)) {
dfield_t* field
= dtuple_get_nth_field(tuple, uf->field_no);
if (!dfield_is_ext(field)) {
dfield_set_ext(field);
n_pushed++;
}
switch (uf->orig_len) {
byte* data;
ulint len;
byte* buf;
case 0:
break;
case BTR_EXTERN_FIELD_REF_SIZE:
/* Restore the original locally stored
part of the column. In the undo log,
InnoDB writes a longer prefix of externally
stored columns, so that column prefixes
in secondary indexes can be reconstructed. */
dfield_set_data(field,
(byte*) dfield_get_data(field)
+ dfield_get_len(field)
- BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_ext(field);
break;
default:
/* Reconstruct the original locally
stored part of the column. The data
will have to be copied. */
ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
data = (byte*) dfield_get_data(field);
len = dfield_get_len(field);
buf = (byte*) mem_heap_alloc(heap,
uf->orig_len);
/* Copy the locally stored prefix. */
memcpy(buf, data,
unsigned(uf->orig_len)
- BTR_EXTERN_FIELD_REF_SIZE);
/* Copy the BLOB pointer. */
memcpy(buf + unsigned(uf->orig_len)
- BTR_EXTERN_FIELD_REF_SIZE,
data + len - BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
dfield_set_data(field, buf, uf->orig_len);
dfield_set_ext(field);
}
}
}
return(n_pushed);
}
/*******************************************************************//**
Returns the length of a BLOB part stored on the header page.
@return part length */

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2009, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1169,13 +1169,9 @@ dict_stats_analyze_index_level(
prev_rec, index, prev_rec_offsets, !level,
n_uniq, &heap);
cmp_rec_rec_with_match(rec,
prev_rec,
rec_offsets,
prev_rec_offsets,
index,
FALSE,
&matched_fields);
cmp_rec_rec(prev_rec, rec,
prev_rec_offsets, rec_offsets, index,
false, &matched_fields);
for (i = matched_fields; i < n_uniq; i++) {
@ -1395,9 +1391,8 @@ dict_stats_scan_page(
/* check whether rec != next_rec when looking at
the first n_prefix fields */
cmp_rec_rec_with_match(rec, next_rec,
offsets_rec, offsets_next_rec,
index, FALSE, &matched_fields);
cmp_rec_rec(rec, next_rec, offsets_rec, offsets_next_rec,
index, false, &matched_fields);
if (matched_fields < n_prefix) {
/* rec != next_rec, => rec is non-boring */

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 2019, MariaDB Corporation.
Copyright (c) 2018, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1436,10 +1436,9 @@ rtr_page_copy_rec_list_end_no_locks(
offsets2 = rec_get_offsets(cur_rec, index, offsets2,
is_leaf,
ULINT_UNDEFINED, &heap);
cmp = cmp_rec_rec_with_match(cur1_rec, cur_rec,
offsets1, offsets2,
index, FALSE,
&cur_matched_fields);
cmp = cmp_rec_rec(cur1_rec, cur_rec,
offsets1, offsets2, index, false,
&cur_matched_fields);
if (cmp < 0) {
page_cur_move_to_prev(&page_cur);
break;
@ -1551,15 +1550,13 @@ rtr_page_copy_rec_list_start_no_locks(
while (!page_rec_is_supremum(cur_rec)) {
ulint cur_matched_fields = 0;
int cmp;
offsets2 = rec_get_offsets(cur_rec, index, offsets2,
is_leaf,
ULINT_UNDEFINED, &heap);
cmp = cmp_rec_rec_with_match(cur1_rec, cur_rec,
offsets1, offsets2,
index, FALSE,
&cur_matched_fields);
int cmp = cmp_rec_rec(cur1_rec, cur_rec,
offsets1, offsets2, index, false,
&cur_matched_fields);
if (cmp < 0) {
page_cur_move_to_prev(&page_cur);
cur_rec = page_cur_get_rec(&page_cur);

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -784,18 +784,6 @@ btr_rec_copy_externally_stored_field(
ulint* len,
mem_heap_t* heap);
/*******************************************************************//**
Flags the data tuple fields that are marked as extern storage in the
update vector. We use this function to remember which fields we must
mark as extern storage in a record inserted for an update.
@return number of flagged external columns */
ulint
btr_push_update_extern_fields(
/*==========================*/
dtuple_t* tuple, /*!< in/out: data tuple */
const upd_t* update, /*!< in: update vector */
mem_heap_t* heap) /*!< in: memory heap */
MY_ATTRIBUTE((nonnull));
/***********************************************************//**
Sets a secondary index record's delete mark to the given value. This
function is only used by the insert buffer merge mechanism. */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -857,6 +857,22 @@ page_rec_is_last(
const page_t* page) /*!< in: page */
MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
true if distance between the records (measured in number of times we have to
move to the next record) is at most the specified value
@param[in] left_rec lefter record
@param[in] right_rec righter record
@param[in] val specified value to compare
@return true if the distance is smaller than the value */
UNIV_INLINE
bool
page_rec_distance_is_at_most(
/*=========================*/
const rec_t* left_rec,
const rec_t* right_rec,
ulint val)
MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
true if the record is the second last user record on a page.
@return true if the second last user record */

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -358,6 +358,26 @@ page_rec_is_last(
return(page_rec_get_next_const(rec) == page_get_supremum_rec(page));
}
/************************************************************//**
true if distance between the records (measured in number of times we have to
move to the next record) is at most the specified value */
UNIV_INLINE
bool
page_rec_distance_is_at_most(
/*=========================*/
const rec_t* left_rec,
const rec_t* right_rec,
ulint val)
{
for (ulint i = 0; i <= val; i++) {
if (left_rec == right_rec) {
return (true);
}
left_rec = page_rec_get_next_const(left_rec);
}
return (false);
}
/************************************************************//**
true if the record is the second last user record on a page.
@return true if the second last user record */

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -80,7 +80,7 @@ cmp_dfield_dfield(
/** Compare a GIS data tuple to a physical record.
@param[in] dtuple data tuple
@param[in] rec B-tree record
@param[in] rec R-tree record
@param[in] offsets rec_get_offsets(rec)
@param[in] mode compare mode
@retval negative if dtuple is less than rec */
@ -190,43 +190,23 @@ cmp_rec_rec_simple(
duplicate key value if applicable,
or NULL */
MY_ATTRIBUTE((nonnull(1,2,3,4), warn_unused_result));
/** Compare two B-tree records.
@param[in] rec1 B-tree record
@param[in] rec2 B-tree record
@param[in] offsets1 rec_get_offsets(rec1, index)
@param[in] offsets2 rec_get_offsets(rec2, index)
@param[in] index B-tree index
@param[in] nulls_unequal true if this is for index cardinality
statistics estimation, and innodb_stats_method=nulls_unequal
or innodb_stats_method=nulls_ignored
@param[out] matched_fields number of completely matched fields
within the first field not completely matched
@return the comparison result
@retval 0 if rec1 is equal to rec2
@retval negative if rec1 is less than rec2
@retval positive if rec2 is greater than rec2 */
int
cmp_rec_rec_with_match(
const rec_t* rec1,
const rec_t* rec2,
const offset_t* offsets1,
const offset_t* offsets2,
const dict_index_t* index,
bool nulls_unequal,
ulint* matched_fields);
/** Compare two B-tree records.
/** Compare two B-tree or R-tree records.
Only the common first fields are compared, and externally stored field
are treated as equal.
@param[in] rec1 B-tree record
@param[in] rec2 B-tree record
@param[in] rec1 record (possibly not on an index page)
@param[in] rec2 B-tree or R-tree record in an index page
@param[in] offsets1 rec_get_offsets(rec1, index)
@param[in] offsets2 rec_get_offsets(rec2, index)
@param[in] nulls_unequal true if this is for index cardinality
statistics estimation with
innodb_stats_method=nulls_unequal
or innodb_stats_method=nulls_ignored
@param[out] matched_fields number of completely matched fields
within the first field not completely matched
@return positive, 0, negative if rec1 is greater, equal, less, than rec2,
respectively */
UNIV_INLINE
@retval 0 if rec1 is equal to rec2
@retval negative if rec1 is less than rec2
@retval positive if rec1 is greater than rec2 */
int
cmp_rec_rec(
const rec_t* rec1,
@ -234,7 +214,9 @@ cmp_rec_rec(
const offset_t* offsets1,
const offset_t* offsets2,
const dict_index_t* index,
ulint* matched_fields = NULL);
bool nulls_unequal = false,
ulint* matched_fields = NULL)
MY_ATTRIBUTE((nonnull(1,2,3,4,5)));
/** Compare two data fields.
@param[in] dfield1 data field

View file

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -52,40 +53,6 @@ cmp_dfield_dfield(
dfield_get_len(dfield2)));
}
/** Compare two B-tree records.
Only the common first fields are compared, and externally stored field
are treated as equal.
@param[in] rec1 B-tree record
@param[in] rec2 B-tree record
@param[in] offsets1 rec_get_offsets(rec1, index)
@param[in] offsets2 rec_get_offsets(rec2, index)
@param[out] matched_fields number of completely matched fields
within the first field not completely matched
@return positive, 0, negative if rec1 is greater, equal, less, than rec2,
respectively */
UNIV_INLINE
int
cmp_rec_rec(
const rec_t* rec1,
const rec_t* rec2,
const offset_t* offsets1,
const offset_t* offsets2,
const dict_index_t* index,
ulint* matched_fields)
{
ulint match_f;
int ret;
ret = cmp_rec_rec_with_match(
rec1, rec2, offsets1, offsets2, index, false, &match_f);
if (matched_fields != NULL) {
*matched_fields = match_f;
}
return(ret);
}
/** Compare two data fields.
@param[in] dfield1 data field
@param[in] dfield2 data field

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1410,6 +1410,7 @@ rec_get_converted_size(
data_size = dtuple_get_data_size(dtuple, 0);
ut_ad(n_ext == dtuple_get_n_ext(dtuple));
extra_size = rec_get_converted_extra_size(
data_size, dtuple_get_n_fields(dtuple), n_ext);

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2017, MariaDB Corporation.
Copyright (c) 2016, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -207,8 +207,6 @@ row_rec_to_index_entry_low(
const rec_t* rec, /*!< in: record in the index */
const dict_index_t* index, /*!< in: index */
const offset_t* offsets,/*!< in: rec_get_offsets(rec, index) */
ulint* n_ext, /*!< out: number of externally
stored columns */
mem_heap_t* heap) /*!< in: memory heap from which
the memory needed is allocated */
MY_ATTRIBUTE((warn_unused_result));
@ -222,8 +220,6 @@ row_rec_to_index_entry(
const rec_t* rec, /*!< in: record in the index */
const dict_index_t* index, /*!< in: index */
const offset_t* offsets,/*!< in/out: rec_get_offsets(rec) */
ulint* n_ext, /*!< out: number of externally
stored columns */
mem_heap_t* heap) /*!< in: memory heap from which
the memory needed is allocated */
MY_ATTRIBUTE((warn_unused_result));

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2019, MariaDB Corporation.
Copyright (c) 2013, 2020, MariaDB Corporation.
Copyright (c) 2008, Google Inc.
Portions of this file contain modifications contributed and copyrighted by

View file

@ -1,8 +1,8 @@
/***********************************************************************
Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2013, 2019, MariaDB Corporation.
Copyright (c) 2013, 2020, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted
by Percona Inc.. Those modifications are
@ -1470,6 +1470,12 @@ os_file_get_parent_dir(
return(NULL);
}
if (last_slash - path < 0) {
/* Sanity check, it prevents gcc from trying to handle this case which
* results in warnings for some optimized builds */
return (NULL);
}
/* Non-trivial directory component */
return(mem_strdupl(path, ulint(last_slash - path)));

View file

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -534,7 +535,7 @@ cmp_data(
/** Compare a GIS data tuple to a physical record.
@param[in] dtuple data tuple
@param[in] rec B-tree record
@param[in] rec R-tree record
@param[in] offsets rec_get_offsets(rec)
@param[in] mode compare mode
@retval negative if dtuple is less than rec */
@ -1096,23 +1097,24 @@ cmp_rec_rec_simple(
return(0);
}
/** Compare two B-tree records.
@param[in] rec1 B-tree record
@param[in] rec2 B-tree record
@param[in] offsets1 rec_get_offsets(rec1, index)
@param[in] offsets2 rec_get_offsets(rec2, index)
@param[in] index B-tree index
@param[in] nulls_unequal true if this is for index cardinality
statistics estimation, and innodb_stats_method=nulls_unequal
or innodb_stats_method=nulls_ignored
@param[out] matched_fields number of completely matched fields
within the first field not completely matched
@return the comparison result
/** Compare two B-tree or R-tree records.
Only the common first fields are compared, and externally stored field
are treated as equal.
@param[in] rec1 record (possibly not on an index page)
@param[in] rec2 B-tree or R-tree record in an index page
@param[in] offsets1 rec_get_offsets(rec1, index)
@param[in] offsets2 rec_get_offsets(rec2, index)
@param[in] nulls_unequal true if this is for index cardinality
statistics estimation with
innodb_stats_method=nulls_unequal
or innodb_stats_method=nulls_ignored
@param[out] matched_fields number of completely matched fields
within the first field not completely matched
@retval 0 if rec1 is equal to rec2
@retval negative if rec1 is less than rec2
@retval positive if rec2 is greater than rec2 */
@retval positive if rec1 is greater than rec2 */
int
cmp_rec_rec_with_match(
cmp_rec_rec(
const rec_t* rec1,
const rec_t* rec2,
const offset_t* offsets1,
@ -1121,17 +1123,14 @@ cmp_rec_rec_with_match(
bool nulls_unequal,
ulint* matched_fields)
{
ulint rec1_n_fields; /* the number of fields in rec */
ulint rec1_f_len; /* length of current field in rec */
const byte* rec1_b_ptr; /* pointer to the current byte
in rec field */
ulint rec2_n_fields; /* the number of fields in rec */
ulint rec2_f_len; /* length of current field in rec */
const byte* rec2_b_ptr; /* pointer to the current byte
in rec field */
ulint cur_field = 0; /* current field number */
int ret = 0; /* return value */
ulint comp;
ut_ad(rec1 != NULL);
ut_ad(rec2 != NULL);
@ -1139,10 +1138,12 @@ cmp_rec_rec_with_match(
ut_ad(rec_offs_validate(rec1, index, offsets1));
ut_ad(rec_offs_validate(rec2, index, offsets2));
ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2));
ut_ad(fil_page_index_page_check(page_align(rec2)));
ut_ad(!!dict_index_is_spatial(index)
== (fil_page_get_type(page_align(rec2)) == FIL_PAGE_RTREE));
comp = rec_offs_comp(offsets1);
rec1_n_fields = rec_offs_n_fields(offsets1);
rec2_n_fields = rec_offs_n_fields(offsets2);
ulint comp = rec_offs_comp(offsets1);
ulint n_fields;
/* Test if rec is the predefined minimum record */
if (UNIV_UNLIKELY(rec_get_info_bits(rec1, comp)
@ -1158,37 +1159,41 @@ cmp_rec_rec_with_match(
goto order_resolved;
}
/* Match fields in a loop */
/* For non-leaf spatial index records, the
dict_index_get_n_unique_in_tree() does include the child page
number, because spatial index node pointers only contain
the MBR (minimum bounding rectangle) and the child page number.
for (; cur_field < rec1_n_fields && cur_field < rec2_n_fields;
cur_field++) {
For B-tree node pointers, the key alone (secondary index
columns and PRIMARY KEY columns) must be unique, and there is
no need to compare the child page number. */
n_fields = std::min(rec_offs_n_fields(offsets1),
rec_offs_n_fields(offsets2));
n_fields = std::min(n_fields, dict_index_get_n_unique_in_tree(index));
for (; cur_field < n_fields; cur_field++) {
ulint mtype;
ulint prtype;
/* If this is node-ptr records then avoid comparing node-ptr
field. Only key field needs to be compared. */
if (cur_field == dict_index_get_n_unique_in_tree(index)) {
break;
}
if (dict_index_is_ibuf(index)) {
if (UNIV_UNLIKELY(dict_index_is_ibuf(index))) {
/* This is for the insert buffer B-tree. */
mtype = DATA_BINARY;
prtype = 0;
} else {
const dict_col_t* col;
col = dict_index_get_nth_col(index, cur_field);
const dict_col_t* col = dict_index_get_nth_col(
index, cur_field);
mtype = col->mtype;
prtype = col->prtype;
/* If the index is spatial index, we mark the
prtype of the first field as MBR field. */
if (cur_field == 0 && dict_index_is_spatial(index)) {
if (UNIV_LIKELY(!dict_index_is_spatial(index))) {
} else if (cur_field == 0) {
ut_ad(DATA_GEOMETRY_MTYPE(mtype));
prtype |= DATA_GIS_MBR;
} else if (!page_rec_is_leaf(rec2)) {
/* Compare the child page number. */
ut_ad(cur_field == 1);
mtype = DATA_SYS_CHILD;
prtype = 0;
}
}
@ -1226,8 +1231,10 @@ cmp_rec_rec_with_match(
to the common fields */
ut_ad(ret == 0);
order_resolved:
*matched_fields = cur_field;
return(ret);
if (matched_fields) {
*matched_fields = cur_field;
}
return ret;
}
#ifdef UNIV_COMPILE_TEST_FUNCS

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2010, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1722,7 +1722,6 @@ row_fts_merge_insert(
corresponding FTS index auxiliary tables */
for (;;) {
dtuple_t* dtuple;
ulint n_ext;
int min_rec = 0;
if (fts_sort_pll_degree <= 2) {
@ -1765,7 +1764,7 @@ row_fts_merge_insert(
}
dtuple = row_rec_to_index_entry_low(
mrec[min_rec], index, offsets[min_rec], &n_ext,
mrec[min_rec], index, offsets[min_rec],
tuple_heap);
row_fts_insert_tuple(

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1526,13 +1526,70 @@ IndexPurge::next() UNIV_NOTHROW
mtr_set_log_mode(&m_mtr, MTR_LOG_NO_REDO);
btr_pcur_restore_position(BTR_MODIFY_LEAF, &m_pcur, &m_mtr);
/* The following is based on btr_pcur_move_to_next_user_rec(). */
m_pcur.old_stored = false;
ut_ad(m_pcur.latch_mode == BTR_MODIFY_LEAF);
do {
if (btr_pcur_is_after_last_on_page(&m_pcur)) {
if (btr_pcur_is_after_last_in_tree(&m_pcur)) {
return DB_END_OF_INDEX;
}
if (!btr_pcur_move_to_next_user_rec(&m_pcur, &m_mtr)) {
buf_block_t* block = btr_pcur_get_block(&m_pcur);
uint32_t next_page = btr_page_get_next(block->frame);
return(DB_END_OF_INDEX);
}
/* MDEV-13542 FIXME: Make these checks part of
btr_pcur_move_to_next_page(), and introduce a
return status that will be checked in all callers! */
switch (next_page) {
default:
if (next_page != block->page.id.page_no()) {
break;
}
/* MDEV-20931 FIXME: Check that
next_page is within the tablespace
bounds! Also check that it is not a
change buffer bitmap page. */
/* fall through */
case 0:
case 1:
case FIL_NULL:
return DB_CORRUPTION;
}
return(DB_SUCCESS);
dict_index_t* index = m_pcur.btr_cur.index;
buf_block_t* next_block = btr_block_get(
page_id_t(block->page.id.space(), next_page),
block->page.size, BTR_MODIFY_LEAF, index,
&m_mtr);
if (UNIV_UNLIKELY(!next_block
|| !fil_page_index_page_check(
next_block->frame)
|| !!dict_index_is_spatial(index)
!= (fil_page_get_type(
next_block->frame)
== FIL_PAGE_RTREE)
|| page_is_comp(next_block->frame)
!= page_is_comp(block->frame)
|| btr_page_get_prev(
next_block->frame)
!= block->page.id.page_no())) {
return DB_CORRUPTION;
}
btr_leaf_page_release(block, BTR_MODIFY_LEAF, &m_mtr);
page_cur_set_before_first(next_block,
&m_pcur.btr_cur.page_cur);
ut_d(page_check_dir(next_block->frame));
} else {
btr_pcur_move_to_next_on_page(&m_pcur);
}
} while (!btr_pcur_is_on_user_rec(&m_pcur));
return DB_SUCCESS;
}
/**

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2011, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -3559,7 +3559,6 @@ row_log_apply_op(
enum row_op op;
ulint extra_size;
ulint data_size;
ulint n_ext;
dtuple_t* entry;
trx_id_t trx_id;
@ -3637,10 +3636,10 @@ corrupted:
}
entry = row_rec_to_index_entry_low(
mrec - data_size, index, offsets, &n_ext, heap);
mrec - data_size, index, offsets, heap);
/* Online index creation is only implemented for secondary
indexes, which never contain off-page columns. */
ut_ad(n_ext == 0);
ut_ad(dtuple_get_n_ext(entry) == 0);
row_log_apply_op_low(index, dup, error, offsets_heap,
has_index_lock, op, trx_id, entry);

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2019, MariaDB Corporation.
Copyright (c) 2014, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -3535,7 +3535,6 @@ row_merge_insert_index_tuples(
double curr_progress = 0;
dict_index_t* old_index = NULL;
const mrec_t* mrec = NULL;
ulint n_ext = 0;
mtr_t mtr;
@ -3601,8 +3600,6 @@ row_merge_insert_index_tuples(
row buffer to data tuple record */
row_merge_mtuple_to_dtuple(
index, dtuple, &row_buf->tuples[n_rows]);
n_ext = dtuple_get_n_ext(dtuple);
n_rows++;
/* BLOB pointers must be copied from dtuple */
mrec = NULL;
@ -3621,7 +3618,7 @@ row_merge_insert_index_tuples(
}
dtuple = row_rec_to_index_entry_low(
mrec, index, offsets, &n_ext, tuple_heap);
mrec, index, offsets, tuple_heap);
}
old_index = dict_table_get_first_index(old_table);
@ -3634,10 +3631,7 @@ row_merge_insert_index_tuples(
}
}
if (!n_ext) {
/* There are no externally stored columns. */
} else {
ut_ad(dict_index_is_clust(index));
if (dict_index_is_clust(index) && dtuple_get_n_ext(dtuple)) {
/* Off-page columns can be fetched safely
when concurrent modifications to the table
are disabled. (Purge can process delete-marked

View file

@ -4681,7 +4681,6 @@ row_scan_index_for_mysql(
ulint i;
ulint cnt;
mem_heap_t* heap = NULL;
ulint n_ext;
offset_t offsets_[REC_OFFS_NORMAL_SIZE];
offset_t* offsets;
rec_offs_init(offsets_);
@ -4823,7 +4822,7 @@ not_ok:
mem_heap_empty(heap);
prev_entry = row_rec_to_index_entry(
rec, index, offsets, &n_ext, heap);
rec, index, offsets, heap);
if (UNIV_LIKELY_NULL(tmp_heap)) {
mem_heap_free(tmp_heap);

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 2019, MariaDB Corporation.
Copyright (c) 2018, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -685,7 +685,6 @@ row_rec_to_index_entry_impl(
const rec_t* rec,
const dict_index_t* index,
const offset_t* offsets,
ulint* n_ext,
mem_heap_t* heap)
{
dtuple_t* entry;
@ -703,8 +702,6 @@ row_rec_to_index_entry_impl(
/* Because this function may be invoked by row0merge.cc
on a record whose header is in different format, the check
rec_offs_validate(rec, index, offsets) must be avoided here. */
ut_ad(n_ext);
*n_ext = 0;
rec_len = rec_offs_n_fields(offsets);
@ -731,7 +728,6 @@ row_rec_to_index_entry_impl(
if (rec_offs_nth_extern(offsets, i)) {
dfield_set_ext(dfield);
(*n_ext)++;
}
}
@ -743,18 +739,15 @@ row_rec_to_index_entry_impl(
@param[in] rec index record
@param[in] index index
@param[in] offsets rec_get_offsets(rec, index)
@param[out] n_ext number of externally stored columns
@param[in,out] heap memory heap for allocations */
dtuple_t*
row_rec_to_index_entry_low(
const rec_t* rec,
const dict_index_t* index,
const offset_t* offsets,
ulint* n_ext,
mem_heap_t* heap)
{
return row_rec_to_index_entry_impl<false>(
rec, index, offsets, n_ext, heap);
return row_rec_to_index_entry_impl<false>(rec, index, offsets, heap);
}
/*******************************************************************//**
@ -767,8 +760,6 @@ row_rec_to_index_entry(
const rec_t* rec, /*!< in: record in the index */
const dict_index_t* index, /*!< in: index */
const offset_t* offsets,/*!< in: rec_get_offsets(rec) */
ulint* n_ext, /*!< out: number of externally
stored columns */
mem_heap_t* heap) /*!< in: memory heap from which
the memory needed is allocated */
{
@ -790,7 +781,7 @@ row_rec_to_index_entry(
rec_offs_make_valid(copy_rec, index, true,
const_cast<offset_t*>(offsets));
entry = row_rec_to_index_entry_impl<true>(
copy_rec, index, offsets, n_ext, heap);
copy_rec, index, offsets, heap);
rec_offs_make_valid(rec, index, true,
const_cast<offset_t*>(offsets));

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -186,6 +186,7 @@ static bool row_undo_mod_must_purge(undo_node_t* node, mtr_t* mtr)
btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&node->pcur);
ut_ad(btr_cur->index->is_primary());
DEBUG_SYNC_C("rollback_purge_clust");
mtr->s_lock(&purge_sys.latch, __FILE__, __LINE__);
@ -319,6 +320,7 @@ row_undo_mod_clust(
== node->new_trx_id);
btr_pcur_commit_specify_mtr(pcur, &mtr);
DEBUG_SYNC_C("rollback_undo_pk");
if (err != DB_SUCCESS) {
goto func_exit;

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -223,7 +223,6 @@ row_upd_check_references_constraints(
dtuple_t* entry;
trx_t* trx;
const rec_t* rec;
ulint n_ext;
dberr_t err;
ibool got_s_lock = FALSE;
@ -240,7 +239,7 @@ row_upd_check_references_constraints(
heap = mem_heap_create(500);
entry = row_rec_to_index_entry(rec, index, offsets, &n_ext, heap);
entry = row_rec_to_index_entry(rec, index, offsets, heap);
mtr_commit(mtr);
@ -341,7 +340,6 @@ wsrep_row_upd_check_foreign_constraints(
dtuple_t* entry;
trx_t* trx;
const rec_t* rec;
ulint n_ext;
dberr_t err;
ibool got_s_lock = FALSE;
ibool opened = FALSE;
@ -359,8 +357,7 @@ wsrep_row_upd_check_foreign_constraints(
heap = mem_heap_create(500);
entry = row_rec_to_index_entry(rec, index, offsets,
&n_ext, heap);
entry = row_rec_to_index_entry(rec, index, offsets, heap);
mtr_commit(mtr);
@ -2812,9 +2809,9 @@ check_fk:
mtr_commit(mtr);
err = row_ins_clust_index_entry(
index, entry, thr,
node->upd_ext ? node->upd_ext->n_ext : 0);
err = row_ins_clust_index_entry(index, entry, thr, node->upd_ext
? node->upd_ext->n_ext
: dtuple_get_n_ext(entry));
node->state = UPD_NODE_INSERT_CLUSTERED;
mem_heap_free(heap);

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -153,15 +153,13 @@ row_vers_impl_x_locked_low(
const ulint rec_del = rec_get_deleted_flag(rec, comp);
if (dict_index_has_virtual(index)) {
ulint n_ext;
ulint est_size = DTUPLE_EST_ALLOC(index->n_fields);
/* Allocate the dtuple for virtual columns extracted from undo
log with its own heap, so to avoid it being freed as we
iterating in the version loop below. */
v_heap = mem_heap_create(est_size);
ientry = row_rec_to_index_entry(
rec, index, offsets, &n_ext, v_heap);
ientry = row_rec_to_index_entry(rec, index, offsets, v_heap);
}
/* We look up if some earlier version, which was modified by

View file

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -2335,8 +2335,6 @@ trx_undo_prev_version_build(
ut_a(ptr);
if (row_upd_changes_field_size_or_external(index, offsets, update)) {
ulint n_ext;
/* We should confirm the existence of disowned external data,
if the previous version record is delete marked. If the trx_id
of the previous record is seen by purge view, we should treat
@ -2377,14 +2375,15 @@ trx_undo_prev_version_build(
those fields that update updates to become externally stored
fields. Store the info: */
entry = row_rec_to_index_entry(
rec, index, offsets, &n_ext, heap);
n_ext += btr_push_update_extern_fields(entry, update, heap);
entry = row_rec_to_index_entry(rec, index, offsets, heap);
/* The page containing the clustered index record
corresponding to entry is latched in mtr. Thus the
following call is safe. */
row_upd_index_replace_new_col_vals(entry, index, update, heap);
/* Get number of externally stored columns in updated record */
const ulint n_ext = dtuple_get_n_ext(entry);
buf = static_cast<byte*>(mem_heap_alloc(
heap, rec_get_converted_size(index, entry, n_ext)));