Merge remote-tracking branch 'origin/10.3' into 10.4

This commit is contained in:
Alexander Barkov 2018-07-25 08:25:57 +04:00
commit 9c0f5a252b
81 changed files with 1374 additions and 464 deletions

View File

@ -47,7 +47,7 @@ IF(CMAKE_C_COMPILER MATCHES "icl")
ENDIF()
ADD_DEFINITIONS(-D_WINDOWS -D__WIN__ -D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0600)
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0A00)
# We do not want the windows.h macros min/max
ADD_DEFINITIONS(-DNOMINMAX)
# Speed up build process excluding unused header files

View File

@ -10,9 +10,9 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values (100,100);
insert into t1 values (1,1);
ERROR 23000: CONSTRAINT `a` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.a` failed for `test`.`t1`
insert into t1 values (20,1);
ERROR 23000: CONSTRAINT `b` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.b` failed for `test`.`t1`
insert into t1 values (20,30);
ERROR 23000: CONSTRAINT `min` failed for `test`.`t1`
insert into t1 values (500,500);
@ -60,7 +60,7 @@ t1 CREATE TABLE `t1` (
CONSTRAINT `CONSTRAINT_1` CHECK (`a` + `b` + `c` < 500)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values(105,105,105);
ERROR 23000: CONSTRAINT `c` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.c` failed for `test`.`t1`
insert into t1 values(249,249,9);
ERROR 23000: CONSTRAINT `CONSTRAINT_1` failed for `test`.`t1`
insert into t1 values(105,105,9);
@ -145,7 +145,7 @@ ERROR HY000: Function or expression '@b' cannot be used in the CHECK clause of `
create table t1 (a int check (a = 1));
insert t1 values (1);
insert t1 values (2);
ERROR 23000: CONSTRAINT `a` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.a` failed for `test`.`t1`
insert t1 values (NULL);
select * from t1;
a
@ -165,13 +165,13 @@ ERROR 22007: Truncated incorrect DOUBLE value: 'Ken'
SHOW WARNINGS;
Level Code Message
Error 1292 Truncated incorrect DOUBLE value: 'Ken'
Error 4025 CONSTRAINT `FirstName` failed for `test`.`t1`
Error 4025 CONSTRAINT `t1.FirstName` failed for `test`.`t1`
INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian');
ERROR 22007: Truncated incorrect DOUBLE value: 'Ken'
SHOW WARNINGS;
Level Code Message
Error 1292 Truncated incorrect DOUBLE value: 'Ken'
Error 4025 CONSTRAINT `FirstName` failed for `test`.`t1`
Error 4025 CONSTRAINT `t1.FirstName` failed for `test`.`t1`
INSERT IGNORE INTO t1 VALUES (NULL, 'Ken');
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'Ken'
@ -197,3 +197,28 @@ EmployeeID FirstName
5 Ken
6 Brian
drop table t1;
#
# MDEV-16630: Ambiguous error message when check constraint
# matches table name
#
use test;
drop table if exists t;
create table t(a int, b int check(b>0),
constraint b check(a<b), constraint a check(a>0),
constraint x check (a>10));
show create table t;
Table Create Table
t CREATE TABLE `t` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL CHECK (`b` > 0),
CONSTRAINT `b` CHECK (`a` < `b`),
CONSTRAINT `a` CHECK (`a` > 0),
CONSTRAINT `x` CHECK (`a` > 10)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
# Field constraint 'b' will fail
insert into t values (-1, 0);
ERROR 23000: CONSTRAINT `t.b` failed for `test`.`t`
# Table constraint 'b' will fail
insert into t values (1,1);
ERROR 23000: CONSTRAINT `b` failed for `test`.`t`
drop table t;

View File

@ -135,3 +135,31 @@ INSERT INTO t1 VALUES (NULL, 'Ken'),(NULL, 'Brian');
set sql_mode=default;
select * from t1;
drop table t1;
--echo #
--echo # MDEV-16630: Ambiguous error message when check constraint
--echo # matches table name
--echo #
use test;
--disable_warnings
drop table if exists t;
--enable_warnings
create table t(a int, b int check(b>0),
constraint b check(a<b), constraint a check(a>0),
constraint x check (a>10));
show create table t;
# Generate error when field constraint 'b' is violated
--echo # Field constraint 'b' will fail
--error ER_CONSTRAINT_FAILED
insert into t values (-1, 0);
# Generate error when table constraint 'b' is violated.
--echo # Table constraint 'b' will fail
--error ER_CONSTRAINT_FAILED
insert into t values (1,1);
drop table t;

View File

@ -1472,3 +1472,17 @@ SELECT LEFT(a, 10), LENGTH(a) FROM t1;
LEFT(a, 10) LENGTH(a)
aaaaaaaaaa 255
DROP TABLE t1;
#
# MDEV-16729 VARCHAR COMPRESSED is created with a wrong length for multi-byte character sets
#
CREATE OR REPLACE TABLE t1 (a VARCHAR(1000) CHARACTER SET utf8 COMPRESSED);
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` varchar(1000) /*!100301 COMPRESSED*/ CHARACTER SET utf8 DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
COLUMN_TYPE
varchar(1000) /*!100301 COMPRESSED*/
DROP TABLE t1;

View File

@ -170,3 +170,14 @@ CREATE TABLE t1(a TINYTEXT COMPRESSED);
INSERT INTO t1 VALUES(REPEAT('a', 255));
SELECT LEFT(a, 10), LENGTH(a) FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-16729 VARCHAR COMPRESSED is created with a wrong length for multi-byte character sets
--echo #
CREATE OR REPLACE TABLE t1 (a VARCHAR(1000) CHARACTER SET utf8 COMPRESSED);
SHOW CREATE TABLE t1;
SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1';
DROP TABLE t1;

View File

@ -7,7 +7,7 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert into t1 values (1);
insert into t1 values (0);
ERROR 23000: CONSTRAINT `a` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.a` failed for `test`.`t1`
drop table t1;
create table t1 (a int, b int, check (a>b));
show create table t1;

View File

@ -0,0 +1,4 @@
CREATE TEMPORARY TABLE t (i INT);
CREATE or replace TABLE t AS SELECT * FROM t;
DROP TEMPORARY TABLE t;
DROP TABLE t;

View File

@ -0,0 +1,4 @@
CREATE TEMPORARY TABLE t (i INT);
CREATE or replace TABLE t AS SELECT * FROM t;
DROP TEMPORARY TABLE t;
DROP TABLE t;

View File

@ -3269,6 +3269,43 @@ select 3, 0*(@d:=@d+1) from qn where @d<1
select * from qn;
ERROR 42000: This version of MariaDB doesn't yet support 'mix of ALL and DISTINCT UNION operations in recursive CTE spec'
drop table t1;
#
# MDEV-16629: function with recursive CTE using a base table
#
CREATE TABLE t1 (id int);
INSERT INTO t1 VALUES (0), (1),(2);
WITH recursive cte AS
(SELECT id FROM t1 UNION SELECT 3 FROM cte)
SELECT count(id) FROM cte;
count(id)
4
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH recursive cte AS
(SELECT id FROM t1 UNION SELECT 3 FROM cte)
SELECT count(id) FROM cte
);
SELECT func();
func()
4
DROP FUNCTION func;
DROP TABLE t1;
#
# MDEV-16661: function with recursive CTE using no base tables
# (fixed by the patch for MDEV-16629)
#
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH RECURSIVE cte AS
(SELECT 1 as id UNION SELECT * FROM cte)
SELECT count(id) FROM cte
);
SELECT func();
func()
1
DROP FUNCTION func;
# Start of 10.3 tests
#
# MDEV-14217 [db crash] Recursive CTE when SELECT includes new field

View File

@ -2283,6 +2283,47 @@ select * from qn;
drop table t1;
--echo #
--echo # MDEV-16629: function with recursive CTE using a base table
--echo #
CREATE TABLE t1 (id int);
INSERT INTO t1 VALUES (0), (1),(2);
WITH recursive cte AS
(SELECT id FROM t1 UNION SELECT 3 FROM cte)
SELECT count(id) FROM cte;
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH recursive cte AS
(SELECT id FROM t1 UNION SELECT 3 FROM cte)
SELECT count(id) FROM cte
);
SELECT func();
DROP FUNCTION func;
DROP TABLE t1;
--echo #
--echo # MDEV-16661: function with recursive CTE using no base tables
--echo # (fixed by the patch for MDEV-16629)
--echo #
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH RECURSIVE cte AS
(SELECT 1 as id UNION SELECT * FROM cte)
SELECT count(id) FROM cte
);
SELECT func();
DROP FUNCTION func;
--echo # Start of 10.3 tests
--echo #

View File

@ -0,0 +1,23 @@
#
# MDEV-15151: function with recursive CTE using no base tables
# (duplicate of MDEV-16661)
#
connection default;
CREATE TABLE t1 (id int KEY);
INSERT INTO t1 VALUES (0), (1),(2);
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH recursive cte AS
(SELECT 1 a UNION SELECT cte.* FROM cte natural join t1)
SELECT * FROM cte limit 1
);
connect con1,localhost,root,,test;
SELECT func();
connect con2,localhost,root,,test;
disconnect con2;
connection con1;
disconnect con1;
connection default;
DROP FUNCTION func;
DROP TABLE t1;

View File

@ -0,0 +1,42 @@
--source include/not_embedded.inc
--echo #
--echo # MDEV-15151: function with recursive CTE using no base tables
--echo # (duplicate of MDEV-16661)
--echo #
--connection default
CREATE TABLE t1 (id int KEY);
INSERT INTO t1 VALUES (0), (1),(2);
CREATE OR REPLACE FUNCTION func() RETURNS int
RETURN
(
WITH recursive cte AS
(SELECT 1 a UNION SELECT cte.* FROM cte natural join t1)
SELECT * FROM cte limit 1
);
--connect (con1,localhost,root,,test)
--let $conid= `SELECT CONNECTION_ID()`
--send SELECT func()
--connect (con2,localhost,root,,test)
--disable_query_log
--eval KILL QUERY $conid
--enable_query_log
--disconnect con2
--disable_result_log
--connection con1
--error 0,ER_QUERY_INTERRUPTED
--reap
--disconnect con1
--enable_result_log
--connection default
DROP FUNCTION func;
DROP TABLE t1;

View File

@ -297,7 +297,7 @@ json_quote('foo')
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`json_quote('foo')` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
`json_quote('foo')` varchar(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
select json_merge('string');
@ -748,6 +748,19 @@ SELECT * FROM t1 WHERE c IN (JSON_EXTRACT('{"a":"b"}', '$.*'));
c
DROP TABLE t1;
#
# MDEV-16814 CREATE TABLE SELECT JSON_QUOTE(multibyte_charset_expr) makes a field of a wrong length
#
CREATE TABLE t1 AS SELECT
JSON_QUOTE(_latin1'foo') AS c1,
JSON_QUOTE(_utf8'foo') AS c2;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`c1` varchar(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`c2` varchar(38) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP TABLE t1;
#
# End of 10.2 tests
#
#

View File

@ -412,6 +412,16 @@ INSERT INTO t1 VALUES ('foo'),('bar');
SELECT * FROM t1 WHERE c IN (JSON_EXTRACT('{"a":"b"}', '$.*'));
DROP TABLE t1;
--echo #
--echo # MDEV-16814 CREATE TABLE SELECT JSON_QUOTE(multibyte_charset_expr) makes a field of a wrong length
--echo #
CREATE TABLE t1 AS SELECT
JSON_QUOTE(_latin1'foo') AS c1,
JSON_QUOTE(_utf8'foo') AS c2;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # End of 10.2 tests
--echo #

View File

@ -20,7 +20,7 @@ t1 CREATE TABLE `t1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
insert t1 values ('[]');
insert t1 values ('a');
ERROR 23000: CONSTRAINT `a` failed for `test`.`t1`
ERROR 23000: CONSTRAINT `t1.a` failed for `test`.`t1`
set timestamp=unix_timestamp('2010:11:12 13:14:15');
create or replace table t1(a json default(json_object('now', now())));
show create table t1;

View File

@ -296,20 +296,21 @@ DROP TABLE t1;
# ASSERTION THD->TRANSACTION.XID_STATE.XID.IS_NULL()
# FAILED
#
DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
START TRANSACTION;
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (1);
COMMIT;
BEGIN;
INSERT INTO t2 VALUES (2);
UPDATE t2 SET a=a+1;
connect con2,localhost,root;
XA START 'xid1';
INSERT INTO t1 VALUES (1);
# Sending:
INSERT INTO t2 SELECT a FROM t1;
DELETE FROM t2;
connection default;
# Waiting until INSERT ... is blocked
DELETE FROM t1;
connection con2;
# Reaping: INSERT INTO t2 SELECT a FROM t1
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
XA COMMIT 'xid1';
ERROR XA102: XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected

View File

@ -393,33 +393,30 @@ DROP TABLE t1;
--echo # FAILED
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1, t2;
--enable_warnings
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
START TRANSACTION;
INSERT INTO t1 VALUES (1);
INSERT INTO t2 VALUES (1); COMMIT;
BEGIN;
INSERT INTO t2 VALUES (2);
UPDATE t2 SET a=a+1;
--connect (con2,localhost,root)
XA START 'xid1';
INSERT INTO t1 VALUES (1);
--echo # Sending:
--send INSERT INTO t2 SELECT a FROM t1
--send DELETE FROM t2
--connection default
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = "Sending data"
AND info = "INSERT INTO t2 SELECT a FROM t1";
--echo # Waiting until INSERT ... is blocked
WHERE state = "Updating"
AND info = "DELETE FROM t2";
--source include/wait_condition.inc
--sleep 0.1
DELETE FROM t1;
--send DELETE FROM t1
--connection con2
--echo # Reaping: INSERT INTO t2 SELECT a FROM t1
--error ER_LOCK_DEADLOCK
--reap
--error ER_XA_RBDEADLOCK
@ -427,6 +424,7 @@ XA COMMIT 'xid1';
connection default;
reap;
COMMIT;
connection con2;

View File

@ -175,8 +175,38 @@ SET DEBUG_SYNC='now WAIT_FOR halfway';
COMMIT;
InnoDB 0 transactions not purged
SET DEBUG_SYNC='now SIGNAL purged';
disconnect prevent_purge;
connection default;
DROP TABLE t1;
CREATE TABLE t1 (y YEAR, vy YEAR AS (y) VIRTUAL UNIQUE, pk INT PRIMARY KEY)
ENGINE=InnoDB;
INSERT INTO t1 (pk,y) VALUES (1,2022);
CREATE TABLE t2(f1 INT NOT NULL, PRIMARY KEY(f1))ENGINE=InnoDB;
SET GLOBAL debug_dbug = '+d,ib_purge_virtual_index_callback';
BEGIN;
INSERT INTO t2(f1) VALUES(1);
connection prevent_purge;
SET DEBUG_SYNC=RESET;
start transaction with consistent snapshot;
connection default;
COMMIT;
connect truncate,localhost,root,,;
REPLACE INTO t1(pk, y) SELECT pk,y FROM t1;
SET DEBUG_SYNC='row_trunc_before_dict_lock SIGNAL commit WAIT_FOR release';
TRUNCATE TABLE t1;
connection prevent_purge;
SET DEBUG_SYNC='now WAIT_FOR commit';
COMMIT;
SET DEBUG_SYNC='now SIGNAL purge_start';
disconnect prevent_purge;
connection default;
SET DEBUG_SYNC='now WAIT_FOR purge_start';
InnoDB 2 transactions not purged
SET DEBUG_SYNC='now SIGNAL release';
SET GLOBAL debug_dbug=@old_dbug;
connection truncate;
disconnect truncate;
connection default;
InnoDB 0 transactions not purged
DROP TABLE t1, t2;
set debug_sync=reset;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;

View File

@ -124,7 +124,7 @@ SELECT * FROM t1;
DROP TABLE t1;
# Test adding virutal index on existing virtual column
# Test adding index on existing virtual column
CREATE TABLE t1 (a INT, b INT, c INT GENERATED ALWAYS AS(a+b));
INSERT INTO t1(a, b) VALUES (1, 1), (2, 2), (3, 3), (4, 4);
@ -156,7 +156,9 @@ INSERT INTO t1(a, b) VALUES (8, 8);
COMMIT;
--echo # wait for purge to process the deleted/updated records.
let $wait_all_purged=2;
--source ../../innodb/include/wait_all_purged.inc
let $wait_all_purged=0;
SET DEBUG_SYNC= 'now SIGNAL purged';
@ -215,12 +217,54 @@ SET DEBUG_SYNC='now WAIT_FOR halfway';
COMMIT;
--source ../../innodb/include/wait_all_purged.inc
SET DEBUG_SYNC='now SIGNAL purged';
disconnect prevent_purge;
connection default;
reap;
DROP TABLE t1;
CREATE TABLE t1 (y YEAR, vy YEAR AS (y) VIRTUAL UNIQUE, pk INT PRIMARY KEY)
ENGINE=InnoDB;
INSERT INTO t1 (pk,y) VALUES (1,2022);
CREATE TABLE t2(f1 INT NOT NULL, PRIMARY KEY(f1))ENGINE=InnoDB;
SET GLOBAL debug_dbug = '+d,ib_purge_virtual_index_callback';
BEGIN;
INSERT INTO t2(f1) VALUES(1);
connection prevent_purge;
SET DEBUG_SYNC=RESET;
start transaction with consistent snapshot;
connection default;
COMMIT;
connect(truncate,localhost,root,,);
REPLACE INTO t1(pk, y) SELECT pk,y FROM t1;
SET DEBUG_SYNC='row_trunc_before_dict_lock SIGNAL commit WAIT_FOR release';
send TRUNCATE TABLE t1;
connection prevent_purge;
SET DEBUG_SYNC='now WAIT_FOR commit';
COMMIT;
SET DEBUG_SYNC='now SIGNAL purge_start';
disconnect prevent_purge;
connection default;
SET DEBUG_SYNC='now WAIT_FOR purge_start';
let $wait_all_purged=2;
--source ../../innodb/include/wait_all_purged.inc
let $wait_all_purged=0;
SET DEBUG_SYNC='now SIGNAL release';
SET GLOBAL debug_dbug=@old_dbug;
connection truncate;
reap;
disconnect truncate;
connection default;
--source ../../innodb/include/wait_all_purged.inc
DROP TABLE t1, t2;
--source include/wait_until_count_sessions.inc
set debug_sync=reset;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;

View File

@ -1,12 +1,18 @@
# Wait for everything to be purged.
# The user should have set innodb_purge_rseg_truncate_frequency=1.
if (!$wait_all_purged)
{
let $wait_all_purged= 0;
}
let $remaining_expect= `select concat('InnoDB ',$wait_all_purged)`;
let $wait_counter= 300;
while ($wait_counter)
{
--replace_regex /.*History list length ([0-9]+).*/\1/
let $remaining= `SHOW ENGINE INNODB STATUS`;
if ($remaining == 'InnoDB 0')
if ($remaining == $remaining_expect)
{
let $wait_counter= 0;
}

View File

@ -45,7 +45,7 @@ trx_last_foreign_key_error varchar(256) YES NULL
trx_is_read_only int(1) NO 0
trx_autocommit_non_locking int(1) NO 0
trx_state trx_weight trx_tables_in_use trx_tables_locked trx_rows_locked trx_rows_modified trx_concurrency_tickets trx_isolation_level trx_unique_checks trx_foreign_key_checks
RUNNING 4 0 1 7 1 0 REPEATABLE READ 1 1
RUNNING 3 0 1 5 1 0 REPEATABLE READ 1 1
trx_isolation_level trx_unique_checks trx_foreign_key_checks
SERIALIZABLE 0 0
trx_state trx_isolation_level trx_last_foreign_key_error

View File

@ -661,7 +661,21 @@ SELECT NAME, COUNT > 0 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
LIKE 'buffer_page_written_index_leaf';
NAME COUNT > 0
buffer_page_written_index_leaf 1
DROP TABLE t1;
CREATE TABLE t1(id INT PRIMARY KEY, a INT, b CHAR(1), UNIQUE KEY u(a,b))
ENGINE=InnoDB;
SET @start = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
= 'lock_rec_lock_created');
BEGIN;
INSERT INTO t1 VALUES(1,1,'a'),(2,9999,'b'),(3,10000,'c'),(4,4,'d');
DELETE FROM t1 WHERE a = 9999 AND b='b';
COMMIT;
SET @end = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
= 'lock_rec_lock_created');
SELECT @end - @start;
@end - @start
0
DROP TABLE t1;
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
DROP TABLE t1;

View File

@ -192,25 +192,9 @@ ROLLBACK;
disconnect stop_purge;
# Wait for purge to empty the table.
# (This is based on wait_all_purged.inc, but there are 2 transactions
# from the pending ALTER TABLE t1 FORCE.)
let $wait_counter= 300;
while ($wait_counter)
{
--replace_regex /.*History list length ([0-9]+).*/\1/
let $remaining= `SHOW ENGINE INNODB STATUS`;
if ($remaining == 'InnoDB 2')
{
let $wait_counter= 0;
}
if ($wait_counter)
{
real_sleep 0.1;
dec $wait_counter;
}
}
echo $remaining transactions not purged;
let $wait_all_purged=2;
--source include/wait_all_purged.inc
let $wait_all_purged=0;
SET DEBUG_SYNC='now SIGNAL logged';
disconnect ddl;

View File

@ -422,10 +422,27 @@ UNLOCK TABLES;
SELECT NAME, COUNT > 0 FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
LIKE 'buffer_page_written_index_leaf';
DROP TABLE t1;
CREATE TABLE t1(id INT PRIMARY KEY, a INT, b CHAR(1), UNIQUE KEY u(a,b))
ENGINE=InnoDB;
SET @start = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
= 'lock_rec_lock_created');
BEGIN;
INSERT INTO t1 VALUES(1,1,'a'),(2,9999,'b'),(3,10000,'c'),(4,4,'d');
DELETE FROM t1 WHERE a = 9999 AND b='b';
COMMIT;
SET @end = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
= 'lock_rec_lock_created');
SELECT @end - @start;
DROP TABLE t1;
--disable_warnings
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
--enable_warnings
DROP TABLE t1;

View File

@ -2,6 +2,7 @@ call mtr.add_suppression("InnoDB: New log files created");
CREATE TABLE t(i INT) ENGINE INNODB ENCRYPTED=YES;
INSERT INTO t VALUES(1);
# Create full backup , modify table, then create incremental/differential backup
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
INSERT INTO t VALUES(2);
SELECT * FROM t;
i

View File

@ -20,6 +20,7 @@ echo # Create full backup , modify table, then create incremental/differential b
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir;
--enable_result_log
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
INSERT INTO t VALUES(2);
SELECT * FROM t;

View File

@ -1 +1 @@
--innodb --loose-changed_page_bitmaps --innodb-sys-tables
--innodb --loose-changed_page_bitmaps --innodb-sys-tables --innodb-flush-log-at-trx-commit=2

View File

@ -5,9 +5,6 @@ call mtr.add_suppression("InnoDB: If you are installing InnoDB, remember that yo
call mtr.add_suppression("InnoDB: Ignoring tablespace for `test`\\.`t21` because it could not be opened");
call mtr.add_suppression("InnoDB: Cannot open datafile for read-only: ");
call mtr.add_suppression("Table .* in the InnoDB data dictionary has tablespace id .*, but tablespace with that id or name does not exist");
SELECT @@GLOBAL.innodb_flush_log_at_trx_commit;
@@GLOBAL.innodb_flush_log_at_trx_commit
1
CREATE TABLE t1(i INT PRIMARY KEY auto_increment, a int) ENGINE INNODB;
ALTER TABLE t1 FORCE, ALGORITHM=INPLACE;
# Fails during full backup

View File

@ -10,7 +10,6 @@ call mtr.add_suppression("Table .* in the InnoDB data dictionary has tablespace
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
let $incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1;
SELECT @@GLOBAL.innodb_flush_log_at_trx_commit;
CREATE TABLE t1(i INT PRIMARY KEY auto_increment, a int) ENGINE INNODB;
--source ../../suite/innodb/include/no_checkpoint_start.inc
ALTER TABLE t1 FORCE, ALGORITHM=INPLACE;

View File

@ -6,9 +6,16 @@ eval LOAD DATA INFILE '$LOAD_FILE' INTO TABLE t1;
eval LOAD DATA INFILE '$LOAD_FILE' INTO TABLE t1;
SELECT * FROM t1 ORDER BY word LIMIT 10;
#
# Save password row for root
#
create temporary table tmp select * from mysql.user where host="localhost" and user="root";
#
# Test slave with wrong password
#
save_master_pos;
connection slave;
sync_with_master;
@ -24,6 +31,7 @@ connection master;
real_sleep 2;
SET PASSWORD FOR root@"localhost" = PASSWORD('');
# Give slave time to connect (will retry every second)
sleep 2;
CREATE TABLE t3(n INT);
@ -80,4 +88,9 @@ SELECT n FROM t1;
connection master;
DROP TABLE t1;
# resttore old passwords
replace into mysql.user select * from tmp;
drop temporary table tmp;
sync_slave_with_master;

View File

@ -0,0 +1,9 @@
include/master-slave.inc
[connection master]
CREATE TEMPORARY TABLE t (i INT);
CREATE TABLE t AS SELECT * FROM t;
connection slave;
connection master;
DROP TEMPORARY TABLE t;
DROP TABLE t;
include/rpl_end.inc

View File

@ -15,6 +15,7 @@ Aaron
Aaron
Ababa
Ababa
create temporary table tmp select * from mysql.user where host="localhost" and user="root";
connection slave;
STOP SLAVE;
connection master;
@ -65,5 +66,7 @@ n
3456
connection master;
DROP TABLE t1;
replace into mysql.user select * from tmp;
drop temporary table tmp;
connection slave;
include/rpl_end.inc

View File

@ -0,0 +1,11 @@
--source include/master-slave.inc
CREATE TEMPORARY TABLE t (i INT);
CREATE TABLE t AS SELECT * FROM t;
--sync_slave_with_master
# Cleanup
--connection master
DROP TEMPORARY TABLE t;
DROP TABLE t;
--source include/rpl_end.inc

View File

@ -1326,9 +1326,9 @@ READ_ONLY YES
COMMAND_LINE_ARGUMENT NONE
VARIABLE_NAME INNODB_LOCK_SCHEDULE_ALGORITHM
SESSION_VALUE NULL
GLOBAL_VALUE vats
GLOBAL_VALUE fcfs
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE vats
DEFAULT_VALUE fcfs
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE ENUM
VARIABLE_COMMENT The algorithm Innodb uses for deciding which locks to grant next when a lock is released. Possible values are FCFS grant the locks in First-Come-First-Served order; VATS use the Variance-Aware-Transaction-Scheduling algorithm, which uses an Eldest-Transaction-First heuristic.

View File

@ -7755,10 +7755,9 @@ void Field_varstring::sql_type(String &res) const
size_t length;
length= cs->cset->snprintf(cs,(char*) res.ptr(),
res.alloced_length(), "%s(%d)",
res.alloced_length(), "%s(%u)",
(has_charset() ? "varchar" : "varbinary"),
(int) field_length / charset()->mbmaxlen -
MY_TEST(compression_method()));
(uint) char_length());
res.length(length);
if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
has_charset() && (charset()->state & MY_CS_BINSORT))

View File

@ -575,7 +575,7 @@ bool Item_func_json_quote::fix_length_and_dec()
Odd but realistic worst case is when all characters
of the argument turn into '\uXXXX\uXXXX', which is 12.
*/
max_length= args[0]->max_length * 12 + 2;
fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * 12 + 2);
return FALSE;
}

View File

@ -27,6 +27,7 @@
#include "sql_array.h" // Dynamic_array
#include "log_event.h" // Query_log_event
#include "sql_derived.h" // mysql_handle_derived
#include "sql_cte.h"
#include "sql_select.h" // Virtual_tmp_table
#ifdef USE_PRAGMA_IMPLEMENTATION
@ -3287,7 +3288,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
#endif
if (open_tables)
res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
res= check_dependencies_in_with_clauses(m_lex->with_clauses_list) ||
instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
if (likely(!res))
{

View File

@ -1039,6 +1039,12 @@ retry:
if (res->table && (res->table == table->table))
continue;
/* Skip if table is tmp table */
if (check_flag & CHECK_DUP_SKIP_TEMP_TABLE &&
res->table && res->table->s->tmp_table != NO_TMP_TABLE)
{
continue;
}
if (check_flag & CHECK_DUP_FOR_CREATE)
DBUG_RETURN(res);

View File

@ -64,6 +64,7 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
/* Flag bits for unique_table() */
#define CHECK_DUP_ALLOW_DIFFERENT_ALIAS 1
#define CHECK_DUP_FOR_CREATE 2
#define CHECK_DUP_SKIP_TEMP_TABLE 4
uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,

View File

@ -4677,13 +4677,6 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
DBUG_RETURN(error ? NULL : tl->table);
}
TABLE *get_purge_table(THD *thd)
{
/* see above, at most one table can be opened */
DBUG_ASSERT(thd->open_tables == NULL || thd->open_tables->next == NULL);
return thd->open_tables;
}
/** Find an open table in the list of prelocked tabled

View File

@ -4199,7 +4199,8 @@ mysql_execute_command(THD *thd)
TABLE_LIST *duplicate;
if (unlikely((duplicate= unique_table(thd, lex->query_tables,
lex->query_tables->next_global,
CHECK_DUP_FOR_CREATE))))
CHECK_DUP_FOR_CREATE |
CHECK_DUP_SKIP_TEMP_TABLE))))
{
update_non_unique_table_error(lex->query_tables, "CREATE",
duplicate);

View File

@ -5275,8 +5275,18 @@ int TABLE::verify_constraints(bool ignore_failure)
if (((*chk)->expr->val_int() == 0 && !(*chk)->expr->null_value) ||
in_use->is_error())
{
StringBuffer<MAX_FIELD_WIDTH> field_error(system_charset_info);
enum_vcol_info_type vcol_type= (*chk)->get_vcol_type();
DBUG_ASSERT(vcol_type == VCOL_CHECK_TABLE ||
vcol_type == VCOL_CHECK_FIELD);
if (vcol_type == VCOL_CHECK_FIELD)
{
field_error.append(s->table_name.str);
field_error.append(".");
}
field_error.append((*chk)->name.str);
my_error(ER_CONSTRAINT_FAILED,
MYF(ignore_failure ? ME_WARNING : 0), (*chk)->name.str,
MYF(ignore_failure ? ME_WARNING : 0), field_error.c_ptr(),
s->db.str, s->error_table_name());
return ignore_failure ? VIEW_CHECK_SKIP : VIEW_CHECK_ERROR;
}

View File

@ -570,10 +570,9 @@ static
bool
btr_search_update_block_hash_info(btr_search_t* info, buf_block_t* block)
{
ut_ad(!btr_search_own_any(RW_LOCK_S));
ut_ad(!btr_search_own_any(RW_LOCK_X));
ut_ad(rw_lock_own(&block->lock, RW_LOCK_S)
|| rw_lock_own(&block->lock, RW_LOCK_X));
ut_ad(!btr_search_own_any());
ut_ad(rw_lock_own_flagged(&block->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
info->last_hash_succ = FALSE;
@ -648,8 +647,8 @@ btr_search_update_hash_ref(
ut_ad(cursor->flag == BTR_CUR_HASH_FAIL);
ut_ad(rw_lock_own(btr_get_search_latch(cursor->index), RW_LOCK_X));
ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S)
|| rw_lock_own(&(block->lock), RW_LOCK_X));
ut_ad(rw_lock_own_flagged(&block->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(page_align(btr_cur_get_rec(cursor)) == block->frame);
ut_ad(page_is_leaf(block->frame));
assert_block_ahi_valid(block);
@ -887,8 +886,8 @@ btr_search_guess_on_hash(
btr_cur_t cursor2;
btr_pcur_t pcur;
#endif
ut_ad(!ahi_latch || rw_lock_own(ahi_latch, RW_LOCK_S)
|| rw_lock_own(ahi_latch, RW_LOCK_X));
ut_ad(!ahi_latch || rw_lock_own_flagged(
ahi_latch, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
if (!btr_search_enabled) {
return(FALSE);
@ -1109,8 +1108,8 @@ retry:
ut_ad(block->page.buf_fix_count == 0
|| buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH
|| rw_lock_own(&block->lock, RW_LOCK_S)
|| rw_lock_own(&block->lock, RW_LOCK_X));
|| rw_lock_own_flagged(&block->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(page_is_leaf(block->frame));
/* We must not dereference index here, because it could be freed
@ -1307,7 +1306,7 @@ void btr_search_drop_page_hash_when_freed(const page_id_t& page_id)
/* In all our callers, the table handle should
be open, or we should be in the process of
dropping the table (preventing eviction). */
ut_ad(index->table->n_ref_count > 0
ut_ad(index->table->get_ref_count() > 0
|| mutex_own(&dict_sys->mutex));
btr_search_drop_page_hash_index(block);
}
@ -1363,8 +1362,8 @@ btr_search_build_page_hash_index(
ut_a(!dict_index_is_ibuf(index));
ut_ad(page_is_leaf(block->frame));
ut_ad(rw_lock_own(&(block->lock), RW_LOCK_S)
|| rw_lock_own(&(block->lock), RW_LOCK_X));
ut_ad(rw_lock_own_flagged(&block->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
rw_lock_s_lock(ahi_latch);
@ -1531,8 +1530,8 @@ btr_search_info_update_slow(btr_search_t* info, btr_cur_t* cursor)
{
rw_lock_t* ahi_latch = btr_get_search_latch(cursor->index);
ut_ad(!rw_lock_own(ahi_latch, RW_LOCK_S));
ut_ad(!rw_lock_own(ahi_latch, RW_LOCK_X));
ut_ad(!rw_lock_own_flagged(ahi_latch,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
buf_block_t* block = btr_cur_get_block(cursor);

View File

@ -4498,8 +4498,9 @@ loop:
case BUF_GET_IF_IN_POOL_OR_WATCH:
case BUF_PEEK_IF_IN_POOL:
case BUF_EVICT_IF_IN_POOL:
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X));
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(
hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
return(NULL);
}
@ -4840,8 +4841,8 @@ evict_from_pool:
ut_ad(block == fix_block);
ut_ad(fix_block->page.buf_fix_count > 0);
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X));
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(buf_block_get_state(fix_block) == BUF_BLOCK_FILE_PAGE);
@ -5015,8 +5016,8 @@ evict_from_pool:
ut_a(ibuf_count_get(fix_block->page.id) == 0);
#endif
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X));
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
return(fix_block);
}
@ -5688,8 +5689,8 @@ func_exit:
ibuf_mtr_commit(&mtr);
}
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X));
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(!bpage || buf_page_in_file(bpage));
return(bpage);

View File

@ -967,7 +967,7 @@ buf_flush_init_for_writing(
}
}
uint32_t checksum= 0;
uint32_t checksum = BUF_NO_CHECKSUM_MAGIC;
switch (srv_checksum_algorithm_t(srv_checksum_algorithm)) {
case SRV_CHECKSUM_ALGORITHM_INNODB:
@ -990,7 +990,6 @@ buf_flush_init_for_writing(
break;
case SRV_CHECKSUM_ALGORITHM_NONE:
case SRV_CHECKSUM_ALGORITHM_STRICT_NONE:
checksum = BUF_NO_CHECKSUM_MAGIC;
mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM,
checksum);
break;

View File

@ -1629,8 +1629,8 @@ func_exit:
}
/* buf_LRU_block_remove_hashed() releases the hash_lock */
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X)
&& !rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
/* We have just freed a BUF_BLOCK_FILE_PAGE. If b != NULL
then it was a compressed page with an uncompressed frame and
@ -2183,9 +2183,8 @@ buf_LRU_free_one_page(
}
/* buf_LRU_block_remove_hashed() releases hash_lock and block_mutex */
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_X)
&& !rw_lock_own(hash_lock, RW_LOCK_S));
ut_ad(!rw_lock_own_flagged(hash_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(!mutex_own(block_mutex));
}

View File

@ -418,7 +418,7 @@ dict_table_try_drop_aborted(
dict_table_t* table, /*!< in: table, or NULL if it
needs to be looked up again */
table_id_t table_id, /*!< in: table identifier */
ulint ref_count) /*!< in: expected table->n_ref_count */
int32 ref_count) /*!< in: expected table->n_ref_count */
{
trx_t* trx;
@ -1328,7 +1328,7 @@ static
ibool
dict_table_can_be_evicted(
/*======================*/
const dict_table_t* table) /*!< in: table to test */
dict_table_t* table) /*!< in: table to test */
{
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
@ -6872,6 +6872,7 @@ dict_foreign_qualify_index(
}
if (field->col->is_virtual()) {
col_name = "";
for (ulint j = 0; j < table->n_v_def; j++) {
col_name = dict_table_get_v_col_name(table, j);
if (innobase_strcasecmp(field->name,col_name) == 0) {

View File

@ -1479,7 +1479,6 @@ fil_space_create(
rw_lock_create(fil_space_latch_key, &space->latch, SYNC_FSP);
if (space->purpose == FIL_TYPE_TEMPORARY) {
ut_d(space->latch.set_temp_fsp());
/* SysTablespace::open_or_create() would pass
size!=0 to fil_node_create(), so first_time_open
would not hold in fil_node_open_file(), and we

View File

@ -257,9 +257,8 @@ rtr_pcur_getnext_from_path(
rtr_info->tree_savepoints[tree_idx] = mtr_set_savepoint(mtr);
#ifdef UNIV_RTR_DEBUG
ut_ad(!(rw_lock_own(&btr_cur->page_cur.block->lock, RW_LOCK_X)
||
rw_lock_own(&btr_cur->page_cur.block->lock, RW_LOCK_S))
ut_ad(!(rw_lock_own_flagged(&btr_cur->page_cur.block->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S))
|| my_latch_mode == BTR_MODIFY_TREE
|| my_latch_mode == BTR_CONT_MODIFY_TREE
|| !page_is_leaf(buf_block_get_frame(

View File

@ -128,7 +128,7 @@ void destroy_thd(MYSQL_THD thd);
void reset_thd(MYSQL_THD thd);
TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
const char *tb, size_t tblen);
TABLE *get_purge_table(THD *thd);
void close_thread_tables(THD* thd);
#ifdef MYSQL_DYNAMIC_PLUGIN
#define tc_size 400
@ -2969,7 +2969,7 @@ ha_innobase::update_thd(
m_user_thd, thd));
/* The table should have been opened in ha_innobase::open(). */
ut_ad(m_prebuilt->table->n_ref_count > 0);
DBUG_ASSERT(m_prebuilt->table->get_ref_count() > 0);
trx_t* trx = check_trx_exists(thd);
@ -13913,7 +13913,7 @@ ha_innobase::info_low(
m_prebuilt->trx->op_info = "returning various info to MariaDB";
ib_table = m_prebuilt->table;
ut_ad(ib_table->n_ref_count > 0);
DBUG_ASSERT(ib_table->get_ref_count() > 0);
if (flag & HA_STATUS_TIME) {
if (is_analyze || innobase_stats_on_metadata) {
@ -19478,7 +19478,7 @@ static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm
" VATS"
" use the Variance-Aware-Transaction-Scheduling algorithm, which"
" uses an Eldest-Transaction-First heuristic.",
NULL, NULL, INNODB_LOCK_SCHEDULE_ALGORITHM_VATS,
NULL, NULL, INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS,
&innodb_lock_schedule_algorithm_typelib);
static MYSQL_SYSVAR_ULONG(buffer_pool_instances, srv_buf_pool_instances,
@ -20618,63 +20618,151 @@ innobase_index_cond(
return handler_index_cond_check(file);
}
/** Find or open a mysql table for the virtual column template
@param[in] thd mysql thread handle
@param[in,out] table InnoDB table whose virtual column template is to be updated
@return TABLE if successful or NULL */
static TABLE *
innobase_find_mysql_table_for_vc(
/*=============================*/
THD* thd,
dict_table_t* table)
/** Parse the table file name into table name and database name.
@param[in] tbl_name InnoDB table name
@param[out] dbname database name buffer (NAME_LEN + 1 bytes)
@param[out] tblname table name buffer (NAME_LEN + 1 bytes)
@param[out] dbnamelen database name length
@param[out] tblnamelen table name length
@return true if the table name is parsed properly. */
static bool table_name_parse(
const table_name_t& tbl_name,
char* dbname,
char* tblname,
ulint& dbnamelen,
ulint& tblnamelen)
{
TABLE *mysql_table;
bool bg_thread = THDVAR(thd, background_thread);
dbnamelen = dict_get_db_name_len(tbl_name.m_name);
char db_buf[MAX_DATABASE_NAME_LEN + 1];
char tbl_buf[MAX_TABLE_NAME_LEN + 1];
if (bg_thread) {
if ((mysql_table = get_purge_table(thd))) {
return mysql_table;
ut_ad(dbnamelen > 0);
ut_ad(dbnamelen <= MAX_DATABASE_NAME_LEN);
memcpy(db_buf, tbl_name.m_name, dbnamelen);
db_buf[dbnamelen] = 0;
tblnamelen = strlen(tbl_name.m_name + dbnamelen + 1);
memcpy(tbl_buf, tbl_name.m_name + dbnamelen + 1, tblnamelen);
tbl_buf[tblnamelen] = 0;
filename_to_tablename(db_buf, dbname, MAX_DATABASE_NAME_LEN + 1, true);
if (tblnamelen > TEMP_FILE_PREFIX_LENGTH
&& !strncmp(tbl_buf, TEMP_FILE_PREFIX, TEMP_FILE_PREFIX_LENGTH)) {
return false;
}
if (char *is_part = strchr(tbl_buf, '#')) {
*is_part = '\0';
}
filename_to_tablename(tbl_buf, tblname, MAX_TABLE_NAME_LEN + 1, true);
return true;
}
/** Acquire metadata lock and MariaDB table handle for an InnoDB table.
@param[in,out] thd thread handle
@param[in,out] table InnoDB table
@return MariaDB table handle
@retval NULL if the table does not exist, is unaccessible or corrupted. */
static TABLE* innodb_acquire_mdl(THD* thd, dict_table_t* table)
{
char db_buf[NAME_LEN + 1], db_buf1[NAME_LEN + 1];
char tbl_buf[NAME_LEN + 1], tbl_buf1[NAME_LEN + 1];
ulint db_buf_len, db_buf1_len;
ulint tbl_buf_len, tbl_buf1_len;
if (!table_name_parse(table->name, db_buf, tbl_buf,
db_buf_len, tbl_buf_len)) {
return NULL;
}
const table_id_t table_id = table->id;
retry_mdl:
const bool unaccessible = !table->is_readable() || table->corrupted;
table->release();
if (unaccessible) {
return NULL;
}
TABLE* mariadb_table = open_purge_table(thd, db_buf, db_buf_len,
tbl_buf, tbl_buf_len);
table = dict_table_open_on_id(table_id, false, DICT_TABLE_OP_NORMAL);
if (table == NULL) {
/* Table is dropped. */
goto fail;
}
if (!fil_table_accessible(table)) {
release_fail:
table->release();
fail:
if (mariadb_table) {
close_thread_tables(thd);
}
return NULL;
}
if (!table_name_parse(table->name, db_buf1, tbl_buf1,
db_buf1_len, tbl_buf1_len)) {
goto release_fail;
}
if (!mariadb_table) {
} else if (!strcmp(db_buf, db_buf1) && !strcmp(tbl_buf, tbl_buf1)) {
return mariadb_table;
} else {
if (table->vc_templ->mysql_table_query_id == thd_get_query_id(thd)) {
/* Table is renamed. So release MDL for old name and try
to acquire the MDL for new table name. */
close_thread_tables(thd);
}
strcpy(tbl_buf, tbl_buf1);
strcpy(db_buf, db_buf1);
tbl_buf_len = tbl_buf1_len;
db_buf_len = db_buf1_len;
goto retry_mdl;
}
/** Find or open a table handle for the virtual column template
@param[in] thd thread handle
@param[in,out] table InnoDB table whose virtual column template
is to be updated
@return table handle
@retval NULL if the table is dropped, unaccessible or corrupted
for purge thread */
static TABLE* innodb_find_table_for_vc(THD* thd, dict_table_t* table)
{
if (THDVAR(thd, background_thread)) {
/* Purge thread acquires dict_operation_lock while
processing undo log record. Release the dict_operation_lock
before acquiring MDL on the table. */
rw_lock_s_unlock(dict_operation_lock);
return innodb_acquire_mdl(thd, table);
} else {
if (table->vc_templ->mysql_table_query_id
== thd_get_query_id(thd)) {
return table->vc_templ->mysql_table;
}
}
char dbname[MAX_DATABASE_NAME_LEN + 1];
char tbname[MAX_TABLE_NAME_LEN + 1];
char* name = table->name.m_name;
uint dbnamelen = (uint) dict_get_db_name_len(name);
uint tbnamelen = (uint) strlen(name) - dbnamelen - 1;
char t_dbname[MAX_DATABASE_NAME_LEN + 1];
char t_tbname[MAX_TABLE_NAME_LEN + 1];
char db_buf[NAME_LEN + 1];
char tbl_buf[NAME_LEN + 1];
ulint db_buf_len, tbl_buf_len;
strncpy(dbname, name, dbnamelen);
dbname[dbnamelen] = 0;
strncpy(tbname, name + dbnamelen + 1, tbnamelen);
tbname[tbnamelen] =0;
/* For partition table, remove the partition name and use the
"main" table name to build the template */
char* is_part = is_partition(tbname);
if (is_part != NULL) {
*is_part = '\0';
if (!table_name_parse(table->name, db_buf, tbl_buf,
db_buf_len, tbl_buf_len)) {
return NULL;
}
dbnamelen = filename_to_tablename(dbname, t_dbname,
MAX_DATABASE_NAME_LEN + 1);
tbnamelen = filename_to_tablename(tbname, t_tbname,
MAX_TABLE_NAME_LEN + 1);
if (bg_thread) {
return open_purge_table(thd, t_dbname, dbnamelen,
t_tbname, tbnamelen);
}
mysql_table = find_fk_open_table(thd, t_dbname, dbnamelen,
t_tbname, tbnamelen);
TABLE* mysql_table = find_fk_open_table(thd, db_buf, db_buf_len,
tbl_buf, tbl_buf_len);
table->vc_templ->mysql_table = mysql_table;
table->vc_templ->mysql_table_query_id = thd_get_query_id(thd);
@ -20693,7 +20781,7 @@ innobase_init_vc_templ(
table->vc_templ = UT_NEW_NOKEY(dict_vcol_templ_t());
TABLE *mysql_table= innobase_find_mysql_table_for_vc(current_thd, table);
TABLE *mysql_table= innodb_find_table_for_vc(current_thd, table);
ut_ad(mysql_table);
if (!mysql_table) {
@ -20787,15 +20875,16 @@ innobase_get_field_from_update_vector(
Allocate a heap and record for calculating virtual fields
Used mainly for virtual fields in indexes
@param[in] thd MariaDB THD
@param[in] index Index in use
@param[in] thd MariaDB THD
@param[in] index Index in use
@param[out] heap Heap that holds temporary row
@param[in,out] mysql_table MariaDB table
@param[out] rec Pointer to allocated MariaDB record
@param[out] storage Internal storage for blobs etc
@param[in,out] table MariaDB table
@param[out] record Pointer to allocated MariaDB record
@param[out] storage Internal storage for blobs etc
@return FALSE ok
@return TRUE malloc failure
@retval false on success
@retval true on malloc failure or failed to open the maria table
for purge thread.
*/
bool innobase_allocate_row_for_vcol(
@ -20809,7 +20898,12 @@ bool innobase_allocate_row_for_vcol(
TABLE *maria_table;
String *blob_value_storage;
if (!*table)
*table= innobase_find_mysql_table_for_vc(thd, index->table);
*table= innodb_find_table_for_vc(thd, index->table);
/* For purge thread, there is a possiblity that table could have
dropped, corrupted or unaccessible. */
if (!*table)
return true;
maria_table= *table;
if (!*heap && !(*heap= mem_heap_create(srv_page_size)))
{

View File

@ -121,7 +121,15 @@ enum btr_latch_mode {
/** Attempt to purge a secondary index record
while holding the dict_index_t::lock S-latch. */
BTR_PURGE_LEAF_ALREADY_S_LATCHED = BTR_PURGE_LEAF
| BTR_ALREADY_S_LATCHED
| BTR_ALREADY_S_LATCHED,
/** In the case of BTR_MODIFY_TREE, the caller specifies
the intention to delete record only. It is used to optimize
block->lock range.*/
BTR_LATCH_FOR_DELETE = 65536,
/** Attempt to purge a secondary index record in the tree. */
BTR_PURGE_TREE = BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE
};
/** This flag ORed to btr_latch_mode says that we do the search in query
@ -137,10 +145,6 @@ the insert buffer to speed up inserts */
to insert record only. It is used to optimize block->lock range.*/
#define BTR_LATCH_FOR_INSERT 32768U
/** In the case of BTR_MODIFY_TREE, the caller specifies the intention
to delete record only. It is used to optimize block->lock range.*/
#define BTR_LATCH_FOR_DELETE 65536U
/** This flag is for undo insert of rtree. For rtree, we need this flag
to find proper rec to undo insert.*/
#define BTR_RTREE_UNDO_INS 131072U

View File

@ -301,6 +301,21 @@ btr_pcur_commit_specify_mtr(
/*========================*/
btr_pcur_t* pcur, /*!< in: persistent cursor */
mtr_t* mtr); /*!< in: mtr to commit */
/** Commits the mtr and sets the clustered index pcur and secondary index
pcur latch mode to BTR_NO_LATCHES, that is, the cursor becomes detached.
Function btr_pcur_store_position should be used for both cursor before
calling this, if restoration of cursor is wanted later.
@param[in] pcur persistent cursor
@param[in] sec_pcur secondary index persistent cursor
@param[in] mtr mtr to commit */
UNIV_INLINE
void
btr_pcurs_commit_specify_mtr(
btr_pcur_t* pcur,
btr_pcur_t* sec_pcur,
mtr_t* mtr);
/*********************************************************//**
Moves the persistent cursor to the next record in the tree. If no records are
left, the cursor stays 'after last in tree'.

View File

@ -365,6 +365,32 @@ btr_pcur_commit_specify_mtr(
pcur->pos_state = BTR_PCUR_WAS_POSITIONED;
}
/** Commits the mtr and sets the clustered index pcur and secondary index
pcur latch mode to BTR_NO_LATCHES, that is, the cursor becomes detached.
Function btr_pcur_store_position should be used for both cursor before
calling this, if restoration of cursor is wanted later.
@param[in] pcur persistent cursor
@param[in] sec_pcur secondary index persistent cursor
@param[in] mtr mtr to commit */
UNIV_INLINE
void
btr_pcurs_commit_specify_mtr(
btr_pcur_t* pcur,
btr_pcur_t* sec_pcur,
mtr_t* mtr)
{
ut_ad(pcur->pos_state == BTR_PCUR_IS_POSITIONED);
ut_ad(sec_pcur->pos_state == BTR_PCUR_IS_POSITIONED);
pcur->latch_mode = BTR_NO_LATCHES;
sec_pcur->latch_mode = BTR_NO_LATCHES;
mtr_commit(mtr);
pcur->pos_state = BTR_PCUR_WAS_POSITIONED;
sec_pcur->pos_state = BTR_PCUR_WAS_POSITIONED;
}
/**************************************************************//**
Sets the old_rec_buf field to NULL. */
UNIV_INLINE

View File

@ -171,6 +171,9 @@ static inline bool btr_search_own_all(ulint mode);
@retval true if owns any of them
@retval false if owns no search latch */
static inline bool btr_search_own_any(ulint mode);
/** @return whether this thread holds any of the search latches */
static inline bool btr_search_own_any();
#endif /* UNIV_DEBUG */
/** Unlock all search latches from shared mode. */

View File

@ -144,6 +144,18 @@ static inline bool btr_search_own_any(ulint mode)
}
return(false);
}
/** @return whether this thread holds any of the search latches */
static inline bool btr_search_own_any()
{
for (ulint i = btr_ahi_parts; i--; ) {
if (rw_lock_own_flagged(btr_search_latches[i],
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)) {
return true;
}
}
return false;
}
#endif /* UNIV_DEBUG */
/** Get the adaptive hash search index latch for a b-tree.

View File

@ -1401,23 +1401,13 @@ dict_table_is_file_per_table(
&& table->space != fil_system.temp_space;
}
/** Get reference count.
@return current value of n_ref_count */
inline
ulint
dict_table_t::get_ref_count() const
{
ut_ad(mutex_own(&dict_sys->mutex));
return(n_ref_count);
}
/** Acquire the table handle. */
inline
void
dict_table_t::acquire()
{
ut_ad(mutex_own(&dict_sys->mutex));
++n_ref_count;
my_atomic_add32_explicit(&n_ref_count, 1, MY_MEMORY_ORDER_RELAXED);
}
/** Release the table handle.
@ -1426,9 +1416,10 @@ inline
bool
dict_table_t::release()
{
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(n_ref_count > 0);
return !--n_ref_count;
int32 n = my_atomic_add32_explicit(
&n_ref_count, -1, MY_MEMORY_ORDER_RELAXED);
ut_ad(n > 0);
return n == 1;
}
/** Encode the number of columns and number of virtual columns in a

View File

@ -1481,7 +1481,11 @@ struct dict_table_t {
/** Get reference count.
@return current value of n_ref_count */
inline ulint get_ref_count() const;
inline int32 get_ref_count()
{
return my_atomic_load32_explicit(&n_ref_count,
MY_MEMORY_ORDER_RELAXED);
}
/** Acquire the table handle. */
inline void acquire();
@ -1928,13 +1932,11 @@ struct dict_table_t {
It is protected by lock_sys.mutex. */
ulint n_rec_locks;
#ifndef DBUG_ASSERT_EXISTS
private:
#endif
/** Count of how many handles are opened to this table. Dropping of the
table is NOT allowed until this count gets to zero. MySQL does NOT
itself check the number of open handles at DROP. */
ulint n_ref_count;
int32 n_ref_count;
public:
/** List of locks on the table. Protected by lock_sys.mutex. */

View File

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1994, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 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
@ -137,11 +138,8 @@ hash_assert_can_search(
if (table->type == HASH_TABLE_SYNC_MUTEX) {
ut_ad(mutex_own(hash_get_mutex(table, fold)));
} else if (table->type == HASH_TABLE_SYNC_RW_LOCK) {
# ifdef UNIV_DEBUG
rw_lock_t* lock = hash_get_lock(table, fold);
ut_ad(rw_lock_own(lock, RW_LOCK_X)
|| rw_lock_own(lock, RW_LOCK_S));
# endif
ut_ad(rw_lock_own_flagged(hash_get_lock(table, fold),
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
} else {
ut_ad(table->type == HASH_TABLE_SYNC_NONE);
}

View File

@ -789,19 +789,21 @@ const lock_t*
lock_trx_has_sys_table_locks(
/*=========================*/
const trx_t* trx) /*!< in: transaction to check */
MY_ATTRIBUTE((warn_unused_result));
MY_ATTRIBUTE((nonnull, warn_unused_result));
/*******************************************************************//**
Check if the transaction holds an exclusive lock on a record.
@return whether the locks are held */
/** Check if the transaction holds an explicit exclusive lock on a record.
@param[in] trx transaction
@param[in] table table
@param[in] block leaf page
@param[in] heap_no heap number identifying the record
@return whether an explicit X-lock is held */
bool
lock_trx_has_rec_x_lock(
/*====================*/
lock_trx_has_expl_x_lock(
const trx_t* trx, /*!< in: transaction to check */
const dict_table_t* table, /*!< in: table to check */
const buf_block_t* block, /*!< in: buffer block of the record */
ulint heap_no)/*!< in: record heap number */
MY_ATTRIBUTE((warn_unused_result));
MY_ATTRIBUTE((nonnull, warn_unused_result));
#endif /* UNIV_DEBUG */
/**

View File

@ -36,6 +36,7 @@ Created 3/14/1997 Heikki Tuuri
#include "que0types.h"
#include "row0types.h"
#include "ut0vec.h"
#include "row0mysql.h"
/** Create a purge node to a query graph.
@param[in] parent parent node, i.e., a thr node
@ -47,8 +48,7 @@ row_purge_node_create(
mem_heap_t* heap)
MY_ATTRIBUTE((warn_unused_result));
/***********************************************************//**
Determines if it is possible to remove a secondary index entry.
/** Determines if it is possible to remove a secondary index entry.
Removal is possible if the secondary index entry does not refer to any
not delete marked version of a clustered index record where DB_TRX_ID
is newer than the purge view.
@ -61,14 +61,27 @@ inserts a record that the secondary index entry would refer to.
However, in that case, the user transaction would also re-insert the
secondary index entry after purge has removed it and released the leaf
page latch.
@param[in,out] node row purge node
@param[in] index secondary index
@param[in] entry secondary index entry
@param[in,out] sec_pcur secondary index cursor or NULL
if it is called for purge buffering
operation.
@param[in,out] sec_mtr mini-transaction which holds
secondary index entry or NULL if it is
called for purge buffering operation.
@param[in] is_tree true=pessimistic purge,
false=optimistic (leaf-page only)
@return true if the secondary index record can be purged */
bool
row_purge_poss_sec(
/*===============*/
purge_node_t* node, /*!< in/out: row purge node */
dict_index_t* index, /*!< in: secondary index */
const dtuple_t* entry) /*!< in: secondary index entry */
MY_ATTRIBUTE((nonnull, warn_unused_result));
purge_node_t* node,
dict_index_t* index,
const dtuple_t* entry,
btr_pcur_t* sec_pcur=NULL,
mtr_t* sec_mtr=NULL,
bool is_tree=false);
/***************************************************************
Does the purge operation for a single undo log record. This is a high-level
function used in an SQL execution graph.
@ -117,6 +130,10 @@ struct purge_node_t{
ibool done; /* Debug flag */
trx_id_t trx_id; /*!< trx id for this purging record */
/** Virtual column information about opening of MariaDB table.
It resets after processing each undo log record. */
purge_vcol_info_t vcol_info;
#ifdef UNIV_DEBUG
/***********************************************************//**
Validate the persisent cursor. The purge node has two references
@ -125,8 +142,13 @@ struct purge_node_t{
each other if the found_clust flag is set.
@return true if the persistent cursor is consistent with
the ref member.*/
bool validate_pcur();
bool validate_pcur();
#endif
/** Whether purge failed to open the maria table for virtual column
computation.
@return true if the table failed to open. */
bool vcol_op_failed() const { return !vcol_info.validate(); }
};
#endif

View File

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 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,4 +53,93 @@ struct row_log_t;
/* MySQL data types */
struct TABLE;
/** Purge virtual column node information. */
struct purge_vcol_info_t
{
private:
/** Is there a possible need to evaluate virtual columns? */
bool requested;
/** Do we have to evaluate virtual columns (using mariadb_table)? */
bool used;
/** True if it is used for the first time. */
bool first_use;
/** MariaDB table opened for virtual column computation. */
TABLE* mariadb_table;
public:
/** Reset the state. */
void reset()
{
requested = false;
used = false;
first_use = false;
mariadb_table = NULL;
}
/** Validate the virtual column information.
@return true if the mariadb table opened successfully
or doesn't try to calculate virtual column. */
bool validate() const { return !used || mariadb_table; }
/** @return the table handle for evaluating virtual columns */
TABLE* table() const { return mariadb_table; }
/** Set the table handle for evaluating virtual columns.
@param[in] table table handle */
void set_table(TABLE* table)
{
ut_ad(!table || is_first_fetch());
mariadb_table = table;
}
/** Note that virtual column information may be needed. */
void set_requested()
{
ut_ad(!used);
ut_ad(!first_use);
ut_ad(!mariadb_table);
requested = true;
}
/** @return whether the virtual column information may be needed */
bool is_requested() const { return requested; }
/** Note that the virtual column information is needed. */
void set_used()
{
ut_ad(requested);
if (first_use) {
first_use = false;
ut_ad(used);
return;
}
if (!used) {
first_use = used = true;
}
}
/** @return whether the virtual column information is needed */
bool is_used() const
{
ut_ad(!first_use || used);
ut_ad(!used || requested);
ut_ad(used || !mariadb_table);
return used;
}
/** Check whether it fetches mariadb table for the first time.
@return true if first time tries to open mariadb table. */
bool is_first_fetch() const
{
ut_ad(!first_use || used);
ut_ad(!used || requested);
ut_ad(used || !mariadb_table);
return first_use;
}
};
#endif

View File

@ -35,6 +35,7 @@ Created 2/6/1997 Heikki Tuuri
#include "rem0types.h"
#include "mtr0mtr.h"
#include "dict0mem.h"
#include "row0types.h"
// Forward declaration
class ReadView;
@ -54,27 +55,34 @@ row_vers_impl_x_locked(
dict_index_t* index,
const ulint* offsets);
/*****************************************************************//**
Finds out if a version of the record, where the version >= the current
/** Finds out if a version of the record, where the version >= the current
purge view, should have ientry as its secondary index entry. We check
if there is any not delete marked version of the record where the trx
id >= purge view, and the secondary index entry == ientry; exactly in
this case we return TRUE.
@param[in] also_curr TRUE if also rec is included in the versions
to search; otherwise only versions prior
to it are searched
@param[in] rec record in the clustered index; the caller
must have a latch on the page
@param[in] mtr mtr holding the latch on rec; it will
also hold the latch on purge_view
@param[in] index secondary index
@param[in] ientry secondary index entry
@param[in] roll_ptr roll_ptr for the purge record
@param[in] trx_id transaction ID on the purging record
@param[in,out] vcol_info virtual column information for purge thread.
@return TRUE if earlier version should have */
ibool
bool
row_vers_old_has_index_entry(
/*=========================*/
ibool also_curr,/*!< in: TRUE if also rec is included in the
versions to search; otherwise only versions
prior to it are searched */
const rec_t* rec, /*!< in: record in the clustered index; the
caller must have a latch on the page */
mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will
also hold the latch on purge_view */
dict_index_t* index, /*!< in: the secondary index */
const dtuple_t* ientry, /*!< in: the secondary index entry */
roll_ptr_t roll_ptr,/*!< in: roll_ptr for the purge record */
trx_id_t trx_id);/*!< in: transaction ID on the purging record */
bool also_curr,
const rec_t* rec,
mtr_t* mtr,
dict_index_t* index,
const dtuple_t* ientry,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
purge_vcol_info_t* vcol_info=NULL);
/*****************************************************************//**
Constructs the version of a clustered index record which a consistent

View File

@ -279,8 +279,7 @@ rw_lock_s_lock_func(
the threads which have s-locked a latch. This would use some CPU
time. */
ut_ad(!rw_lock_own(lock, RW_LOCK_S)); /* see NOTE above */
ut_ad(!rw_lock_own(lock, RW_LOCK_X));
ut_ad(!rw_lock_own_flagged(lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
if (!rw_lock_s_lock_low(lock, pass, file_name, line)) {

View File

@ -979,8 +979,7 @@ struct latch_t {
UNIV_NOTHROW
:
m_id(id),
m_rw_lock(),
m_temp_fsp() { }
m_rw_lock() {}
/** Destructor */
virtual ~latch_t() UNIV_NOTHROW { }
@ -1014,24 +1013,6 @@ struct latch_t {
return(sync_latch_get_level(m_id));
}
/** @return true if the latch is for a temporary file space*/
bool is_temp_fsp() const
UNIV_NOTHROW
{
return(m_temp_fsp);
}
/** Set the temporary tablespace flag. (For internal temporary
tables, MySQL 5.7 does not always acquire the index->lock. We
need to figure out the context and add some special rules
during the checks.) */
void set_temp_fsp()
UNIV_NOTHROW
{
ut_ad(get_id() == LATCH_ID_FIL_SPACE);
m_temp_fsp = true;
}
/** @return the latch name, m_id must be set */
const char* get_name() const
UNIV_NOTHROW
@ -1047,9 +1028,6 @@ struct latch_t {
/** true if it is a rw-lock. In debug mode, rw_lock_t derives from
this class and sets this variable. */
bool m_rw_lock;
/** true if it is an temporary space latch */
bool m_temp_fsp;
};
/** Subclass this to iterate over a thread's acquired latch levels. */

View File

@ -616,7 +616,7 @@ public:
@retval pointer to trx
*/
trx_t *find(trx_t *caller_trx, trx_id_t trx_id, bool do_ref_count= false)
trx_t *find(trx_t *caller_trx, trx_id_t trx_id, bool do_ref_count)
{
/*
In MariaDB 10.3, purge will reset DB_TRX_ID to 0
@ -624,9 +624,10 @@ public:
always have a nonzero trx_t::id; there the value 0 is
reserved for transactions that did not write or lock
anything yet.
The caller should already have handled trx_id==0 specially.
*/
if (!trx_id)
return NULL;
ut_ad(trx_id);
if (caller_trx && caller_trx->id == trx_id)
{
if (do_ref_count)
@ -1044,13 +1045,13 @@ public:
bool is_registered(trx_t *caller_trx, trx_id_t id)
{
return rw_trx_hash.find(caller_trx, id);
return id && find(caller_trx, id, false);
}
trx_t *find(trx_t *caller_trx, trx_id_t id)
trx_t *find(trx_t *caller_trx, trx_id_t id, bool do_ref_count= true)
{
return rw_trx_hash.find(caller_trx, id, true);
return rw_trx_hash.find(caller_trx, id, do_ref_count);
}

View File

@ -1433,7 +1433,7 @@ lock_rec_create_low(
lock_rec_bitmap_reset(lock);
lock_rec_set_nth_bit(lock, heap_no);
index->table->n_rec_locks++;
ut_ad(index->table->n_ref_count > 0 || !index->table->can_be_evicted);
ut_ad(index->table->get_ref_count() > 0 || !index->table->can_be_evicted);
#ifdef WITH_WSREP
if (c_lock && wsrep_on_trx(trx)
@ -3531,7 +3531,7 @@ lock_table_create(
lock->un_member.tab_lock.table = table;
ut_ad(table->n_ref_count > 0 || !table->can_be_evicted);
ut_ad(table->get_ref_count() > 0 || !table->can_be_evicted);
UT_LIST_ADD_LAST(trx->lock.trx_locks, lock);
@ -4937,8 +4937,12 @@ lock_rec_queue_validate(
/* Unlike the non-debug code, this invariant can only succeed
if the check and assertion are covered by the lock mutex. */
const trx_t *impl_trx = trx_sys.rw_trx_hash.find(current_trx(),
lock_clust_rec_some_has_impl(rec, index, offsets));
const trx_id_t impl_trx_id = lock_clust_rec_some_has_impl(
rec, index, offsets);
const trx_t *impl_trx = impl_trx_id
? trx_sys.find(current_trx(), impl_trx_id, false)
: 0;
ut_ad(lock_mutex_own());
/* impl_trx cannot be committed until lock_mutex_exit()
@ -5547,18 +5551,31 @@ static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx,
#endif /* UNIV_DEBUG */
/*********************************************************************//**
If a transaction has an implicit x-lock on a record, but no explicit x-lock
set on the record, sets one for it. */
/** If an implicit x-lock exists on a record, convert it to an explicit one.
Often, this is called by a transaction that is about to enter a lock wait
due to the lock conflict. Two explicit locks would be created: first the
exclusive lock on behalf of the lock-holder transaction in this function,
and then a wait request on behalf of caller_trx, in the calling function.
This may also be called by the same transaction that is already holding
an implicit exclusive lock on the record. In this case, no explicit lock
should be created.
@param[in,out] caller_trx current transaction
@param[in] block index tree leaf page
@param[in] rec record on the leaf page
@param[in] index the index of the record
@param[in] offsets rec_get_offsets(rec,index)
@return whether caller_trx already holds an exclusive lock on rec */
static
void
bool
lock_rec_convert_impl_to_expl(
/*==========================*/
trx_t* caller_trx,/*!<in/out: trx of current thread */
const buf_block_t* block, /*!< in: buffer block of rec */
const rec_t* rec, /*!< in: user record on page */
dict_index_t* index, /*!< in: index of record */
const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */
trx_t* caller_trx,
const buf_block_t* block,
const rec_t* rec,
dict_index_t* index,
const ulint* offsets)
{
trx_t* trx;
@ -5574,12 +5591,23 @@ lock_rec_convert_impl_to_expl(
trx_id = lock_clust_rec_some_has_impl(rec, index, offsets);
if (trx_id == 0) {
return false;
}
if (UNIV_UNLIKELY(trx_id == caller_trx->id)) {
return true;
}
trx = trx_sys.find(caller_trx, trx_id);
} else {
ut_ad(!dict_index_is_online_ddl(index));
trx = lock_sec_rec_some_has_impl(caller_trx, rec, index,
offsets);
if (trx == caller_trx) {
trx->release_reference();
return true;
}
ut_d(lock_rec_other_trx_holds_expl(caller_trx, trx, rec,
block));
@ -5597,6 +5625,8 @@ lock_rec_convert_impl_to_expl(
lock_rec_convert_impl_to_expl_for_trx(
block, rec, index, trx, heap_no);
}
return false;
}
/*********************************************************************//**
@ -5641,8 +5671,11 @@ lock_clust_rec_modify_check_and_lock(
/* If a transaction has no explicit x-lock set on the record, set one
for it */
lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, index,
offsets);
if (lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec, index,
offsets)) {
/* We already hold an implicit exclusive lock. */
return DB_SUCCESS;
}
err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, index, thr);
@ -5786,10 +5819,11 @@ lock_sec_rec_read_check_and_lock(
database recovery is running. */
if (!page_rec_is_supremum(rec)
&& page_get_max_trx_id(block->frame) >= trx_sys.get_min_trx_id()) {
lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec,
index, offsets);
&& page_get_max_trx_id(block->frame) >= trx_sys.get_min_trx_id()
&& lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec,
index, offsets)) {
/* We already hold an implicit exclusive lock. */
return DB_SUCCESS;
}
err = lock_rec_lock(FALSE, ulint(mode) | gap_mode,
@ -5850,10 +5884,11 @@ lock_clust_rec_read_check_and_lock(
heap_no = page_rec_get_heap_no(rec);
if (heap_no != PAGE_HEAP_NO_SUPREMUM) {
lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec,
index, offsets);
if (heap_no != PAGE_HEAP_NO_SUPREMUM
&& lock_rec_convert_impl_to_expl(thr_get_trx(thr), block, rec,
index, offsets)) {
/* We already hold an implicit exclusive lock. */
return DB_SUCCESS;
}
err = lock_rec_lock(FALSE, ulint(mode) | gap_mode,
@ -6560,12 +6595,14 @@ lock_trx_has_sys_table_locks(
return(strongest_lock);
}
/*******************************************************************//**
Check if the transaction holds an exclusive lock on a record.
@return whether the locks are held */
/** Check if the transaction holds an explicit exclusive lock on a record.
@param[in] trx transaction
@param[in] table table
@param[in] block leaf page
@param[in] heap_no heap number identifying the record
@return whether an explicit X-lock is held */
bool
lock_trx_has_rec_x_lock(
/*====================*/
lock_trx_has_expl_x_lock(
const trx_t* trx, /*!< in: transaction to check */
const dict_table_t* table, /*!< in: table to check */
const buf_block_t* block, /*!< in: buffer block of the record */
@ -6574,11 +6611,9 @@ lock_trx_has_rec_x_lock(
ut_ad(heap_no > PAGE_HEAP_NO_SUPREMUM);
lock_mutex_enter();
ut_a(lock_table_has(trx, table, LOCK_IX)
|| table->is_temporary());
ut_a(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, trx)
|| table->is_temporary());
ut_ad(lock_table_has(trx, table, LOCK_IX));
ut_ad(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, block, heap_no,
trx));
lock_mutex_exit();
return(true);
}

View File

@ -4062,6 +4062,32 @@ next_file:
return(status);
}
/** Check that IO of specific size is possible for the file
opened with FILE_FLAG_NO_BUFFERING.
The requirement is that IO is multiple of the disk sector size.
@param[in] file file handle
@param[in] io_size expected io size
@return true - unbuffered io of requested size is possible, false otherwise.
@note: this function only works correctly with Windows 8 or later,
(GetFileInformationByHandleEx with FileStorageInfo is only supported there).
It will return true on earlier Windows version.
*/
static bool unbuffered_io_possible(HANDLE file, size_t io_size)
{
FILE_STORAGE_INFO info;
if (GetFileInformationByHandleEx(
file, FileStorageInfo, &info, sizeof(info))) {
ULONG sector_size = info.LogicalBytesPerSector;
if (sector_size)
return io_size % sector_size == 0;
}
return true;
}
/** NOTE! Use the corresponding macro os_file_create(), not directly
this function!
Opens an existing file or creates a new.
@ -4237,46 +4263,58 @@ os_file_create_func(
access |= GENERIC_WRITE;
}
do {
for (;;) {
const char *operation;
/* Use default security attributes and no template file. */
file = CreateFile(
(LPCTSTR) name, access, share_mode, NULL,
name, access, share_mode, NULL,
create_flag, attributes, NULL);
if (file == INVALID_HANDLE_VALUE) {
const char* operation;
operation = (create_mode == OS_FILE_CREATE
&& !read_only)
? "create" : "open";
*success = false;
if (on_error_no_exit) {
retry = os_file_handle_error_no_exit(
name, operation, on_error_silent);
} else {
retry = os_file_handle_error(name, operation);
}
} else {
retry = false;
*success = true;
if (srv_use_native_aio && ((attributes & FILE_FLAG_OVERLAPPED) != 0)) {
/* Bind the file handle to completion port. Completion port
might not be created yet, in some stages of backup, but
must always be there for the server.*/
HANDLE port =(type == OS_LOG_FILE)?
log_completion_port : data_completion_port;
ut_a(port || srv_operation != SRV_OPERATION_NORMAL);
if (port) {
ut_a(CreateIoCompletionPort(file, port, 0, 0));
}
}
/* If FILE_FLAG_NO_BUFFERING was set, check if this can work at all,
for expected IO sizes. Reopen without the unbuffered flag, if it is won't work*/
if ((file != INVALID_HANDLE_VALUE)
&& (attributes & FILE_FLAG_NO_BUFFERING)
&& (type == OS_LOG_FILE)
&& !unbuffered_io_possible(file, OS_FILE_LOG_BLOCK_SIZE)) {
ut_a(CloseHandle(file));
attributes &= ~FILE_FLAG_NO_BUFFERING;
create_flag = OPEN_ALWAYS;
continue;
}
} while (retry);
*success = (file != INVALID_HANDLE_VALUE);
if (*success) {
break;
}
operation = (create_mode == OS_FILE_CREATE && !read_only) ?
"create" : "open";
if (on_error_no_exit) {
retry = os_file_handle_error_no_exit(
name, operation, on_error_silent);
}
else {
retry = os_file_handle_error(name, operation);
}
if (!retry) {
break;
}
}
if (*success && srv_use_native_aio && (attributes & FILE_FLAG_OVERLAPPED)) {
/* Bind the file handle to completion port. Completion port
might not be created yet, in some stages of backup, but
must always be there for the server.*/
HANDLE port = (type == OS_LOG_FILE) ?
log_completion_port : data_completion_port;
ut_a(port || srv_operation != SRV_OPERATION_NORMAL);
if (port) {
ut_a(CreateIoCompletionPort(file, port, 0, 0));
}
}
return(file);
}

View File

@ -3863,7 +3863,7 @@ row_ins_step(
/* No-rollback tables should only be written to by a
single thread at a time, but there can be multiple
concurrent readers. We must hold an open table handle. */
DBUG_ASSERT(node->table->n_ref_count > 0);
DBUG_ASSERT(node->table->get_ref_count() > 0);
DBUG_ASSERT(node->ins_type == INS_DIRECT);
/* No-rollback tables can consist only of a single index. */
DBUG_ASSERT(UT_LIST_GET_LEN(node->entry_list) == 1);

View File

@ -329,8 +329,8 @@ row_log_online_op(
ut_ad(dtuple_validate(tuple));
ut_ad(dtuple_get_n_fields(tuple) == dict_index_get_n_fields(index));
ut_ad(rw_lock_own(dict_index_get_lock(index), RW_LOCK_S)
|| rw_lock_own(dict_index_get_lock(index), RW_LOCK_X));
ut_ad(rw_lock_own_flagged(&index->lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
if (index->is_corrupted()) {
return;

View File

@ -136,7 +136,8 @@ row_purge_remove_clust_if_poss_low(
ulint offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs_init(offsets_);
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S));
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)
|| node->vcol_info.is_used());
index = dict_table_get_first_index(node->table);
@ -230,8 +231,55 @@ row_purge_remove_clust_if_poss(
return(false);
}
/***********************************************************//**
Determines if it is possible to remove a secondary index entry.
/** Tries to store secondary index cursor before openin mysql table for
virtual index condition computation.
@param[in,out] node row purge node
@param[in] index secondary index
@param[in,out] sec_pcur secondary index cursor
@param[in,out] sec_mtr mini-transaction which holds
secondary index entry */
static void row_purge_store_vsec_cur(
purge_node_t* node,
dict_index_t* index,
btr_pcur_t* sec_pcur,
mtr_t* sec_mtr)
{
row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, sec_mtr);
if (!node->found_clust) {
return;
}
node->vcol_info.set_requested();
btr_pcur_store_position(sec_pcur, sec_mtr);
btr_pcurs_commit_specify_mtr(&node->pcur, sec_pcur, sec_mtr);
}
/** Tries to restore secondary index cursor after opening the mysql table
@param[in,out] node row purge node
@param[in] index secondary index
@param[in,out] sec_mtr mini-transaction which holds secondary index entry
@param[in] is_tree true=pessimistic purge,
false=optimistic (leaf-page only)
@return false in case of restore failure. */
static bool row_purge_restore_vsec_cur(
purge_node_t* node,
dict_index_t* index,
btr_pcur_t* sec_pcur,
mtr_t* sec_mtr,
bool is_tree)
{
sec_mtr->start();
index->set_modified(*sec_mtr);
return btr_pcur_restore_position(
is_tree ? BTR_PURGE_TREE : BTR_PURGE_LEAF,
sec_pcur, sec_mtr);
}
/** Determines if it is possible to remove a secondary index entry.
Removal is possible if the secondary index entry does not refer to any
not delete marked version of a clustered index record where DB_TRX_ID
is newer than the purge view.
@ -244,34 +292,95 @@ inserts a record that the secondary index entry would refer to.
However, in that case, the user transaction would also re-insert the
secondary index entry after purge has removed it and released the leaf
page latch.
@param[in,out] node row purge node
@param[in] index secondary index
@param[in] entry secondary index entry
@param[in,out] sec_pcur secondary index cursor or NULL
if it is called for purge buffering
operation.
@param[in,out] sec_mtr mini-transaction which holds
secondary index entry or NULL if it is
called for purge buffering operation.
@param[in] is_tree true=pessimistic purge,
false=optimistic (leaf-page only)
@return true if the secondary index record can be purged */
bool
row_purge_poss_sec(
/*===============*/
purge_node_t* node, /*!< in/out: row purge node */
dict_index_t* index, /*!< in: secondary index */
const dtuple_t* entry) /*!< in: secondary index entry */
purge_node_t* node,
dict_index_t* index,
const dtuple_t* entry,
btr_pcur_t* sec_pcur,
mtr_t* sec_mtr,
bool is_tree)
{
bool can_delete;
mtr_t mtr;
ut_ad(!dict_index_is_clust(index));
const bool store_cur = sec_mtr && !node->vcol_info.is_used()
&& dict_index_has_virtual(index);
if (store_cur) {
row_purge_store_vsec_cur(node, index, sec_pcur, sec_mtr);
ut_ad(sec_mtr->has_committed()
== node->vcol_info.is_requested());
/* The PRIMARY KEY value was not found in the clustered
index. The secondary index record found. We can purge
the secondary index record. */
if (!node->vcol_info.is_requested()) {
ut_ad(!node->found_clust);
return true;
}
}
retry_purge_sec:
mtr_start(&mtr);
can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr)
|| !row_vers_old_has_index_entry(TRUE,
|| !row_vers_old_has_index_entry(true,
btr_pcur_get_rec(&node->pcur),
&mtr, index, entry,
node->roll_ptr, node->trx_id);
node->roll_ptr, node->trx_id,
&node->vcol_info);
if (node->vcol_info.is_first_fetch()) {
ut_ad(store_cur);
const TABLE* t= node->vcol_info.table();
DBUG_LOG("purge", "retry " << t
<< (is_tree ? " tree" : " leaf")
<< index->name << "," << index->table->name
<< ": " << rec_printer(entry).str());
ut_ad(mtr.has_committed());
if (t) {
node->vcol_info.set_used();
goto retry_purge_sec;
}
node->table = NULL;
sec_pcur = NULL;
return false;
}
/* Persistent cursor is closed if reposition fails. */
if (node->found_clust) {
btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
} else {
mtr_commit(&mtr);
mtr.commit();
}
return(can_delete);
ut_ad(mtr.has_committed());
if (store_cur && !row_purge_restore_vsec_cur(
node, index, sec_pcur, sec_mtr, is_tree)) {
return false;
}
return can_delete;
}
/***************************************************************
@ -287,7 +396,6 @@ row_purge_remove_sec_if_poss_tree(
const dtuple_t* entry) /*!< in: index entry */
{
btr_pcur_t pcur;
btr_cur_t* btr_cur;
ibool success = TRUE;
dberr_t err;
mtr_t mtr;
@ -348,16 +456,16 @@ row_purge_remove_sec_if_poss_tree(
ut_error;
}
btr_cur = btr_pcur_get_btr_cur(&pcur);
/* We should remove the index record if no later version of the row,
which cannot be purged yet, requires its existence. If some requires,
we should do nothing. */
if (row_purge_poss_sec(node, index, entry)) {
if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, true)) {
/* Remove the index record, which should have been
marked for deletion. */
if (!rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
if (!rec_get_deleted_flag(btr_cur_get_rec(
btr_pcur_get_btr_cur(&pcur)),
dict_table_is_comp(index->table))) {
ib::error()
<< "tried to purge non-delete-marked record"
@ -365,15 +473,18 @@ row_purge_remove_sec_if_poss_tree(
<< " of table " << index->table->name
<< ": tuple: " << *entry
<< ", record: " << rec_index_print(
btr_cur_get_rec(btr_cur), index);
btr_cur_get_rec(
btr_pcur_get_btr_cur(&pcur)),
index);
ut_ad(0);
goto func_exit;
}
btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
false, &mtr);
btr_cur_pessimistic_delete(&err, FALSE,
btr_pcur_get_btr_cur(&pcur),
0, false, &mtr);
switch (UNIV_EXPECT(err, DB_SUCCESS)) {
case DB_SUCCESS:
break;
@ -385,6 +496,13 @@ row_purge_remove_sec_if_poss_tree(
}
}
if (node->vcol_op_failed()) {
ut_ad(mtr.has_committed());
ut_ad(!pcur.old_rec_buf);
ut_ad(pcur.pos_state == BTR_PCUR_NOT_POSITIONED);
return false;
}
func_exit:
btr_pcur_close(&pcur);
func_exit_no_pcur:
@ -445,8 +563,10 @@ row_purge_remove_sec_if_poss_leaf(
index->is_committed(). */
ut_ad(!dict_index_is_online_ddl(index));
/* Change buffering is disabled for spatial index. */
mode = dict_index_is_spatial(index)
/* Change buffering is disabled for spatial index and
virtual index. */
mode = (dict_index_is_spatial(index)
|| dict_index_has_virtual(index))
? BTR_MODIFY_LEAF
: BTR_PURGE_LEAF;
}
@ -474,7 +594,7 @@ row_purge_remove_sec_if_poss_leaf(
case ROW_FOUND:
/* Before attempting to purge a record, check
if it is safe to do so. */
if (row_purge_poss_sec(node, index, entry)) {
if (row_purge_poss_sec(node, index, entry, &pcur, &mtr, false)) {
btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
/* Only delete-marked records should be purged. */
@ -540,6 +660,12 @@ row_purge_remove_sec_if_poss_leaf(
success = false;
}
}
if (node->vcol_op_failed()) {
btr_pcur_close(&pcur);
return false;
}
/* (The index entry is still needed,
or the deletion succeeded) */
/* fall through */
@ -586,6 +712,10 @@ row_purge_remove_sec_if_poss(
return;
}
retry:
if (node->vcol_op_failed()) {
return;
}
success = row_purge_remove_sec_if_poss_tree(node, index, entry);
/* The delete operation may fail if we have little
file space left: TODO: easiest to crash the database
@ -652,6 +782,12 @@ row_purge_del_mark(
node->row, NULL, node->index,
heap, ROW_BUILD_FOR_PURGE);
row_purge_remove_sec_if_poss(node, node->index, entry);
if (node->vcol_op_failed()) {
mem_heap_free(heap);
return false;
}
mem_heap_empty(heap);
}
@ -667,11 +803,10 @@ row_purge_del_mark(
whose old history can no longer be observed.
@param[in,out] node purge node
@param[in,out] mtr mini-transaction (will be started and committed) */
static
void
row_purge_reset_trx_id(purge_node_t* node, mtr_t* mtr)
static void row_purge_reset_trx_id(purge_node_t* node, mtr_t* mtr)
{
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S));
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)
|| node->vcol_info.is_used());
/* Reset DB_TRX_ID, DB_ROLL_PTR for old records. */
mtr->start();
@ -746,7 +881,8 @@ row_purge_upd_exist_or_extern_func(
{
mem_heap_t* heap;
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S));
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)
|| node->vcol_info.is_used());
ut_ad(!node->table->skip_alter_undo);
if (node->rec_type == TRX_UNDO_UPD_DEL_REC
@ -1107,10 +1243,15 @@ row_purge(
bool purged = row_purge_record(
node, undo_rec, thr, updated_extern);
rw_lock_s_unlock(dict_operation_lock);
if (!node->vcol_info.is_used()) {
rw_lock_s_unlock(dict_operation_lock);
}
ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_S));
if (purged
|| srv_shutdown_state != SRV_SHUTDOWN_NONE) {
|| srv_shutdown_state != SRV_SHUTDOWN_NONE
|| node->vcol_op_failed()) {
return;
}
@ -1142,6 +1283,8 @@ row_purge_end(
node->done = TRUE;
node->vcol_info.reset();
ut_a(thr->run_node != NULL);
mem_heap_empty(node->heap);
@ -1189,6 +1332,7 @@ row_purge_step(
row_purge_end(thr);
} else {
thr->run_node = node;
node->vcol_info.reset();
}
} else {
row_purge_end(thr);

View File

@ -1100,8 +1100,8 @@ sel_set_rtr_rec_lock(
rw_lock_x_lock(&(match->block.lock));
retry:
cur_block = btr_pcur_get_block(pcur);
ut_ad(rw_lock_own(&(match->block.lock), RW_LOCK_X)
|| rw_lock_own(&(match->block.lock), RW_LOCK_S));
ut_ad(rw_lock_own_flagged(&match->block.lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
ut_ad(page_is_leaf(buf_block_get_frame(cur_block)));
err = lock_sec_rec_read_check_and_lock(

View File

@ -1757,6 +1757,8 @@ row_truncate_table_for_mysql(
trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
}
DEBUG_SYNC_C("row_trunc_before_dict_lock");
/* Step-3: Validate ownership of needed locks (Exclusive lock).
Ownership will also ensure there is no active SQL queries, INSERT,
SELECT, .....*/

View File

@ -222,8 +222,8 @@ row_undo_mod_clust(
ut_ad(thr_get_trx(thr) == node->trx);
ut_ad(node->trx->dict_operation_lock_mode);
ut_ad(node->trx->in_rollback);
ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_S)
|| rw_lock_own(dict_operation_lock, RW_LOCK_X));
ut_ad(rw_lock_own_flagged(dict_operation_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
log_free_check();
pcur = &node->pcur;
@ -543,7 +543,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
clustered index entry, because there is no MVCC or purge. */
if (node->table->is_temporary()
|| row_vers_old_has_index_entry(
FALSE, btr_pcur_get_rec(&node->pcur),
false, btr_pcur_get_rec(&node->pcur),
&mtr_vers, index, entry, 0, 0)) {
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
btr_cur, TRUE, thr, &mtr);

View File

@ -3093,9 +3093,7 @@ row_upd_clust_step(
ulint mode;
DEBUG_SYNC_C_IF_THD(
thr_get_trx(thr)->mysql_thd,
"innodb_row_upd_clust_step_enter");
DEBUG_SYNC_C_IF_THD(trx->mysql_thd, "innodb_row_upd_clust_step_enter");
if (dict_index_is_online_ddl(index)) {
ut_ad(node->table->id != DICT_INDEXES_ID);
@ -3157,10 +3155,11 @@ row_upd_clust_step(
}
}
ut_ad(index->table->no_rollback()
|| lock_trx_has_rec_x_lock(thr_get_trx(thr), index->table,
btr_pcur_get_block(pcur),
page_rec_get_heap_no(rec)));
ut_ad(index->table->no_rollback() || index->table->is_temporary()
|| row_get_rec_trx_id(rec, index, offsets) == trx->id
|| lock_trx_has_expl_x_lock(trx, index->table,
btr_pcur_get_block(pcur),
page_rec_get_heap_no(rec)));
/* NOTE: the following function calls will also commit mtr */

View File

@ -126,14 +126,22 @@ row_vers_impl_x_locked_low(
DBUG_RETURN(0);
}
trx_t* trx = trx_sys.find(caller_trx, trx_id);
trx_t* trx;
if (trx == 0) {
/* The transaction that modified or inserted clust_rec is no
longer active, or it is corrupt: no implicit lock on rec */
lock_check_trx_id_sanity(trx_id, clust_rec, clust_index, clust_offsets);
mem_heap_free(heap);
DBUG_RETURN(0);
if (trx_id == caller_trx->id) {
trx = caller_trx;
trx->reference();
} else {
trx = trx_sys.find(caller_trx, trx_id);
if (trx == 0) {
/* The transaction that modified or inserted
clust_rec is no longer active, or it is
corrupt: no implicit lock on rec */
lock_check_trx_id_sanity(trx_id, clust_rec,
clust_index, clust_offsets);
mem_heap_free(heap);
DBUG_RETURN(0);
}
}
comp = page_rec_is_comp(rec);
@ -424,14 +432,16 @@ row_vers_impl_x_locked(
@param[in,out] row the cluster index row in dtuple form
@param[in] clust_index clustered index
@param[in] index the secondary index
@param[in] heap heap used to build virtual dtuple */
@param[in] heap heap used to build virtual dtuple
@param[in,out] vcol_info virtual column information. */
static
void
row_vers_build_clust_v_col(
dtuple_t* row,
dict_index_t* clust_index,
dict_index_t* index,
mem_heap_t* heap)
dtuple_t* row,
dict_index_t* clust_index,
dict_index_t* index,
mem_heap_t* heap,
purge_vcol_info_t* vcol_info)
{
mem_heap_t* local_heap = NULL;
VCOL_STORAGE *vcol_storage= NULL;
@ -441,12 +451,22 @@ row_vers_build_clust_v_col(
ut_ad(dict_index_has_virtual(index));
if (vcol_info != NULL) {
vcol_info->set_used();
maria_table = vcol_info->table();
}
innobase_allocate_row_for_vcol(thd, index,
&local_heap,
&maria_table,
&record,
&vcol_storage);
if (vcol_info && !vcol_info->table()) {
vcol_info->set_table(maria_table);
goto func_exit;
}
for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
const dict_field_t* ind_field = dict_index_get_nth_field(
index, i);
@ -464,6 +484,7 @@ row_vers_build_clust_v_col(
}
}
func_exit:
if (local_heap) {
if (vcol_storage)
innobase_free_row_for_vcol(vcol_storage);
@ -485,16 +506,16 @@ row_vers_build_clust_v_col(
static
void
row_vers_build_cur_vrow_low(
bool in_purge,
const rec_t* rec,
dict_index_t* clust_index,
ulint* clust_offsets,
dict_index_t* index,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
mem_heap_t* v_heap,
const dtuple_t**vrow,
mtr_t* mtr)
bool in_purge,
const rec_t* rec,
dict_index_t* clust_index,
ulint* clust_offsets,
dict_index_t* index,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
mem_heap_t* v_heap,
const dtuple_t** vrow,
mtr_t* mtr)
{
const rec_t* version;
rec_t* prev_version;
@ -771,20 +792,22 @@ func_exit:
@param[in,out] heap heap memory
@param[in,out] v_heap heap memory to keep virtual colum dtuple
@param[in] mtr mtr holding the latch on rec
@param[in,out] vcol_info virtual column information for purge thread
@return dtuple contains virtual column data */
static
const dtuple_t*
row_vers_build_cur_vrow(
bool in_purge,
const rec_t* rec,
dict_index_t* clust_index,
ulint** clust_offsets,
dict_index_t* index,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
mem_heap_t* heap,
mem_heap_t* v_heap,
mtr_t* mtr)
bool in_purge,
const rec_t* rec,
dict_index_t* clust_index,
ulint** clust_offsets,
dict_index_t* index,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
mem_heap_t* heap,
mem_heap_t* v_heap,
mtr_t* mtr,
purge_vcol_info_t* vcol_info)
{
const dtuple_t* cur_vrow = NULL;
@ -804,8 +827,17 @@ row_vers_build_cur_vrow(
rec, *clust_offsets,
NULL, NULL, NULL, NULL, heap);
if (vcol_info && !vcol_info->is_used()) {
mtr->commit();
}
row_vers_build_clust_v_col(
row, clust_index, index, heap);
row, clust_index, index, heap, vcol_info);
if (vcol_info != NULL && vcol_info->is_first_fetch()) {
return NULL;
}
cur_vrow = dtuple_copy(row, v_heap);
dtuple_dup_v_fld(cur_vrow, v_heap);
} else {
@ -820,27 +852,34 @@ row_vers_build_cur_vrow(
return(cur_vrow);
}
/*****************************************************************//**
Finds out if a version of the record, where the version >= the current
/** Finds out if a version of the record, where the version >= the current
purge view, should have ientry as its secondary index entry. We check
if there is any not delete marked version of the record where the trx
id >= purge view, and the secondary index entry and ientry are identified in
the alphabetical ordering; exactly in this case we return TRUE.
id >= purge view, and the secondary index entry == ientry; exactly in
this case we return TRUE.
@param[in] also_curr TRUE if also rec is included in the versions
to search; otherwise only versions prior
to it are searched
@param[in] rec record in the clustered index; the caller
must have a latch on the page
@param[in] mtr mtr holding the latch on rec; it will
also hold the latch on purge_view
@param[in] index secondary index
@param[in] ientry secondary index entry
@param[in] roll_ptr roll_ptr for the purge record
@param[in] trx_id transaction ID on the purging record
@param[in,out] vcol_info virtual column information for purge thread.
@return TRUE if earlier version should have */
ibool
bool
row_vers_old_has_index_entry(
/*=========================*/
ibool also_curr,/*!< in: TRUE if also rec is included in the
versions to search; otherwise only versions
prior to it are searched */
const rec_t* rec, /*!< in: record in the clustered index; the
caller must have a latch on the page */
mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will
also hold the latch on purge_view */
dict_index_t* index, /*!< in: the secondary index */
const dtuple_t* ientry, /*!< in: the secondary index entry */
roll_ptr_t roll_ptr,/*!< in: roll_ptr for the purge record */
trx_id_t trx_id) /*!< in: transaction ID on the purging record */
bool also_curr,
const rec_t* rec,
mtr_t* mtr,
dict_index_t* index,
const dtuple_t* ientry,
roll_ptr_t roll_ptr,
trx_id_t trx_id,
purge_vcol_info_t* vcol_info)
{
const rec_t* version;
rec_t* prev_version;
@ -908,8 +947,18 @@ row_vers_old_has_index_entry(
columns need to be computed */
if (trx_undo_roll_ptr_is_insert(t_roll_ptr)
|| dbug_v_purge) {
if (vcol_info && !vcol_info->is_used()) {
mtr->commit();
}
row_vers_build_clust_v_col(
row, clust_index, index, heap);
row, clust_index, index, heap,
vcol_info);
if (vcol_info && vcol_info->is_first_fetch()) {
goto unsafe_to_purge;
}
entry = row_build_index_entry(
row, ext, index, heap);
@ -974,7 +1023,7 @@ safe_to_purge:
if (v_heap) {
mem_heap_free(v_heap);
}
return(TRUE);
return true;
}
}
} else if (dict_index_has_virtual(index)) {
@ -982,9 +1031,14 @@ safe_to_purge:
deleted, but the previous version of it might not. We will
need to get the virtual column data from undo record
associated with current cluster index */
cur_vrow = row_vers_build_cur_vrow(
also_curr, rec, clust_index, &clust_offsets,
index, roll_ptr, trx_id, heap, v_heap, mtr);
index, roll_ptr, trx_id, heap, v_heap, mtr, vcol_info);
if (vcol_info && vcol_info->is_first_fetch()) {
goto unsafe_to_purge;
}
}
version = rec;
@ -1003,14 +1057,14 @@ safe_to_purge:
if (!prev_version) {
/* Versions end here */
unsafe_to_purge:
mem_heap_free(heap);
if (v_heap) {
mem_heap_free(v_heap);
}
return(FALSE);
return false;
}
clust_offsets = rec_get_offsets(prev_version, clust_index,

View File

@ -35,6 +35,7 @@ Created 2012-08-21 Sunny Bains
#include "ut0new.h"
#include "srv0start.h"
#include "fil0fil.h"
#include <map>
#include <vector>
@ -910,19 +911,10 @@ LatchDebug::check_order(
case SYNC_TREE_NODE:
{
const latch_t* fsp_latch;
fsp_latch = find(latches, SYNC_FSP);
ut_a((fsp_latch != NULL
&& fsp_latch->is_temp_fsp())
|| find(latches, SYNC_INDEX_TREE) != 0
|| find(latches, SYNC_DICT_OPERATION)
|| basic_check(latches,
level, SYNC_TREE_NODE - 1));
}
ut_a(find(latches, SYNC_FSP) == &fil_system.temp_space->latch
|| find(latches, SYNC_INDEX_TREE)
|| find(latches, SYNC_DICT_OPERATION)
|| basic_check(latches, level, SYNC_TREE_NODE - 1));
break;
case SYNC_TREE_NODE_NEW:

View File

@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@ -1092,12 +1092,12 @@ rw_lock_own_flagged(
const rw_lock_debug_t* info = *it;
ut_ad(os_thread_eq(info->thread_id, os_thread_get_curr_id()));
if (info->pass != 0) {
if (info->pass) {
continue;
}
ut_ad(os_thread_eq(info->thread_id, os_thread_get_curr_id()));
switch (info->lock_type) {
case RW_LOCK_S:

View File

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, 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
@ -1468,26 +1468,20 @@ cache_select_table(
trx_i_s_cache_t* cache, /*!< in: whole cache */
enum i_s_table table) /*!< in: which table */
{
i_s_table_cache_t* table_cache;
ut_ad(rw_lock_own(cache->rw_lock, RW_LOCK_S)
|| rw_lock_own(cache->rw_lock, RW_LOCK_X));
ut_ad(rw_lock_own_flagged(cache->rw_lock,
RW_LOCK_FLAG_X | RW_LOCK_FLAG_S));
switch (table) {
case I_S_INNODB_TRX:
table_cache = &cache->innodb_trx;
break;
return &cache->innodb_trx;
case I_S_INNODB_LOCKS:
table_cache = &cache->innodb_locks;
break;
return &cache->innodb_locks;
case I_S_INNODB_LOCK_WAITS:
table_cache = &cache->innodb_lock_waits;
break;
default:
ut_error;
return &cache->innodb_lock_waits;
}
return(table_cache);
ut_error;
return NULL;
}
/*******************************************************************//**

View File

@ -761,7 +761,7 @@ trx_lists_init_at_db_start()
for (undo = UT_LIST_GET_FIRST(rseg->undo_list);
undo != NULL;
undo = UT_LIST_GET_NEXT(undo_list, undo)) {
trx_t *trx = trx_sys.rw_trx_hash.find(0, undo->trx_id);
trx_t *trx = trx_sys.find(0, undo->trx_id, false);
if (!trx) {
trx_resurrect(undo, rseg, start_time,
&rows_to_undo, false);

View File

@ -358,7 +358,7 @@ ut_print_buf_hex(
for (data = static_cast<const byte*>(buf), i = 0; i < len; i++) {
byte b = *data++;
o << hexdigit[(int) b >> 16] << hexdigit[b & 15];
o << hexdigit[int(b) >> 4] << hexdigit[b & 15];
}
o << ")";

View File

@ -412,9 +412,18 @@ delimiter //
create procedure mysql.spider_plugin_installer()
begin
set @win_plugin := IF(@@version_compile_os like 'Win%', 1, 0);
set @have_spider_i_s_plugin := 0;
select @have_spider_i_s_plugin := 1 from INFORMATION_SCHEMA.plugins where PLUGIN_NAME = 'SPIDER';
set @have_spider_plugin := 0;
select @have_spider_plugin := 1 from INFORMATION_SCHEMA.plugins where PLUGIN_NAME = 'SPIDER';
if @have_spider_plugin = 0 then
select @have_spider_plugin := 1 from mysql.plugin where name = 'spider';
if @have_spider_i_s_plugin = 0 then
if @have_spider_plugin = 1 then
-- spider plugin is present in mysql.plugin but not in
-- information_schema.plugins. Remove spider plugin entry
-- in mysql.plugin first.
delete from mysql.plugin where name = 'spider';
end if;
-- Install spider plugin
if @win_plugin = 0 then
install plugin spider soname 'ha_spider.so';
else
@ -423,7 +432,16 @@ begin
end if;
set @have_spider_i_s_alloc_mem_plugin := 0;
select @have_spider_i_s_alloc_mem_plugin := 1 from INFORMATION_SCHEMA.plugins where PLUGIN_NAME = 'SPIDER_ALLOC_MEM';
if @have_spider_i_s_alloc_mem_plugin = 0 then
set @have_spider_alloc_mem_plugin := 0;
select @have_spider_alloc_mem_plugin := 1 from mysql.plugin where name = 'spider_alloc_mem';
if @have_spider_i_s_alloc_mem_plugin = 0 then
if @have_spider_alloc_mem_plugin = 1 then
-- spider_alloc_mem plugin is present in mysql.plugin but not in
-- information_schema.plugins. Remove spider_alloc_mem plugin entry
-- in mysql.plugin first.
delete from mysql.plugin where name = 'spider_alloc_mem';
end if;
-- Install spider_alloc_mem plugin
if @win_plugin = 0 then
install plugin spider_alloc_mem soname 'ha_spider.so';
else