mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
Merge 10.6 into 10.7
This commit is contained in:
@@ -343,7 +343,7 @@ check_server_version(unsigned long version_number,
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
Receive options important for XtraBackup from MySQL server.
|
||||
Receive options important for XtraBackup from server.
|
||||
@return true on success. */
|
||||
bool get_mysql_vars(MYSQL *connection)
|
||||
{
|
||||
@@ -1843,8 +1843,8 @@ static std::string make_local_paths(const char *data_file_path)
|
||||
bool write_backup_config_file()
|
||||
{
|
||||
int rc= backup_file_printf("backup-my.cnf",
|
||||
"# This MySQL options file was generated by innobackupex.\n\n"
|
||||
"# The MySQL server\n"
|
||||
"# This options file was generated by innobackupex.\n\n"
|
||||
"# The server\n"
|
||||
"[mysqld]\n"
|
||||
"innodb_checksum_algorithm=%s\n"
|
||||
"innodb_data_file_path=%s\n"
|
||||
@@ -1929,7 +1929,7 @@ flush_changed_page_bitmaps()
|
||||
|
||||
|
||||
/*********************************************************************//**
|
||||
Deallocate memory, disconnect from MySQL server, etc.
|
||||
Deallocate memory, disconnect from server, etc.
|
||||
@return true on success. */
|
||||
void
|
||||
backup_cleanup()
|
||||
|
@@ -6265,22 +6265,28 @@ static bool check_all_privileges()
|
||||
}
|
||||
|
||||
/* KILL ... */
|
||||
if ((!opt_no_lock && (opt_kill_long_queries_timeout || opt_lock_ddl_per_table))
|
||||
/* START SLAVE SQL_THREAD */
|
||||
/* STOP SLAVE SQL_THREAD */
|
||||
|| opt_safe_slave_backup) {
|
||||
if (!opt_no_lock && (opt_kill_long_queries_timeout || opt_kill_long_query_type)) {
|
||||
check_result |= check_privilege(
|
||||
granted_privileges,
|
||||
"SUPER", "*", "*",
|
||||
"CONNECTION ADMIN", "*", "*",
|
||||
PRIVILEGE_WARNING);
|
||||
}
|
||||
|
||||
/* START SLAVE SQL_THREAD */
|
||||
/* STOP SLAVE SQL_THREAD */
|
||||
if (opt_safe_slave_backup) {
|
||||
check_result |= check_privilege(
|
||||
granted_privileges,
|
||||
"REPLICATION SLAVE ADMIN", "*", "*",
|
||||
PRIVILEGE_WARNING);
|
||||
}
|
||||
|
||||
/* SHOW MASTER STATUS */
|
||||
/* SHOW SLAVE STATUS */
|
||||
if (opt_galera_info || opt_slave_info
|
||||
|| (opt_no_lock && opt_safe_slave_backup)) {
|
||||
|| opt_safe_slave_backup) {
|
||||
check_result |= check_privilege(granted_privileges,
|
||||
"REPLICATION CLIENT", "*", "*",
|
||||
"SLAVE MONITOR", "*", "*",
|
||||
PRIVILEGE_WARNING);
|
||||
}
|
||||
|
||||
|
@@ -174,7 +174,7 @@ INSERT INTO global_suppressions VALUES
|
||||
|
||||
/* Added 2009-08-XX after fixing Bug #42408 */
|
||||
|
||||
("Although a path was specified for the .* option, log tables are used"),
|
||||
("Although a .* file was specified, log tables are used. To enable logging to files "),
|
||||
("Backup: Operation aborted"),
|
||||
("Restore: Operation aborted"),
|
||||
("Restore: The grant .* was skipped because the user does not exist"),
|
||||
|
@@ -3042,6 +3042,122 @@ a
|
||||
3
|
||||
2
|
||||
drop table t1,t2,t3;
|
||||
#
|
||||
# MDEV-29139: Redundant IN/ALL/ANY predicand in GROUP BY clause of
|
||||
# IN/ALL/ANY/EXISTS subquery
|
||||
#
|
||||
create table t1 (a int);
|
||||
create table t2 (b int);
|
||||
create table t3 (c int);
|
||||
create table t4 (d int);
|
||||
insert into t1 values (3), (1);
|
||||
insert into t2 values (3), (2);
|
||||
insert into t3 values (4), (2);
|
||||
insert into t4 values (1), (7);
|
||||
explain extended select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
|
||||
2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
b
|
||||
3
|
||||
2
|
||||
prepare stmt from "select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4))";
|
||||
execute stmt;
|
||||
b
|
||||
3
|
||||
2
|
||||
execute stmt;
|
||||
b
|
||||
3
|
||||
2
|
||||
deallocate prepare stmt;
|
||||
explain extended select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) >=
|
||||
any (select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
|
||||
2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) >=
|
||||
any (select d from t4));
|
||||
b
|
||||
3
|
||||
2
|
||||
explain extended select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) <
|
||||
all (select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
|
||||
2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) <
|
||||
all (select d from t4));
|
||||
b
|
||||
3
|
||||
2
|
||||
explain extended select b from t2
|
||||
where b in (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t2`.`b` AS `b` from `test`.`t2` semi join (`test`.`t3`) where 1
|
||||
select b from t2
|
||||
where b in (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
b
|
||||
2
|
||||
explain extended select b from t2
|
||||
where b >= any (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
|
||||
2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where <nop>(<in_optimizer>(`test`.`t2`.`b`,(/* select#2 */ select min(`test`.`t3`.`c`) from `test`.`t3`) <= <cache>(`test`.`t2`.`b`)))
|
||||
select b from t2
|
||||
where b >= any (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
b
|
||||
3
|
||||
2
|
||||
explain extended select b from t2
|
||||
where b <= all (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
|
||||
2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where <not>(<in_optimizer>(`test`.`t2`.`b`,<min>(/* select#2 */ select `test`.`t3`.`c` from `test`.`t3`) < <cache>(`test`.`t2`.`b`)))
|
||||
select b from t2
|
||||
where b <= all (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
b
|
||||
2
|
||||
drop table t1,t2,t3,t4;
|
||||
# End of 10.3 tests
|
||||
#
|
||||
# MDEV-19134: EXISTS() slower if ORDER BY is defined
|
||||
|
@@ -2481,6 +2481,80 @@ eval $q3;
|
||||
|
||||
drop table t1,t2,t3;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-29139: Redundant IN/ALL/ANY predicand in GROUP BY clause of
|
||||
--echo # IN/ALL/ANY/EXISTS subquery
|
||||
--echo #
|
||||
|
||||
create table t1 (a int);
|
||||
create table t2 (b int);
|
||||
create table t3 (c int);
|
||||
create table t4 (d int);
|
||||
|
||||
insert into t1 values (3), (1);
|
||||
insert into t2 values (3), (2);
|
||||
insert into t3 values (4), (2);
|
||||
insert into t4 values (1), (7);
|
||||
|
||||
let $q1=
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
|
||||
eval explain extended $q1;
|
||||
eval $q1;
|
||||
|
||||
eval prepare stmt from "$q1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
|
||||
let $q2=
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) >=
|
||||
any (select d from t4));
|
||||
|
||||
eval explain extended $q2;
|
||||
eval $q2;
|
||||
|
||||
let $q3=
|
||||
select b from t2
|
||||
where exists (select c from t3
|
||||
group by (select a from t1 where a = 1) <
|
||||
all (select d from t4));
|
||||
|
||||
eval explain extended $q3;
|
||||
eval $q3;
|
||||
|
||||
let $q4=
|
||||
select b from t2
|
||||
where b in (select c from t3
|
||||
group by (select a from t1 where a = 1) in (select d from t4));
|
||||
|
||||
eval explain extended $q4;
|
||||
eval $q4;
|
||||
|
||||
let $q5=
|
||||
select b from t2
|
||||
where b >= any (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
|
||||
eval explain extended $q5;
|
||||
eval $q5;
|
||||
|
||||
let $q6=
|
||||
select b from t2
|
||||
where b <= all (select c from t3
|
||||
group by (select a from t1 where a = 1) in
|
||||
(select d from t4));
|
||||
|
||||
eval explain extended $q6;
|
||||
eval $q6;
|
||||
|
||||
drop table t1,t2,t3,t4;
|
||||
|
||||
--echo # End of 10.3 tests
|
||||
|
||||
--echo #
|
||||
|
@@ -14,7 +14,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -32,7 +32,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -50,7 +50,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -68,7 +68,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -86,7 +86,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -104,7 +104,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -122,7 +122,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -140,7 +140,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -158,7 +158,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -176,7 +176,7 @@ connection node_1_u;
|
||||
begin;
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -202,7 +202,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -220,7 +220,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -238,7 +238,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -256,7 +256,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -274,7 +274,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -292,7 +292,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -310,7 +310,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -328,7 +328,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -346,7 +346,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
@@ -364,7 +364,7 @@ connection node_1_u;
|
||||
begin;
|
||||
execute upd;
|
||||
connection node_1_i;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
execute ins1;
|
||||
connection node_1;
|
||||
set debug_sync='now WAIT_FOR ins_waiting';
|
||||
|
@@ -72,7 +72,7 @@ while($counter > 0)
|
||||
update user set j = j + 1 WHERE id > 0;
|
||||
|
||||
--connection node_1_i
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
send insert into user_session(id,fk1,fk2) values (2, 2, 2);
|
||||
|
||||
--connection node_1
|
||||
@@ -126,7 +126,7 @@ while($counter > 0)
|
||||
#update user set j = j + 1 WHERE id > 0;
|
||||
|
||||
--connection node_1_i
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
|
||||
send execute ins1;
|
||||
|
||||
--connection node_1
|
||||
|
@@ -7,12 +7,12 @@ SET DEBUG_SYNC = 'innodb_row_search_for_mysql_exit SIGNAL first_del_row_search_m
|
||||
DELETE FROM t WHERE b = 20;
|
||||
connect con_ins_1,localhost,root,,;
|
||||
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished';
|
||||
SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL first_ins_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL first_ins_locked';
|
||||
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
|
||||
INSERT INTO t VALUES(10, 20);
|
||||
connect con_del_2,localhost,root,,;
|
||||
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL second_del_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL second_del_locked';
|
||||
DELETE FROM t WHERE b = 20;
|
||||
connection default;
|
||||
SET DEBUG_SYNC = 'now WAIT_FOR second_del_locked';
|
||||
|
42
mysql-test/suite/innodb/r/deadlock_victim_race.result
Normal file
42
mysql-test/suite/innodb/r/deadlock_victim_race.result
Normal file
@@ -0,0 +1,42 @@
|
||||
CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
|
||||
CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
|
||||
INSERT INTO t VALUES (10, 10), (20, 20), (30, 30);
|
||||
INSERT INTO t2 VALUES (10), (20), (30);
|
||||
BEGIN;
|
||||
SELECT * FROM t WHERE a = 20 FOR UPDATE;
|
||||
a b
|
||||
20 20
|
||||
connect con_2,localhost,root,,;
|
||||
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2';
|
||||
UPDATE t SET b = 100;
|
||||
connect con_3,localhost,root,,;
|
||||
BEGIN;
|
||||
UPDATE t2 SET a = a + 100;
|
||||
SELECT * FROM t WHERE a = 30 FOR UPDATE;
|
||||
a b
|
||||
30 30
|
||||
SET DEBUG_SYNC='now WAIT_FOR upd_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked';
|
||||
SELECT * FROM t WHERE a = 20 FOR UPDATE;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR sel_locked';
|
||||
ROLLBACK;
|
||||
SET DEBUG_SYNC='now SIGNAL upd_cont';
|
||||
SET innodb_lock_wait_timeout=1;
|
||||
SET DEBUG_SYNC="now WAIT_FOR upd_locked";
|
||||
SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
|
||||
SELECT * FROM t WHERE a = 10 FOR UPDATE;
|
||||
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
|
||||
connection con_3;
|
||||
a b
|
||||
20 20
|
||||
connection con_2;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
disconnect con_3;
|
||||
disconnect con_2;
|
||||
connection default;
|
||||
SET DEBUG_SYNC = 'RESET';
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
@@ -11,6 +11,21 @@ CREATE TABLE `d255`.`_##################################################`
|
||||
ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/_@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023
|
||||
CREATE TABLE `d255`.`##################################################`
|
||||
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB;
|
||||
#
|
||||
# MDEV-29258 Failing assertion for name length on RENAME TABLE
|
||||
#
|
||||
CREATE TABLE `d255`.`d245` (x INT) ENGINE=InnoDB;
|
||||
DROP TABLE `d255`.`d250`;
|
||||
RENAME TABLE `d250#`.`d245` TO `d250#`.`d250`;
|
||||
RENAME TABLE `d255`.`d250` TO a;
|
||||
DROP TABLE a,t;
|
||||
#
|
||||
# MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE
|
||||
#
|
||||
CREATE TABLE `d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB;
|
||||
CREATE TABLE `d255`.u(a INT PRIMARY KEY,
|
||||
CONSTRAINT `d320` FOREIGN KEY (a) REFERENCES `d255`.t (a)) ENGINE=InnoDB;
|
||||
RENAME TABLE `d255`.u TO u;
|
||||
DROP TABLE u;
|
||||
DROP DATABASE `d255`;
|
||||
DROP TABLE t;
|
||||
# End of 10.3 tests
|
||||
|
@@ -38,7 +38,7 @@ select f1, f2 from t2 for update;
|
||||
f1 f2
|
||||
1 2
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
update t1 set f1 = 10 where f1 = 2;
|
||||
connection con1;
|
||||
set debug_sync='now WAIT_FOR upd_waiting';
|
||||
@@ -97,7 +97,7 @@ select f1, f2 from t2 for update;
|
||||
f1 f2
|
||||
1 91
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
update t1 set f2 = 28 where f2 = 91;
|
||||
connection con1;
|
||||
set debug_sync='now WAIT_FOR upd_waiting';
|
||||
@@ -164,7 +164,7 @@ select f1 from t3 for update;
|
||||
f1
|
||||
2
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
update t1 set f1 = 10 where f1 = 2;
|
||||
connection con1;
|
||||
set debug_sync='now WAIT_FOR upd_waiting';
|
||||
@@ -253,7 +253,7 @@ select f1 from t3 for update;
|
||||
f1
|
||||
2
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
update t1 set f2 = 28 where f2 = 91;
|
||||
connection con1;
|
||||
set debug_sync='now WAIT_FOR upd_waiting';
|
||||
|
@@ -16,19 +16,19 @@ SET DEBUG_SYNC = 'innodb_row_search_for_mysql_exit SIGNAL first_del_row_search_m
|
||||
--connect(con_ins_1,localhost,root,,)
|
||||
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished';
|
||||
# It's supposed the following INSERT will be suspended just after
|
||||
# lock_wait_suspend_thread_enter syncpoint, and will be awaken
|
||||
# lock_wait_start syncpoint, and will be awaken
|
||||
# after the previous DELETE commits. ib_after_row_insert will be executed
|
||||
# after the INSERT is woken up. The previous DELETE will wait for
|
||||
# first_del_cont signal before commit, and this signal will be sent later.
|
||||
# So it's safe to use two signals in a row here, it's guaranted the first
|
||||
# signal will be received before the second signal is sent.
|
||||
SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL first_ins_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL first_ins_locked';
|
||||
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
|
||||
--send INSERT INTO t VALUES(10, 20)
|
||||
|
||||
--connect(con_del_2,localhost,root,,)
|
||||
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL second_del_locked';
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL second_del_locked';
|
||||
###############################################################################
|
||||
# This DELETE is locked by the previous DELETE, after that DELETE is
|
||||
# committed, it will still be locked by the next INSERT on delete-marked
|
||||
|
102
mysql-test/suite/innodb/t/deadlock_victim_race.test
Normal file
102
mysql-test/suite/innodb/t/deadlock_victim_race.test
Normal file
@@ -0,0 +1,102 @@
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_debug_sync.inc
|
||||
--source include/count_sessions.inc
|
||||
|
||||
CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
|
||||
CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
|
||||
|
||||
INSERT INTO t VALUES (10, 10), (20, 20), (30, 30);
|
||||
INSERT INTO t2 VALUES (10), (20), (30);
|
||||
|
||||
BEGIN; # trx 1
|
||||
SELECT * FROM t WHERE a = 20 FOR UPDATE;
|
||||
# Locking order:
|
||||
# (10,10) (20,20) (30,30)
|
||||
# ^
|
||||
# trx 1
|
||||
|
||||
--connect(con_2,localhost,root,,)
|
||||
# RC is neccessary to do semi-consistent read
|
||||
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
BEGIN; # trx 2
|
||||
# The first time it will be hit on trying to lock (20,20), the second hit
|
||||
# will be on (30,30).
|
||||
SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2';
|
||||
# We must not modify primary key fields to cause rr_sequential() read record
|
||||
# function choosing in mysql_update(), i.e. both query_plan.using_filesort and
|
||||
# query_plan.using_io_buffer must be false during init_read_record() call.
|
||||
--send UPDATE t SET b = 100
|
||||
|
||||
--connect(con_3,localhost,root,,)
|
||||
BEGIN; # trx 3
|
||||
# The following update is necessary to increase the transaction weight, which is
|
||||
# calculated as the number of locks + the number of undo records during deadlock
|
||||
# report. Victim's transaction should have minimum weight. We need trx 2 to be
|
||||
# choosen as victim, that's why we need to increase the current transaction
|
||||
# weight.
|
||||
UPDATE t2 SET a = a + 100;
|
||||
SELECT * FROM t WHERE a = 30 FOR UPDATE;
|
||||
SET DEBUG_SYNC='now WAIT_FOR upd_locked';
|
||||
# Locking queue:
|
||||
# (10,10) (20,20) (30,30)
|
||||
# ^ ^ ^
|
||||
# trx 2 trx 1 trx 3
|
||||
# trx 2 (waiting for 1)
|
||||
|
||||
SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked';
|
||||
--send SELECT * FROM t WHERE a = 20 FOR UPDATE
|
||||
--connection default
|
||||
SET DEBUG_SYNC='now WAIT_FOR sel_locked';
|
||||
# Locking queue:
|
||||
# (10,10) (20,20) (30,30)
|
||||
# ^ ^ ^
|
||||
# trx 2 trx 1 trx 3
|
||||
# trx 2 (waiting for 1)
|
||||
# trx 3 (waiting for 1)
|
||||
#
|
||||
# Note trx 1 must grant lock to trx2 before trx 2 checks the lock state in
|
||||
# lock_trx_handle_wait(), i.e. the function must return DB_SUCCESS, that's why
|
||||
# the following ROLLBACK must be executed before sending upd_cont signal.
|
||||
ROLLBACK;
|
||||
SET DEBUG_SYNC='now SIGNAL upd_cont';
|
||||
|
||||
SET innodb_lock_wait_timeout=1;
|
||||
SET DEBUG_SYNC="now WAIT_FOR upd_locked";
|
||||
# Locking queue:
|
||||
# (10,10) (20,20) (30,30)
|
||||
# ^ ^ ^
|
||||
# trx 2 trx 2 trx 3
|
||||
# trx 3 (waiting for 2) trx 2 (waiting for 3)
|
||||
#
|
||||
# Deadlock happened after trx 1 granted lock to trx 2, and trx2 continued
|
||||
# sequential read (with rr_sequential() read record function), and requested
|
||||
# lock on (30,30). But the deadlock has not been determined yet.
|
||||
|
||||
SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
|
||||
--error ER_LOCK_WAIT_TIMEOUT
|
||||
# The deadlock will be determined in lock_wait() after lock wait timeout
|
||||
# expired.
|
||||
SELECT * FROM t WHERE a = 10 FOR UPDATE;
|
||||
|
||||
--connection con_3
|
||||
--reap
|
||||
|
||||
--connection con_2
|
||||
# As lock_trx_handle_wait() wrongly returned DB_SUCCESS instead of
|
||||
# DB_DEADLOCK, row_search_mvcc() of trx 2 behaves so as if (30,30) was locked.
|
||||
# But the waiting(for trx 3) lock was cancelled by deadlock checker after
|
||||
# trx 2 was choosen as a victim (see lock_cancel_waiting_and_release() call
|
||||
# from Deadlock::report() for details). The try to update non-locked record
|
||||
# will cause assertion if the bug is not fixed.
|
||||
--error ER_LOCK_DEADLOCK
|
||||
--reap
|
||||
|
||||
--disconnect con_3
|
||||
--disconnect con_2
|
||||
|
||||
--connection default
|
||||
SET DEBUG_SYNC = 'RESET';
|
||||
DROP TABLE t;
|
||||
DROP TABLE t2;
|
||||
|
||||
--source include/wait_until_count_sessions.inc
|
@@ -38,8 +38,40 @@ eval CREATE TABLE `$d255`.`_$d250`
|
||||
--replace_result $d255 d255
|
||||
eval CREATE TABLE `$d255`.`$d250`
|
||||
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-29258 Failing assertion for name length on RENAME TABLE
|
||||
--echo #
|
||||
|
||||
let $d245=-------------------------------------------------;
|
||||
--replace_result $d245 d245 $d255 d255
|
||||
eval CREATE TABLE `$d255`.`$d245` (x INT) ENGINE=InnoDB;
|
||||
--replace_result $d250 d250 $d255 d255
|
||||
eval DROP TABLE `$d255`.`$d250`;
|
||||
|
||||
--replace_result $d245 d245 $d250 d250 d255 d255
|
||||
eval RENAME TABLE `$d255`.`$d245` TO `$d255`.`$d250`;
|
||||
--replace_result $d250 d250 $d255 d255
|
||||
eval RENAME TABLE `$d255`.`$d250` TO a;
|
||||
--replace_result $d255 d255
|
||||
DROP TABLE a,t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE
|
||||
--echo #
|
||||
|
||||
let $d225=#############################################;
|
||||
let $d320=################################################################;
|
||||
|
||||
--replace_result $d255 d255
|
||||
eval CREATE TABLE `$d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB;
|
||||
--replace_result $d255 d255 $d320 d320
|
||||
eval CREATE TABLE `$d255`.u(a INT PRIMARY KEY,
|
||||
CONSTRAINT `$d320` FOREIGN KEY (a) REFERENCES `$d255`.t (a)) ENGINE=InnoDB;
|
||||
--replace_result $d255 d255
|
||||
eval RENAME TABLE `$d255`.u TO u;
|
||||
DROP TABLE u;
|
||||
--replace_result $d255 d255
|
||||
eval DROP DATABASE `$d255`;
|
||||
DROP TABLE t;
|
||||
|
||||
--echo # End of 10.3 tests
|
||||
|
@@ -28,7 +28,7 @@ start transaction;
|
||||
select f1, f2 from t2 for update;
|
||||
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
send update t1 set f1 = 10 where f1 = 2;
|
||||
|
||||
connection con1;
|
||||
@@ -72,7 +72,7 @@ start transaction;
|
||||
select f1, f2 from t2 for update;
|
||||
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
send update t1 set f2 = 28 where f2 = 91;
|
||||
|
||||
connection con1;
|
||||
@@ -120,7 +120,7 @@ start transaction;
|
||||
select f1 from t3 for update;
|
||||
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
send update t1 set f1 = 10 where f1 = 2;
|
||||
|
||||
connection con1;
|
||||
@@ -183,7 +183,7 @@ start transaction;
|
||||
select f1 from t3 for update;
|
||||
|
||||
connection default;
|
||||
set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
|
||||
send update t1 set f2 = 28 where f2 = 91;
|
||||
|
||||
connection con1;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
|
||||
FLUSH TABLES;
|
||||
# Test Part 1: Grammar Test
|
||||
CREATE TABLE articles (
|
||||
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
|
||||
@@ -31,7 +32,7 @@ INSERT INTO articles (title, body) VALUES
|
||||
('1001 MySQL Tricks','How to use full-text search engine'),
|
||||
('Go MySQL Tricks','How to use full text search engine');
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql') ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
@@ -68,7 +69,7 @@ INSERT INTO articles (title, body) VALUES
|
||||
('Go MySQL Tricks','How to use full text search engine');
|
||||
ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql') ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
@@ -88,21 +89,23 @@ MATCH(title, body) AGAINST('full text');
|
||||
id title body
|
||||
5 Go MySQL Tricks How to use full text search engine
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION);
|
||||
MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION)
|
||||
ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
3 Optimizing MySQL In this tutorial we will show ...
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
5 Go MySQL Tricks How to use full text search engine
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
3 Optimizing MySQL In this tutorial we will show ...
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION);
|
||||
MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION)
|
||||
ORDER BY id;
|
||||
id title body
|
||||
5 Go MySQL Tricks How to use full text search engine
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
3 Optimizing MySQL In this tutorial we will show ...
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
5 Go MySQL Tricks How to use full text search engine
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('"mysql database"' IN BOOLEAN MODE);
|
||||
id title body
|
||||
@@ -138,27 +141,27 @@ INSERT INTO articles (title, body) VALUES
|
||||
('Go MariaDB Tricks','How to use full text search engine');
|
||||
# restart
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('MySQL');
|
||||
MATCH(title, body) AGAINST('MySQL') ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
3 Optimizing MySQL In this tutorial we will show ...
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('tutorial');
|
||||
MATCH(title, body) AGAINST('tutorial') ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
3 Optimizing MySQL In this tutorial we will show ...
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('Tricks');
|
||||
MATCH(title, body) AGAINST('Tricks') ORDER BY id;
|
||||
id title body
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
5 Go MariaDB Tricks How to use full text search engine
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full text search');
|
||||
MATCH(title, body) AGAINST('full text search') ORDER BY id;
|
||||
id title body
|
||||
5 Go MariaDB Tricks How to use full text search engine
|
||||
4 1001 MySQL Tricks How to use full-text search engine
|
||||
5 Go MariaDB Tricks How to use full text search engine
|
||||
SELECT COUNT(*) FROM articles;
|
||||
COUNT(*)
|
||||
5
|
||||
@@ -186,7 +189,8 @@ UNINSTALL PLUGIN simple_parser;
|
||||
Warnings:
|
||||
Warning 1620 Plugin is busy and will be uninstalled on shutdown
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql')
|
||||
ORDER BY id;
|
||||
id title body
|
||||
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
|
||||
2 How To Use MySQL Well After you went through a ...
|
||||
|
@@ -6,6 +6,9 @@
|
||||
# Install fts parser plugin
|
||||
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
|
||||
|
||||
# Flush the table mysql.plugin in case the server shutdown would time out.
|
||||
FLUSH TABLES;
|
||||
|
||||
-- echo # Test Part 1: Grammar Test
|
||||
# Create a myisam table and alter it to innodb table
|
||||
CREATE TABLE articles (
|
||||
@@ -52,7 +55,7 @@ INSERT INTO articles (title, body) VALUES
|
||||
|
||||
# Simple term search
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql') ORDER BY id;
|
||||
|
||||
# Test stopword and word len less than fts_min_token_size
|
||||
SELECT * FROM articles WHERE
|
||||
@@ -90,7 +93,7 @@ ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
|
||||
|
||||
# Simple term search
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql') ORDER BY id;
|
||||
|
||||
# Test stopword and word len less than fts_min_token_size
|
||||
SELECT * FROM articles WHERE
|
||||
@@ -105,10 +108,12 @@ SELECT * FROM articles WHERE
|
||||
|
||||
# Test query expansion
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION);
|
||||
MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION)
|
||||
ORDER BY id;
|
||||
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION);
|
||||
MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION)
|
||||
ORDER BY id;
|
||||
|
||||
# No result here, we get '"mysql' 'database"' by simple parser
|
||||
SELECT * FROM articles WHERE
|
||||
@@ -152,13 +157,13 @@ INSERT INTO articles (title, body) VALUES
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('MySQL');
|
||||
MATCH(title, body) AGAINST('MySQL') ORDER BY id;
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('tutorial');
|
||||
MATCH(title, body) AGAINST('tutorial') ORDER BY id;
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('Tricks');
|
||||
MATCH(title, body) AGAINST('Tricks') ORDER BY id;
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('full text search');
|
||||
MATCH(title, body) AGAINST('full text search') ORDER BY id;
|
||||
SELECT COUNT(*) FROM articles;
|
||||
|
||||
INSERT INTO articles (title, body) VALUES ('111', '1234 1234 1234');
|
||||
@@ -195,7 +200,8 @@ UNINSTALL PLUGIN simple_parser;
|
||||
|
||||
# Simple term search
|
||||
SELECT * FROM articles WHERE
|
||||
MATCH(title, body) AGAINST('mysql');
|
||||
MATCH(title, body) AGAINST('mysql')
|
||||
ORDER BY id;
|
||||
|
||||
# Test stopword and word len less than fts_min_token_size
|
||||
SELECT * FROM articles WHERE
|
||||
|
@@ -3,4 +3,12 @@ FOUND 1 /missing required privilege RELOAD/ in backup.log
|
||||
FOUND 1 /missing required privilege PROCESS/ in backup.log
|
||||
FOUND 1 /GRANT USAGE ON/ in backup.log
|
||||
GRANT RELOAD, PROCESS on *.* to backup@localhost;
|
||||
NOT FOUND /missing required privilege REPLICA MONITOR/ in backup.log
|
||||
GRANT REPLICA MONITOR ON *.* TO backup@localhost;
|
||||
REVOKE REPLICA MONITOR ON *.* FROM backup@localhost;
|
||||
GRANT CONNECTION ADMIN ON *.* TO backup@localhost;
|
||||
FOUND 1 /missing required privilege REPLICATION SLAVE ADMIN/ in backup.log
|
||||
NOT FOUND /missing required privilege REPLICA MONITOR/ in backup.log
|
||||
GRANT REPLICATION SLAVE ADMIN ON *.* TO backup@localhost;
|
||||
GRANT REPLICA MONITOR ON *.* TO backup@localhost;
|
||||
DROP USER backup@localhost;
|
||||
|
@@ -25,7 +25,62 @@ GRANT RELOAD, PROCESS on *.* to backup@localhost;
|
||||
--disable_result_log
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --target-dir=$targetdir;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
|
||||
# MDEV-23607 Warning: missing required privilege REPLICATION CLIENT
|
||||
# --slave-info and galera info require REPLICA MONITOR
|
||||
--disable_result_log
|
||||
error 1;
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --slave-info --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
|
||||
--let SEARCH_PATTERN= missing required privilege REPLICA MONITOR
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
GRANT REPLICA MONITOR ON *.* TO backup@localhost;
|
||||
--disable_result_log
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --slave-info --target-dir=$targetdir;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
REVOKE REPLICA MONITOR ON *.* FROM backup@localhost;
|
||||
|
||||
# TODO need a query that would delay a BACKUP STAGE START/ BACKUP STAGE BLOCK_COMMIT longer than the kill-long-queries-timeout
|
||||
#--send SELECT SLEEP(9) kill_me
|
||||
## kill-long-query-type=(not empty) requires CONNECTION ADMIN
|
||||
#--disable_result_log
|
||||
#error 1;
|
||||
#--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --kill-long-query-type=all --kill-long-queries-timeout=4 --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
|
||||
#--reap
|
||||
#--enable_result_log
|
||||
#rmdir $targetdir;
|
||||
#
|
||||
#--let SEARCH_PATTERN= missing required privilege CONNECTION ADMIN
|
||||
#--source include/search_pattern_in_file.inc
|
||||
|
||||
GRANT CONNECTION ADMIN ON *.* TO backup@localhost;
|
||||
--disable_result_log
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --kill-long-query-type=all --kill-long-queries-timeout=1 --target-dir=$targetdir;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
|
||||
# --safe-slave-backup requires REPLICATION SLAVE ADMIN, and REPLICA MONITOR
|
||||
--disable_result_log
|
||||
error 1;
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --safe-slave-backup --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
|
||||
--let SEARCH_PATTERN= missing required privilege REPLICATION SLAVE ADMIN
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= missing required privilege REPLICA MONITOR
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
GRANT REPLICATION SLAVE ADMIN ON *.* TO backup@localhost;
|
||||
GRANT REPLICA MONITOR ON *.* TO backup@localhost;
|
||||
--disable_result_log
|
||||
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --safe-slave-backup --target-dir=$targetdir;
|
||||
--enable_result_log
|
||||
rmdir $targetdir;
|
||||
|
||||
DROP USER backup@localhost;
|
||||
# Cleanup
|
||||
rmdir $targetdir;
|
||||
|
@@ -191,6 +191,20 @@ ERROR 23000: Duplicate entry '1-2020-03-01-2020-03-02' for key 'PRIMARY'
|
||||
alter table t1 add system versioning;
|
||||
drop table t1;
|
||||
#
|
||||
# MDEV-18873 Server crashes in Compare_identifiers::operator or in
|
||||
# my_strcasecmp_utf8 upon ADD PERIOD IF NOT EXISTS with empty name
|
||||
#
|
||||
alter table t add period if not exists for `` (s,e);
|
||||
ERROR 42000: Incorrect column name ''
|
||||
create table t(s DATE, e DATE);
|
||||
alter table t add period if not exists for `` (s,e);
|
||||
ERROR 42000: Incorrect column name ''
|
||||
alter table t add period if not exists for ` ` (s,e);
|
||||
ERROR 42000: Incorrect column name ' '
|
||||
create table t2 (period for `` (s,e)) select * from t;
|
||||
ERROR 42000: Incorrect column name ''
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-21941 RENAME doesn't work for system time or period fields
|
||||
#
|
||||
create or replace table t1 (
|
||||
|
@@ -152,6 +152,29 @@ alter table t1 add system versioning;
|
||||
# cleanup
|
||||
drop table t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-18873 Server crashes in Compare_identifiers::operator or in
|
||||
--echo # my_strcasecmp_utf8 upon ADD PERIOD IF NOT EXISTS with empty name
|
||||
--echo #
|
||||
|
||||
# When there is no table defined.
|
||||
--error ER_WRONG_COLUMN_NAME
|
||||
alter table t add period if not exists for `` (s,e);
|
||||
|
||||
# When there is an actual table.
|
||||
create table t(s DATE, e DATE);
|
||||
--error ER_WRONG_COLUMN_NAME
|
||||
alter table t add period if not exists for `` (s,e);
|
||||
|
||||
# When the last character is space
|
||||
--error ER_WRONG_COLUMN_NAME
|
||||
alter table t add period if not exists for ` ` (s,e);
|
||||
|
||||
# Create table with an empty period name
|
||||
--error ER_WRONG_COLUMN_NAME
|
||||
create table t2 (period for `` (s,e)) select * from t;
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-21941 RENAME doesn't work for system time or period fields
|
||||
--echo #
|
||||
|
@@ -0,0 +1,55 @@
|
||||
include/master-slave.inc
|
||||
[connection master]
|
||||
#
|
||||
# Set replica to ignore system mysql tables
|
||||
connection slave;
|
||||
include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
|
||||
include/start_slave.inc
|
||||
#
|
||||
# Execute grant-based commands on primary which modify mysql system
|
||||
# tables
|
||||
connection master;
|
||||
CREATE ROLE journalist;
|
||||
CREATE USER testuser@localhost IDENTIFIED by '';
|
||||
GRANT journalist to testuser@localhost;
|
||||
#
|
||||
# Execute SET commands which use the previous user/role data
|
||||
SET DEFAULT ROLE journalist for testuser@localhost;
|
||||
SET PASSWORD for testuser@localhost= PASSWORD('123');
|
||||
include/save_master_gtid.inc
|
||||
#
|
||||
# Verify primary's grant tables have the correct user/role data
|
||||
select count(*)=1 from mysql.user where User='testuser';
|
||||
count(*)=1
|
||||
1
|
||||
select count(*)=1 from mysql.roles_mapping where User='testuser';
|
||||
count(*)=1
|
||||
1
|
||||
#
|
||||
# Ensure that the replica receives all of the primary's events without
|
||||
# error
|
||||
connection slave;
|
||||
include/sync_with_master_gtid.inc
|
||||
Last_SQL_Error =
|
||||
Last_SQL_Errno = 0
|
||||
#
|
||||
# Verify that the replica did not execute the master's commands
|
||||
select count(*)=0 from mysql.user where User='testuser';
|
||||
count(*)=0
|
||||
1
|
||||
select count(*)=0 from mysql.roles_mapping where User='testuser';
|
||||
count(*)=0
|
||||
1
|
||||
#
|
||||
# Clean up
|
||||
connection master;
|
||||
DROP ROLE journalist;
|
||||
DROP USER testuser@localhost;
|
||||
include/save_master_gtid.inc
|
||||
connection slave;
|
||||
include/sync_with_master_gtid.inc
|
||||
include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_wild_ignore_table="";
|
||||
include/start_slave.inc
|
||||
include/rpl_end.inc
|
83
mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
Normal file
83
mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
Normal file
@@ -0,0 +1,83 @@
|
||||
#
|
||||
# Purpose:
|
||||
# This test ensures that the SET DEFAULT ROLE and SET PASSWORD commands can
|
||||
# be ignored by replica filter rules. MDEV-28294 exposed a bug in which
|
||||
# SET DEFAULT ROLE would check for the existence of the given roles/user even
|
||||
# when the targeted tables are ignored, resulting in errors if the targeted
|
||||
# data does not exist. More specifically, when previously issued
|
||||
# CREATE USER/ROLE commands are ignored by the replica because of the
|
||||
# replication filtering rules, SET DEFAULT ROLE would result in an error
|
||||
# because the targeted data does not exist.
|
||||
#
|
||||
# Methodology:
|
||||
# Using a replica configured with replicate_wild_ignore_table="mysql.%",
|
||||
# execute SET DEFAULT ROLE and SET PASSWORD on the primary and ensure that the
|
||||
# replica neither errors nor executes the commands which the primary sends.
|
||||
#
|
||||
# References:
|
||||
# MDEV-28294: set default role bypasses Replicate_Wild_Ignore_Table: mysql.%
|
||||
#
|
||||
|
||||
source include/master-slave.inc;
|
||||
source include/have_binlog_format_mixed.inc;
|
||||
|
||||
--echo #
|
||||
--echo # Set replica to ignore system mysql tables
|
||||
connection slave;
|
||||
let $old_filter= query_get_value(SHOW SLAVE STATUS, Replicate_Wild_Ignore_Table, 1);
|
||||
source include/stop_slave.inc;
|
||||
SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
|
||||
source include/start_slave.inc;
|
||||
|
||||
--echo #
|
||||
--echo # Execute grant-based commands on primary which modify mysql system
|
||||
--echo # tables
|
||||
connection master;
|
||||
CREATE ROLE journalist;
|
||||
CREATE USER testuser@localhost IDENTIFIED by '';
|
||||
GRANT journalist to testuser@localhost;
|
||||
|
||||
--echo #
|
||||
--echo # Execute SET commands which use the previous user/role data
|
||||
SET DEFAULT ROLE journalist for testuser@localhost;
|
||||
SET PASSWORD for testuser@localhost= PASSWORD('123');
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--echo #
|
||||
--echo # Verify primary's grant tables have the correct user/role data
|
||||
select count(*)=1 from mysql.user where User='testuser';
|
||||
select count(*)=1 from mysql.roles_mapping where User='testuser';
|
||||
|
||||
--echo #
|
||||
--echo # Ensure that the replica receives all of the primary's events without
|
||||
--echo # error
|
||||
connection slave;
|
||||
--source include/sync_with_master_gtid.inc
|
||||
let $error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error, 1);
|
||||
--echo Last_SQL_Error = $error
|
||||
let $errno= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
|
||||
--echo Last_SQL_Errno = $errno
|
||||
|
||||
--echo #
|
||||
--echo # Verify that the replica did not execute the master's commands
|
||||
select count(*)=0 from mysql.user where User='testuser';
|
||||
select count(*)=0 from mysql.roles_mapping where User='testuser';
|
||||
|
||||
--echo #
|
||||
--echo # Clean up
|
||||
|
||||
# The master has to drop the role/user combination while the slave still has
|
||||
# its filters active; otherwise, the slave would try to drop users/roles that
|
||||
# were never replicated.
|
||||
--connection master
|
||||
DROP ROLE journalist;
|
||||
DROP USER testuser@localhost;
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave
|
||||
--source include/sync_with_master_gtid.inc
|
||||
source include/stop_slave.inc;
|
||||
--eval SET @@GLOBAL.replicate_wild_ignore_table="$old_filter"
|
||||
source include/start_slave.inc;
|
||||
|
||||
--source include/rpl_end.inc
|
@@ -92,7 +92,7 @@ extern "C" FILE *my_win_popen(const char *cmd, const char *mode)
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
/* Unknown mode, éxpected "r", "rt", "w", "wt" */
|
||||
/* Unknown mode, expected "r", "rt", "w", "wt" */
|
||||
abort();
|
||||
}
|
||||
if (!SetHandleInformation(parent_pipe_end, HANDLE_FLAG_INHERIT, 0))
|
||||
|
@@ -392,7 +392,8 @@ bool Item_subselect::mark_as_eliminated_processor(void *arg)
|
||||
bool Item_subselect::eliminate_subselect_processor(void *arg)
|
||||
{
|
||||
unit->item= NULL;
|
||||
unit->exclude();
|
||||
if (!unit->is_excluded())
|
||||
unit->exclude();
|
||||
eliminated= TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
|
||||
Copyright (c) 2009, 2021, MariaDB
|
||||
Copyright (c) 2009, 2022, MariaDB
|
||||
|
||||
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
|
||||
@@ -1937,10 +1937,17 @@ class Grant_tables
|
||||
public:
|
||||
Grant_tables() : p_user_table(&m_user_table_json) { }
|
||||
|
||||
int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
|
||||
/**
|
||||
An auxiliary to build a list of involved tables.
|
||||
|
||||
@retval 0 Success
|
||||
@retval -1 A my_error reported error
|
||||
*/
|
||||
int build_table_list(THD *thd, TABLE_LIST** ptr_first,
|
||||
int which_tables, enum thr_lock_type lock_type,
|
||||
TABLE_LIST *tables)
|
||||
{
|
||||
DBUG_ENTER("Grant_tables::open_and_lock");
|
||||
TABLE_LIST tables[USER_TABLE+1], *first= NULL;
|
||||
DBUG_ENTER("Grant_tables::build_table_list");
|
||||
|
||||
DBUG_ASSERT(which_tables); /* At least one table must be opened. */
|
||||
/*
|
||||
@@ -1965,12 +1972,23 @@ class Grant_tables
|
||||
tl->updating= lock_type >= TL_FIRST_WRITE;
|
||||
if (i >= FIRST_OPTIONAL_TABLE)
|
||||
tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
|
||||
tl->next_global= tl->next_local= first;
|
||||
first= tl;
|
||||
tl->next_global= tl->next_local= *ptr_first;
|
||||
*ptr_first= tl;
|
||||
}
|
||||
else
|
||||
tl->table= NULL;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
|
||||
{
|
||||
DBUG_ENTER("Grant_tables::open_and_lock");
|
||||
|
||||
TABLE_LIST tables[USER_TABLE+1], *first= NULL;
|
||||
|
||||
if (build_table_list(thd, &first, which_tables, lock_type, tables))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
uint counter;
|
||||
int res= really_open(thd, first, &counter);
|
||||
@@ -2041,6 +2059,48 @@ class Grant_tables
|
||||
inline const Roles_mapping_table& roles_mapping_table() const
|
||||
{ return m_roles_mapping_table; }
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
/**
|
||||
Checks if the tables targeted by a grant command should be ignored because
|
||||
of the configured replication filters
|
||||
|
||||
@retval 1 Tables are excluded for replication
|
||||
@retval 0 tables are included for replication
|
||||
*/
|
||||
int rpl_ignore_tables(THD *thd, TABLE_LIST* tables, int which_tables= 0,
|
||||
enum thr_lock_type lock_type= TL_IGNORE)
|
||||
{
|
||||
DBUG_ENTER("Grant_tables::rpl_ignore_tables");
|
||||
|
||||
if (!(thd->slave_thread && !thd->spcont))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
TABLE_LIST all_tables[USER_TABLE+1];
|
||||
|
||||
if (!tables)
|
||||
{
|
||||
int rc __attribute__((unused))=
|
||||
build_table_list(thd, &tables, which_tables, lock_type, all_tables);
|
||||
|
||||
DBUG_ASSERT(!rc); // Grant_tables must be already initialized
|
||||
DBUG_ASSERT(tables);
|
||||
}
|
||||
|
||||
if (tables->lock_type >= TL_FIRST_WRITE)
|
||||
{
|
||||
/*
|
||||
GRANT and REVOKE are applied the slave in/exclusion rules as they are
|
||||
some kind of updates to the mysql.% tables.
|
||||
*/
|
||||
Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
|
||||
|
||||
if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
/* Before any operation is possible on grant tables, they must be opened.
|
||||
@@ -2054,16 +2114,9 @@ class Grant_tables
|
||||
{
|
||||
DBUG_ENTER("Grant_tables::really_open:");
|
||||
#ifdef HAVE_REPLICATION
|
||||
if (tables->lock_type >= TL_FIRST_WRITE &&
|
||||
thd->slave_thread && !thd->spcont)
|
||||
if (rpl_ignore_tables(thd, tables))
|
||||
{
|
||||
/*
|
||||
GRANT and REVOKE are applied the slave in/exclusion rules as they are
|
||||
some kind of updates to the mysql.% tables.
|
||||
*/
|
||||
Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
|
||||
if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
#endif
|
||||
if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
|
||||
@@ -4080,6 +4133,17 @@ int acl_check_set_default_role(THD *thd, const char *host, const char *user,
|
||||
const char *role)
|
||||
{
|
||||
DBUG_ENTER("acl_check_set_default_role");
|
||||
#ifdef HAVE_REPLICATION
|
||||
/*
|
||||
If the roles_mapping table is excluded by the replication filter, we return
|
||||
successful without validating the user/role data because the command will
|
||||
be ignored in a later call to `acl_set_default_role()` for a graceful exit.
|
||||
*/
|
||||
Grant_tables tables;
|
||||
TABLE_LIST* first= NULL;
|
||||
if (tables.rpl_ignore_tables(thd, first, Table_roles_mapping, TL_WRITE))
|
||||
DBUG_RETURN(0);
|
||||
#endif
|
||||
DBUG_RETURN(check_alter_user(thd, host, user) ||
|
||||
check_user_can_set_role(thd, user, host, NULL, role, NULL));
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include "sql_limit.h" // Select_limit_counters
|
||||
#include "json_table.h" // Json_table_column
|
||||
#include "sql_schema.h"
|
||||
#include "table.h"
|
||||
|
||||
/* Used for flags of nesting constructs */
|
||||
#define SELECT_NESTING_MAP_SIZE 64
|
||||
@@ -4562,6 +4563,11 @@ public:
|
||||
|
||||
int add_period(Lex_ident name, Lex_ident_sys_st start, Lex_ident_sys_st end)
|
||||
{
|
||||
if (check_period_name(name.str)) {
|
||||
my_error(ER_WRONG_COLUMN_NAME, MYF(0), name.str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lex_string_cmp(system_charset_info, &start, &end) == 0)
|
||||
{
|
||||
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str);
|
||||
|
@@ -5069,7 +5069,7 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
|
||||
length= build_table_filename(to, sizeof(to) - 1, new_db->str,
|
||||
new_name->str, "", flags & FN_TO_IS_TMP);
|
||||
// Check if we hit FN_REFLEN bytes along with file extension.
|
||||
if (length+reg_ext_length >= FN_REFLEN)
|
||||
if (length+reg_ext_length > FN_REFLEN)
|
||||
{
|
||||
my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(to)-1, to);
|
||||
DBUG_RETURN(TRUE);
|
||||
|
@@ -4926,7 +4926,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
|
||||
int
|
||||
rename_file_ext(const char * from,const char * to,const char * ext)
|
||||
{
|
||||
char from_b[FN_REFLEN],to_b[FN_REFLEN];
|
||||
/* Reserve space for ./databasename/tablename.frm + NUL byte */
|
||||
char from_b[2 + FN_REFLEN + 4 + 1], to_b[2 + FN_REFLEN + 4 + 1];
|
||||
(void) strxmov(from_b,from,ext,NullS);
|
||||
(void) strxmov(to_b,to,ext,NullS);
|
||||
return mysql_file_rename(key_file_frm, from_b, to_b, MYF(0));
|
||||
@@ -5181,6 +5182,12 @@ bool check_column_name(const char *name)
|
||||
}
|
||||
|
||||
|
||||
bool check_period_name(const char *name)
|
||||
{
|
||||
return check_column_name(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Checks whether a table is intact. Should be done *just* after the table has
|
||||
been opened.
|
||||
|
@@ -3223,6 +3223,7 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
|
||||
void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
|
||||
bool check_db_name(LEX_STRING *db);
|
||||
bool check_column_name(const char *name);
|
||||
bool check_period_name(const char *name);
|
||||
bool check_table_name(const char *name, size_t length, bool check_for_path_chars);
|
||||
int rename_file_ext(const char * from,const char * to,const char * ext);
|
||||
char *get_field(MEM_ROOT *mem, Field *field);
|
||||
|
@@ -40,6 +40,8 @@ struct Compare_identifiers
|
||||
{
|
||||
int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const
|
||||
{
|
||||
DBUG_ASSERT(a.str != NULL);
|
||||
DBUG_ASSERT(b.str != NULL);
|
||||
DBUG_ASSERT(a.str[a.length] == 0);
|
||||
DBUG_ASSERT(b.str[b.length] == 0);
|
||||
return my_strcasecmp(system_charset_info, a.str, b.str);
|
||||
|
@@ -642,7 +642,6 @@ bool DOMNODELIST::DropItem(PGLOBAL g, int n)
|
||||
if (Listp == NULL || Listp->length < n)
|
||||
return true;
|
||||
|
||||
//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set'
|
||||
return false;
|
||||
} // end of DeleteItem
|
||||
|
||||
|
@@ -530,8 +530,7 @@ btr_page_alloc_low(
|
||||
mtr->u_lock_register(savepoint);
|
||||
root->page.lock.u_lock();
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
if (root->index)
|
||||
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
|
||||
btr_search_drop_page_hash_index(root, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -645,8 +644,7 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr,
|
||||
mtr->u_lock_register(savepoint);
|
||||
root->page.lock.u_lock();
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
if (root->index)
|
||||
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
|
||||
btr_search_drop_page_hash_index(root, true);
|
||||
#endif
|
||||
}
|
||||
err= fseg_free_page(&root->page.frame[blob ||
|
||||
|
@@ -278,10 +278,10 @@ latch_block:
|
||||
latch_leaves->blocks[1] = block;
|
||||
}
|
||||
|
||||
mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
||||
|
||||
block->page.fix();
|
||||
block->page.lock.x_lock();
|
||||
|
||||
mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
ut_ad(!btr_search_check_marked_free_index(block));
|
||||
#endif
|
||||
@@ -7017,10 +7017,11 @@ btr_store_big_rec_extern_fields(
|
||||
mtr.start();
|
||||
index->set_modified(mtr);
|
||||
mtr.set_log_mode_sub(*btr_mtr);
|
||||
mtr.memo_push(rec_block, MTR_MEMO_PAGE_X_FIX);
|
||||
|
||||
rec_block->page.fix();
|
||||
rec_block->page.lock.x_lock();
|
||||
|
||||
mtr.memo_push(rec_block, MTR_MEMO_PAGE_X_FIX);
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
ut_ad(!btr_search_check_marked_free_index(rec_block));
|
||||
#endif
|
||||
@@ -7396,9 +7397,10 @@ skip_free:
|
||||
/* The buffer pool block containing the BLOB pointer is
|
||||
exclusively latched by local_mtr. To satisfy some design
|
||||
constraints, we must recursively latch it in mtr as well. */
|
||||
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
||||
block->fix();
|
||||
block->page.lock.x_lock();
|
||||
|
||||
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
ut_ad(!btr_search_check_marked_free_index(block));
|
||||
#endif
|
||||
|
@@ -1292,10 +1292,6 @@ void btr_search_drop_page_hash_index(buf_block_t* block,
|
||||
rec_offs* offsets;
|
||||
|
||||
retry:
|
||||
/* This debug check uses a dirty read that could theoretically cause
|
||||
false positives while buf_pool.clear_hash_index() is executing. */
|
||||
assert_block_ahi_valid(block);
|
||||
|
||||
if (!block->index) {
|
||||
return;
|
||||
}
|
||||
|
@@ -2815,12 +2815,10 @@ get_latch:
|
||||
goto page_id_mismatch;
|
||||
}
|
||||
get_latch_valid:
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
if (block->index) {
|
||||
mtr_t::defer_drop_ahi(block, fix_type);
|
||||
}
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
mtr->memo_push(block, fix_type);
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
btr_search_drop_page_hash_index(block, true);
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
break;
|
||||
case RW_SX_LATCH:
|
||||
fix_type = MTR_MEMO_PAGE_SX_FIX;
|
||||
|
@@ -1098,7 +1098,7 @@ dict_create_table_step(
|
||||
}
|
||||
|
||||
if (node->state == TABLE_ADD_TO_CACHE) {
|
||||
node->table->can_be_evicted = true;
|
||||
node->table->can_be_evicted = !node->table->fts;
|
||||
node->table->add_to_cache();
|
||||
|
||||
err = DB_SUCCESS;
|
||||
|
@@ -1665,7 +1665,7 @@ dict_table_rename_in_cache(
|
||||
in UTF-8 charset. The variable fkid here is used
|
||||
to store foreign key constraint name in charset
|
||||
my_charset_filename for comparison further below. */
|
||||
char fkid[MAX_TABLE_NAME_LEN+20];
|
||||
char fkid[MAX_TABLE_NAME_LEN * 2 + 20];
|
||||
|
||||
/* The old table name in my_charset_filename is stored
|
||||
in old_name_cs_filename */
|
||||
@@ -1697,7 +1697,8 @@ dict_table_rename_in_cache(
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN);
|
||||
strncpy(fkid, foreign->id, (sizeof fkid) - 1);
|
||||
fkid[(sizeof fkid) - 1] = '\0';
|
||||
|
||||
const bool on_tmp = dict_table_t::is_temporary_name(
|
||||
fkid);
|
||||
@@ -3522,10 +3523,11 @@ dict_table_get_highest_foreign_id(
|
||||
for (dict_foreign_set::iterator it = table->foreign_set.begin();
|
||||
it != table->foreign_set.end();
|
||||
++it) {
|
||||
char fkid[MAX_TABLE_NAME_LEN+20];
|
||||
char fkid[MAX_TABLE_NAME_LEN * 2 + 20];
|
||||
foreign = *it;
|
||||
|
||||
strcpy(fkid, foreign->id);
|
||||
strncpy(fkid, foreign->id, (sizeof fkid) - 1);
|
||||
fkid[(sizeof fkid) - 1] = '\0';
|
||||
/* Convert foreign key identifier on dictionary memory
|
||||
cache to filename charset. */
|
||||
innobase_convert_to_filename_charset(
|
||||
|
@@ -245,7 +245,8 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
|
||||
ut_ad(ib_vector_is_empty(autoinc_locks));
|
||||
mem_heap_empty(lock.lock_heap);
|
||||
lock.table_locks.clear();
|
||||
lock.was_chosen_as_deadlock_victim= false;
|
||||
/* commit_persist() already reset this. */
|
||||
ut_ad(!lock.was_chosen_as_deadlock_victim);
|
||||
lock.n_rec_locks= 0;
|
||||
while (dict_table_t *table= UT_LIST_GET_FIRST(lock.evicted_tables))
|
||||
{
|
||||
|
@@ -14826,9 +14826,11 @@ ha_innobase::info_low(
|
||||
stats.index_file_length
|
||||
= ulonglong(stat_sum_of_other_index_sizes)
|
||||
* size;
|
||||
space->s_lock();
|
||||
stats.delete_length = 1024
|
||||
* fsp_get_available_space_in_free_extents(
|
||||
*space);
|
||||
space->s_unlock();
|
||||
}
|
||||
stats.check_time = 0;
|
||||
stats.mrr_length_per_rec= (uint)ref_length + 8; // 8 = max(sizeof(void *));
|
||||
|
@@ -622,12 +622,6 @@ public:
|
||||
PAGE_FLUSH_SYNC
|
||||
};
|
||||
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
/** If a stale adaptive hash index exists on the block, drop it. */
|
||||
ATTRIBUTE_COLD
|
||||
static void defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/** Log a write of a byte string to a page.
|
||||
@param block buffer page
|
||||
|
@@ -339,27 +339,11 @@ struct trx_lock_t
|
||||
/** lock wait start time */
|
||||
Atomic_relaxed<my_hrtime_t> suspend_time;
|
||||
|
||||
#if defined(UNIV_DEBUG) || !defined(DBUG_OFF)
|
||||
/** 2=high priority WSREP thread has marked this trx to abort;
|
||||
1=another transaction chose this as a victim in deadlock resolution. */
|
||||
Atomic_relaxed<byte> was_chosen_as_deadlock_victim;
|
||||
|
||||
/** Clear the deadlock victim status. */
|
||||
void clear_deadlock_victim()
|
||||
{
|
||||
#ifndef WITH_WSREP
|
||||
was_chosen_as_deadlock_victim= false;
|
||||
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
|
||||
/* There is no 8-bit version of the 80386 BTR instruction.
|
||||
Technically, this is the wrong addressing mode (16-bit), but
|
||||
there are other data members stored after the byte. */
|
||||
__asm__ __volatile__("lock btrw $0, %0"
|
||||
: "+m" (was_chosen_as_deadlock_victim));
|
||||
#else
|
||||
was_chosen_as_deadlock_victim.fetch_and(byte(~1));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WITH_WSREP
|
||||
/** Flag the lock owner as a victim in Galera conflict resolution. */
|
||||
void set_wsrep_victim()
|
||||
{
|
||||
@@ -373,7 +357,17 @@ struct trx_lock_t
|
||||
was_chosen_as_deadlock_victim.fetch_or(2);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
#else /* defined(UNIV_DEBUG) || !defined(DBUG_OFF) */
|
||||
|
||||
/** High priority WSREP thread has marked this trx to abort or
|
||||
another transaction chose this as a victim in deadlock resolution. */
|
||||
Atomic_relaxed<bool> was_chosen_as_deadlock_victim;
|
||||
|
||||
/** Flag the lock owner as a victim in Galera conflict resolution. */
|
||||
void set_wsrep_victim() {
|
||||
was_chosen_as_deadlock_victim= true;
|
||||
}
|
||||
#endif /* defined(UNIV_DEBUG) || !defined(DBUG_OFF) */
|
||||
|
||||
/** Next available rec_pool[] entry */
|
||||
byte rec_cached;
|
||||
|
@@ -44,6 +44,7 @@ Created 5/7/1996 Heikki Tuuri
|
||||
#include "row0vers.h"
|
||||
#include "pars0pars.h"
|
||||
#include "srv0mon.h"
|
||||
#include "scope.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
@@ -1275,6 +1276,14 @@ lock_rec_enqueue_waiting(
|
||||
trx_t* trx = thr_get_trx(thr);
|
||||
ut_ad(xtest() || trx->mutex_is_owner());
|
||||
ut_ad(!trx->dict_operation_lock_mode);
|
||||
/* Apart from Galera, only transactions that have waiting lock can be
|
||||
chosen as deadlock victim. Only one lock can be waited for at a time,
|
||||
and a transaction is associated with a single thread. That is why there
|
||||
must not be waiting lock requests if the transaction is deadlock victim
|
||||
and it is not WSREP. Galera transaction abort can be invoked from MDL
|
||||
acquisition code when the transaction does not have waiting record
|
||||
lock, that's why we check only deadlock victim bit here. */
|
||||
ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
|
||||
|
||||
if (trx->mysql_thd && thd_lock_wait_timeout(trx->mysql_thd) == 0) {
|
||||
trx->error_state = DB_LOCK_WAIT_TIMEOUT;
|
||||
@@ -1292,7 +1301,6 @@ lock_rec_enqueue_waiting(
|
||||
}
|
||||
|
||||
trx->lock.wait_thr = thr;
|
||||
trx->lock.clear_deadlock_victim();
|
||||
|
||||
DBUG_LOG("ib_lock", "trx " << ib::hex(trx->id)
|
||||
<< " waits for lock in index " << index->name
|
||||
@@ -1475,7 +1483,14 @@ lock_rec_lock(
|
||||
que_thr_t* thr) /*!< in: query thread */
|
||||
{
|
||||
trx_t *trx= thr_get_trx(thr);
|
||||
|
||||
/* There must not be lock requests for reads or updates if transaction was
|
||||
chosen as deadlock victim. Apart from Galera, only transactions that have
|
||||
waiting lock may be chosen as deadlock victims. Only one lock can be waited
|
||||
for at a time, and a transaction is associated with a single thread. Galera
|
||||
transaction abort can be invoked from MDL acquisition code when the
|
||||
transaction does not have waiting lock, that's why we check only deadlock
|
||||
victim bit here. */
|
||||
ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
|
||||
ut_ad(!srv_read_only_mode);
|
||||
ut_ad(((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_S ||
|
||||
((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_X);
|
||||
@@ -1627,7 +1642,9 @@ void lock_sys_t::wait_resume(THD *thd, my_hrtime_t start, my_hrtime_t now)
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
ATTRIBUTE_NOINLINE MY_ATTRIBUTE((nonnull))
|
||||
/** Report lock waits to parallel replication.
|
||||
/** Report lock waits to parallel replication. Sets
|
||||
trx->error_state= DB_DEADLOCK if trx->lock.was_chosen_as_deadlock_victim was
|
||||
set when lock_sys.wait_mutex was unlocked.
|
||||
@param trx transaction that may be waiting for a lock
|
||||
@param wait_lock lock that is being waited for */
|
||||
static void lock_wait_rpl_report(trx_t *trx)
|
||||
@@ -1642,7 +1659,8 @@ static void lock_wait_rpl_report(trx_t *trx)
|
||||
ut_ad(!(wait_lock->type_mode & LOCK_AUTO_INC));
|
||||
/* This would likely be too large to attempt to use a memory transaction,
|
||||
even for wait_lock->is_table(). */
|
||||
if (!lock_sys.wr_lock_try())
|
||||
const bool nowait= lock_sys.wr_lock_try();
|
||||
if (!nowait)
|
||||
{
|
||||
mysql_mutex_unlock(&lock_sys.wait_mutex);
|
||||
lock_sys.wr_lock(SRW_LOCK_CALL);
|
||||
@@ -1652,6 +1670,10 @@ static void lock_wait_rpl_report(trx_t *trx)
|
||||
{
|
||||
func_exit:
|
||||
lock_sys.wr_unlock();
|
||||
/* trx->lock.was_chosen_as_deadlock_victim can be set when
|
||||
lock_sys.wait_mutex was unlocked, let's check it. */
|
||||
if (!nowait && trx->lock.was_chosen_as_deadlock_victim)
|
||||
trx->error_state= DB_DEADLOCK;
|
||||
return;
|
||||
}
|
||||
ut_ad(wait_lock->is_waiting());
|
||||
@@ -1700,7 +1722,13 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
trx_t *trx= thr_get_trx(thr);
|
||||
|
||||
if (trx->mysql_thd)
|
||||
DEBUG_SYNC_C("lock_wait_suspend_thread_enter");
|
||||
DEBUG_SYNC_C("lock_wait_start");
|
||||
|
||||
/* Create the sync point for any quit from the function. */
|
||||
ut_d(SCOPE_EXIT([trx]() {
|
||||
if (trx->mysql_thd)
|
||||
DEBUG_SYNC_C("lock_wait_end");
|
||||
}));
|
||||
|
||||
/* InnoDB system transactions may use the global value of
|
||||
innodb_lock_wait_timeout, because trx->mysql_thd == NULL. */
|
||||
@@ -1731,11 +1759,8 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
{
|
||||
/* The lock has already been released or this transaction
|
||||
was chosen as a deadlock victim: no need to wait */
|
||||
if (trx->lock.was_chosen_as_deadlock_victim.fetch_and(byte(~1)))
|
||||
trx->error_state= DB_DEADLOCK;
|
||||
else
|
||||
trx->error_state= DB_SUCCESS;
|
||||
|
||||
trx->error_state=
|
||||
trx->lock.was_chosen_as_deadlock_victim ? DB_DEADLOCK : DB_SUCCESS;
|
||||
return trx->error_state;
|
||||
}
|
||||
|
||||
@@ -1770,7 +1795,7 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
wait_lock->un_member.tab_lock.table->id <= DICT_FIELDS_ID);
|
||||
thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE)
|
||||
? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK);
|
||||
dberr_t error_state= DB_SUCCESS;
|
||||
trx->error_state= DB_SUCCESS;
|
||||
|
||||
mysql_mutex_lock(&lock_sys.wait_mutex);
|
||||
if (trx->lock.wait_lock)
|
||||
@@ -1778,23 +1803,28 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
if (Deadlock::check_and_resolve(trx))
|
||||
{
|
||||
ut_ad(!trx->lock.wait_lock);
|
||||
error_state= DB_DEADLOCK;
|
||||
trx->error_state= DB_DEADLOCK;
|
||||
goto end_wait;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* trx->lock.was_chosen_as_deadlock_victim can be changed before
|
||||
lock_sys.wait_mutex is acquired, so let's check it once more. */
|
||||
trx->error_state=
|
||||
trx->lock.was_chosen_as_deadlock_victim ? DB_DEADLOCK : DB_SUCCESS;
|
||||
goto end_wait;
|
||||
|
||||
}
|
||||
if (row_lock_wait)
|
||||
lock_sys.wait_start();
|
||||
|
||||
trx->error_state= DB_SUCCESS;
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
if (rpl)
|
||||
lock_wait_rpl_report(trx);
|
||||
#endif
|
||||
|
||||
trx->error_state= DB_SUCCESS;
|
||||
|
||||
while (trx->lock.wait_lock)
|
||||
{
|
||||
int err;
|
||||
@@ -1807,20 +1837,19 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
else
|
||||
err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex,
|
||||
&abstime);
|
||||
error_state= trx->error_state;
|
||||
switch (error_state) {
|
||||
switch (trx->error_state) {
|
||||
case DB_DEADLOCK:
|
||||
case DB_INTERRUPTED:
|
||||
break;
|
||||
default:
|
||||
ut_ad(error_state != DB_LOCK_WAIT_TIMEOUT);
|
||||
ut_ad(trx->error_state != DB_LOCK_WAIT_TIMEOUT);
|
||||
/* Dictionary transactions must ignore KILL, because they could
|
||||
be executed as part of a multi-transaction DDL operation,
|
||||
such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */
|
||||
if (!trx->dict_operation && trx_is_interrupted(trx))
|
||||
/* innobase_kill_query() can only set trx->error_state=DB_INTERRUPTED
|
||||
for any transaction that is attached to a connection. */
|
||||
error_state= DB_INTERRUPTED;
|
||||
trx->error_state= DB_INTERRUPTED;
|
||||
else if (!err)
|
||||
continue;
|
||||
#ifdef WITH_WSREP
|
||||
@@ -1828,7 +1857,7 @@ dberr_t lock_wait(que_thr_t *thr)
|
||||
#endif
|
||||
else
|
||||
{
|
||||
error_state= DB_LOCK_WAIT_TIMEOUT;
|
||||
trx->error_state= DB_LOCK_WAIT_TIMEOUT;
|
||||
lock_sys.timeouts++;
|
||||
}
|
||||
}
|
||||
@@ -1848,8 +1877,7 @@ end_wait:
|
||||
mysql_mutex_unlock(&lock_sys.wait_mutex);
|
||||
thd_wait_end(trx->mysql_thd);
|
||||
|
||||
trx->error_state= error_state;
|
||||
return error_state;
|
||||
return trx->error_state;
|
||||
}
|
||||
|
||||
|
||||
@@ -1862,7 +1890,7 @@ static void lock_wait_end(trx_t *trx)
|
||||
ut_ad(state == TRX_STATE_ACTIVE || state == TRX_STATE_PREPARED);
|
||||
ut_ad(trx->lock.wait_thr);
|
||||
|
||||
if (trx->lock.was_chosen_as_deadlock_victim.fetch_and(byte(~1)))
|
||||
if (trx->lock.was_chosen_as_deadlock_victim)
|
||||
{
|
||||
ut_ad(state == TRX_STATE_ACTIVE);
|
||||
trx->error_state= DB_DEADLOCK;
|
||||
@@ -3401,17 +3429,18 @@ lock_table_enqueue_waiting(
|
||||
ut_ad(trx->mutex_is_owner());
|
||||
ut_ad(!trx->dict_operation_lock_mode);
|
||||
|
||||
#ifdef WITH_WSREP
|
||||
if (trx->is_wsrep() && trx->lock.was_chosen_as_deadlock_victim) {
|
||||
return(DB_DEADLOCK);
|
||||
}
|
||||
#endif /* WITH_WSREP */
|
||||
|
||||
/* Enqueue the lock request that will wait to be granted */
|
||||
lock_table_create(table, mode | LOCK_WAIT, trx, c_lock);
|
||||
|
||||
trx->lock.wait_thr = thr;
|
||||
trx->lock.clear_deadlock_victim();
|
||||
/* Apart from Galera, only transactions that have waiting lock
|
||||
may be chosen as deadlock victims. Only one lock can be waited for at a
|
||||
time, and a transaction is associated with a single thread. That is why
|
||||
there must not be waiting lock requests if the transaction is deadlock
|
||||
victim and it is not WSREP. Galera transaction abort can be invoked
|
||||
from MDL acquisition code when the transaction does not have waiting
|
||||
lock, that's why we check only deadlock victim bit here. */
|
||||
ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
|
||||
|
||||
MONITOR_INC(MONITOR_TABLELOCK_WAIT);
|
||||
return(DB_LOCK_WAIT);
|
||||
@@ -3949,7 +3978,6 @@ released:
|
||||
mysql_mutex_unlock(&lock_sys.wait_mutex);
|
||||
}
|
||||
|
||||
trx->lock.was_chosen_as_deadlock_victim= false;
|
||||
trx->lock.n_rec_locks= 0;
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
@@ -5718,10 +5746,12 @@ dberr_t lock_sys_t::cancel(trx_t *trx, lock_t *lock)
|
||||
lock_sys.rd_lock(SRW_LOCK_CALL);
|
||||
mysql_mutex_lock(&lock_sys.wait_mutex);
|
||||
lock= trx->lock.wait_lock;
|
||||
if (!lock);
|
||||
else if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
|
||||
/* Even if waiting lock was cancelled while lock_sys.wait_mutex was
|
||||
unlocked, we need to return deadlock error if transaction was chosen
|
||||
as deadlock victim to rollback it */
|
||||
if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
|
||||
err= DB_DEADLOCK;
|
||||
else
|
||||
else if (lock)
|
||||
goto resolve_table_lock;
|
||||
}
|
||||
else
|
||||
@@ -5769,10 +5799,12 @@ retreat:
|
||||
lock_sys.wr_lock(SRW_LOCK_CALL);
|
||||
mysql_mutex_lock(&lock_sys.wait_mutex);
|
||||
lock= trx->lock.wait_lock;
|
||||
if (!lock);
|
||||
else if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
|
||||
/* Even if waiting lock was cancelled while lock_sys.wait_mutex was
|
||||
unlocked, we need to return deadlock error if transaction was chosen
|
||||
as deadlock victim to rollback it */
|
||||
if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
|
||||
err= DB_DEADLOCK;
|
||||
else
|
||||
else if (lock)
|
||||
goto resolve_record_lock;
|
||||
}
|
||||
else
|
||||
@@ -5850,6 +5882,7 @@ while holding a clustered index leaf page latch.
|
||||
lock request was released */
|
||||
dberr_t lock_trx_handle_wait(trx_t *trx)
|
||||
{
|
||||
DEBUG_SYNC_C("lock_trx_handle_wait_enter");
|
||||
if (trx->lock.was_chosen_as_deadlock_victim)
|
||||
return DB_DEADLOCK;
|
||||
if (!trx->lock.wait_lock)
|
||||
|
@@ -1287,43 +1287,6 @@ bool mtr_t::memo_contains(const fil_space_t& space, bool shared)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
/** If a stale adaptive hash index exists on the block, drop it. */
|
||||
ATTRIBUTE_COLD
|
||||
void mtr_t::defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type)
|
||||
{
|
||||
switch (fix_type) {
|
||||
default:
|
||||
ut_ad(fix_type == MTR_MEMO_BUF_FIX);
|
||||
/* We do not drop the adaptive hash index, because safely doing so
|
||||
would require acquiring exclusive block->page.lock, which could
|
||||
lead to hangs in some access paths. Those code paths should have
|
||||
no business accessing the adaptive hash index anyway. */
|
||||
break;
|
||||
case MTR_MEMO_PAGE_S_FIX:
|
||||
/* Temporarily release our S-latch. */
|
||||
block->page.lock.s_unlock();
|
||||
block->page.lock.x_lock();
|
||||
if (dict_index_t *index= block->index)
|
||||
if (index->freed())
|
||||
btr_search_drop_page_hash_index(block);
|
||||
block->page.lock.x_unlock();
|
||||
block->page.lock.s_lock();
|
||||
ut_ad(!block->page.is_read_fixed());
|
||||
break;
|
||||
case MTR_MEMO_PAGE_SX_FIX:
|
||||
block->page.lock.u_x_upgrade();
|
||||
if (dict_index_t *index= block->index)
|
||||
if (index->freed())
|
||||
btr_search_drop_page_hash_index(block);
|
||||
block->page.lock.x_u_downgrade();
|
||||
break;
|
||||
case MTR_MEMO_PAGE_X_FIX:
|
||||
btr_search_drop_page_hash_index(block);
|
||||
}
|
||||
}
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
|
||||
/** Upgrade U-latched pages to X */
|
||||
struct UpgradeX
|
||||
{
|
||||
@@ -1378,8 +1341,7 @@ void mtr_t::page_lock(buf_block_t *block, ulint rw_latch)
|
||||
ut_d(const auto state= block->page.state());
|
||||
ut_ad(state > buf_page_t::FREED);
|
||||
ut_ad(state > buf_page_t::WRITE_FIX || state < buf_page_t::READ_FIX);
|
||||
switch (rw_latch)
|
||||
{
|
||||
switch (rw_latch) {
|
||||
case RW_NO_LATCH:
|
||||
fix_type= MTR_MEMO_BUF_FIX;
|
||||
goto done;
|
||||
@@ -1405,10 +1367,8 @@ void mtr_t::page_lock(buf_block_t *block, ulint rw_latch)
|
||||
}
|
||||
|
||||
#ifdef BTR_CUR_HASH_ADAPT
|
||||
if (dict_index_t *index= block->index)
|
||||
if (index->freed())
|
||||
defer_drop_ahi(block, fix_type);
|
||||
#endif /* BTR_CUR_HASH_ADAPT */
|
||||
btr_search_drop_page_hash_index(block, true);
|
||||
#endif
|
||||
|
||||
done:
|
||||
ut_ad(state < buf_page_t::UNFIXED ||
|
||||
@@ -1638,8 +1598,10 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset)
|
||||
|
||||
if (is_logged())
|
||||
{
|
||||
m_memo.for_each_block_in_reverse
|
||||
(CIterate<MarkFreed>((MarkFreed{{space.id, offset}})));
|
||||
CIterate<MarkFreed> mf{MarkFreed{{space.id, offset}}};
|
||||
m_memo.for_each_block_in_reverse(mf);
|
||||
if (mf.functor.freed && !m_made_dirty)
|
||||
m_made_dirty= is_block_dirtied(mf.functor.freed);
|
||||
m_log.close(log_write<FREE_PAGE>({space.id, offset}, nullptr));
|
||||
}
|
||||
}
|
||||
|
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
size_t pending_io_count()
|
||||
{
|
||||
return (size_t)m_max_aio - m_cache.size();
|
||||
return m_cache.pos();
|
||||
}
|
||||
|
||||
tpool::task_group* get_task_group()
|
||||
|
@@ -399,11 +399,11 @@ static dberr_t trx_purge_free_segment(trx_rseg_t *rseg, fil_addr_t hdr_addr)
|
||||
mtr.commit();
|
||||
mtr.start();
|
||||
mtr.flag_modified();
|
||||
mtr.memo_push(rseg_hdr, MTR_MEMO_PAGE_X_FIX);
|
||||
mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
|
||||
rseg->latch.wr_lock(SRW_LOCK_CALL);
|
||||
rseg_hdr->page.lock.x_lock();
|
||||
block->page.lock.x_lock();
|
||||
mtr.memo_push(rseg_hdr, MTR_MEMO_PAGE_X_FIX);
|
||||
mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
|
||||
}
|
||||
|
||||
/* The page list may now be inconsistent, but the length field
|
||||
|
@@ -1859,9 +1859,9 @@ trx_undo_page_report_rename(trx_t* trx, const dict_table_t* table,
|
||||
byte* const start = block->page.frame + first_free;
|
||||
size_t len = strlen(table->name.m_name);
|
||||
const size_t fixed = 2 + 1 + 11 + 11 + 2;
|
||||
ut_ad(len <= NAME_LEN * 2 + 1);
|
||||
ut_ad(len <= NAME_CHAR_LEN * 5 * 2 + 1);
|
||||
/* The -10 is used in trx_undo_left() */
|
||||
compile_time_assert((NAME_LEN * 1) * 2 + fixed
|
||||
compile_time_assert(NAME_CHAR_LEN * 5 * 2 + fixed
|
||||
+ TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE
|
||||
< UNIV_PAGE_SIZE_MIN - 10 - FIL_PAGE_DATA_END);
|
||||
|
||||
|
@@ -135,6 +135,9 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There must not be partial rollback if transaction was chosen as deadlock
|
||||
victim. Galera transaction abort can be invoked during partial rollback. */
|
||||
ut_ad(!(lock.was_chosen_as_deadlock_victim & 1));
|
||||
ut_a(error_state == DB_SUCCESS);
|
||||
const undo_no_t limit= savept->least_undo_no;
|
||||
apply_online_log= false;
|
||||
@@ -211,6 +214,10 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
|
||||
case TRX_STATE_NOT_STARTED:
|
||||
trx->will_lock = false;
|
||||
ut_ad(trx->mysql_thd);
|
||||
/* Galera transaction abort can be invoked from MDL acquision
|
||||
code, so trx->lock.was_chosen_as_deadlock_victim can be set
|
||||
even if trx->state is TRX_STATE_NOT_STARTED. */
|
||||
ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
|
||||
#ifdef WITH_WSREP
|
||||
trx->wsrep= false;
|
||||
trx->lock.was_chosen_as_deadlock_victim= false;
|
||||
@@ -418,9 +425,6 @@ trx_rollback_to_savepoint_for_mysql_low(
|
||||
trx_mark_sql_stat_end(trx);
|
||||
|
||||
trx->op_info = "";
|
||||
#ifdef WITH_WSREP
|
||||
trx->lock.was_chosen_as_deadlock_victim = false;
|
||||
#endif
|
||||
return(err);
|
||||
}
|
||||
|
||||
|
@@ -1379,9 +1379,8 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr)
|
||||
wsrep= false;
|
||||
wsrep_commit_ordered(mysql_thd);
|
||||
}
|
||||
ut_ad(!(lock.was_chosen_as_deadlock_victim & byte(~2U)));
|
||||
lock.was_chosen_as_deadlock_victim= false;
|
||||
#endif /* WITH_WSREP */
|
||||
lock.was_chosen_as_deadlock_victim= false;
|
||||
}
|
||||
|
||||
void trx_t::commit_cleanup()
|
||||
|
@@ -23,13 +23,13 @@
|
||||
solution was needed than the one-to-one conversion table. To
|
||||
note a few, here is an example of a Czech sorting sequence:
|
||||
|
||||
co < hlaska < hláska < hlava < chlapec < krtek
|
||||
co < hlaska < hláska < hlava < chlapec < krtek
|
||||
|
||||
It because some of the rules are: double char 'ch' is sorted
|
||||
between 'h' and 'i'. Accented character 'á' (a with acute) is
|
||||
between 'h' and 'i'. Accented character 'á' (a with acute) is
|
||||
sorted after 'a' and before 'b', but only if the word is
|
||||
otherwise the same. However, because 's' is sorted before 'v'
|
||||
in hlava, the accentness of 'á' is overridden. There are many
|
||||
in hlava, the accentness of 'á' is overridden. There are many
|
||||
more rules.
|
||||
|
||||
This file defines functions my_strxfrm and my_strcoll for
|
||||
@@ -42,8 +42,9 @@
|
||||
passes, that's why we need four times more space for expanded
|
||||
string.
|
||||
|
||||
This file also contains the ISO-Latin-2 definitions of
|
||||
characters.
|
||||
The non-ASCII literal strings in this file are encoded
|
||||
in the iso-8859-2 / latin-2 character set
|
||||
(https://en.wikipedia.org/wiki/ISO/IEC_8859-2)
|
||||
|
||||
Author: (c) 1997--1998 Jan Pazdziora, adelton@fi.muni.cz
|
||||
Jan Pazdziora has a shared copyright for this code
|
||||
@@ -112,7 +113,7 @@ static const struct wordvalue doubles[] = {
|
||||
};
|
||||
|
||||
/*
|
||||
Unformal description of the algorithm:
|
||||
Informal description of the algorithm:
|
||||
|
||||
We walk the string left to right.
|
||||
|
||||
@@ -127,7 +128,7 @@ static const struct wordvalue doubles[] = {
|
||||
|
||||
End of pass is marked with value 1 on the output.
|
||||
|
||||
For each character, we read it's value from the table.
|
||||
For each character, we read its value from the table.
|
||||
|
||||
If the value is ignore (0), we go straight to the next character.
|
||||
|
||||
@@ -139,31 +140,6 @@ static const struct wordvalue doubles[] = {
|
||||
exists behind it, find its value.
|
||||
|
||||
We append 0 to the end.
|
||||
---
|
||||
Neformální popis algoritmu:
|
||||
|
||||
Procházíme řetězec zleva doprava.
|
||||
|
||||
Konec řetězce je předán buď jako parametr, nebo je to *p == 0.
|
||||
Toto je ošetřeno makrem IS_END.
|
||||
|
||||
Pokud jsme došli na konec řetězce při průchodu 0, nejdeme na
|
||||
začátek, ale na uloženou pozici, protože první a druhý průchod
|
||||
běží současně.
|
||||
|
||||
Konec vstupu (průchodu) označíme na výstupu hodnotou 1.
|
||||
|
||||
Pro každý znak řetězce načteme hodnotu z třídící tabulky.
|
||||
|
||||
Jde-li o hodnotu ignorovat (0), skočíme ihned na další znak..
|
||||
|
||||
Jde-li o hodnotu konec slova (2) a je to průchod 0 nebo 1,
|
||||
přeskočíme všechny další 0 -- 2 a prohodíme průchody.
|
||||
|
||||
Jde-li o kompozitní znak (255), otestujeme, zda následuje
|
||||
správný do dvojice, dohledáme správnou hodnotu.
|
||||
|
||||
Na konci připojíme znak 0
|
||||
*/
|
||||
|
||||
#define ADD_TO_RESULT(dest, len, totlen, value) \
|
||||
@@ -336,24 +312,23 @@ my_strnxfrm_czech(CHARSET_INFO *cs __attribute__((unused)),
|
||||
|
||||
|
||||
/*
|
||||
Neformální popis algoritmu:
|
||||
Informal description of the algorithm:
|
||||
|
||||
procházíme řetězec zleva doprava
|
||||
konec řetězce poznáme podle *p == 0
|
||||
pokud jsme došli na konec řetězce při průchodu 0, nejdeme na
|
||||
začátek, ale na uloženou pozici, protože první a druhý
|
||||
průchod běží současně
|
||||
konec vstupu (průchodu) označíme na výstupu hodnotou 1
|
||||
we pass the chain from left to right
|
||||
we know the end of the string by *p == 0
|
||||
if we reached the end of the string on transition 0, then we don't go to
|
||||
start, but to the saved position, because the first and second
|
||||
the passage runs concurrently
|
||||
we mark the end of the input (transition) with the value 1 on the output
|
||||
|
||||
načteme hodnotu z třídící tabulky
|
||||
jde-li o hodnotu ignorovat (0), skočíme na další průchod
|
||||
jde-li o hodnotu konec slova (2) a je to průchod 0 nebo 1,
|
||||
přeskočíme všechny další 0 -- 2 a prohodíme
|
||||
průchody
|
||||
jde-li o kompozitní znak (255), otestujeme, zda následuje
|
||||
správný do dvojice, dohledáme správnou hodnotu
|
||||
then we load the value from the sorting table
|
||||
if the value is ignore (0), we jump to the next pass
|
||||
if the value is the end of the word (2) and it is a 0 or 1 transition,
|
||||
we skip all the other 0 -- 2 and switch transitions
|
||||
if it is a composite character (255), we test whether it follows
|
||||
correct to the pair, we find the correct value
|
||||
|
||||
na konci připojíme znak 0
|
||||
then we add the character 0 at the end
|
||||
*/
|
||||
|
||||
|
||||
|
@@ -504,19 +504,19 @@ struct charset_info_st my_charset_latin1_nopad=
|
||||
*
|
||||
* The modern sort order is used, where:
|
||||
*
|
||||
* 'ä' -> "ae"
|
||||
* 'ö' -> "oe"
|
||||
* 'ü' -> "ue"
|
||||
* 'ß' -> "ss"
|
||||
* 'ä' -> "ae"
|
||||
* 'ö' -> "oe"
|
||||
* 'ü' -> "ue"
|
||||
* 'ß' -> "ss"
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* This is a simple latin1 mapping table, which maps all accented
|
||||
* characters to their non-accented equivalents. Note: in this
|
||||
* table, 'ä' is mapped to 'A', 'ÿ' is mapped to 'Y', etc. - all
|
||||
* table, 'ä' is mapped to 'A', 'ÿ' is mapped to 'Y', etc. - all
|
||||
* accented characters except the following are treated the same way.
|
||||
* Ü, ü, Ö, ö, Ä, ä
|
||||
* Ü, ü, Ö, ö, Ä, ä
|
||||
*/
|
||||
|
||||
static const uchar sort_order_latin1_de[] = {
|
||||
@@ -582,7 +582,7 @@ static const uchar combo2map[]={
|
||||
my_strnxfrm_latin_de() on both strings and compared the result strings.
|
||||
|
||||
This means that:
|
||||
Ä must also matches ÁE and Aè, because my_strxn_frm_latin_de() will convert
|
||||
Ä must also matches ÁE and Aè, because my_strxn_frm_latin_de() will convert
|
||||
both to AE.
|
||||
|
||||
The other option would be to not do any accent removal in
|
||||
@@ -708,7 +708,7 @@ void my_hash_sort_latin1_de(CHARSET_INFO *cs __attribute__((unused)),
|
||||
|
||||
/*
|
||||
Remove end space. We have to do this to be able to compare
|
||||
'AE' and 'Ä' as identical
|
||||
'AE' and 'Ä' as identical
|
||||
*/
|
||||
end= skip_trailing_space(key, len);
|
||||
|
||||
|
@@ -40,79 +40,121 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
|
||||
namespace tpool
|
||||
{
|
||||
|
||||
enum cache_notification_mode
|
||||
{
|
||||
NOTIFY_ONE,
|
||||
NOTIFY_ALL
|
||||
};
|
||||
|
||||
/**
|
||||
Generic "pointer" cache of a fixed size
|
||||
with fast put/get operations.
|
||||
|
||||
Compared to STL containers, is faster/does not
|
||||
do allocations. However, put() operation will wait
|
||||
if there is no free items.
|
||||
Compared to STL containers,e.g stack or queue
|
||||
is faster/does not do allocations.
|
||||
|
||||
However, get() operation will wait if there is no free items.
|
||||
|
||||
We assume that put() will only put back the elements that
|
||||
were retrieved previously with get().
|
||||
*/
|
||||
template<typename T> class cache
|
||||
{
|
||||
/** Protects updates of m_pos and m_cache members */
|
||||
std::mutex m_mtx;
|
||||
|
||||
/**
|
||||
Notify waiting threads about "cache full" or "cache not empty" conditions
|
||||
@see get() and wait()
|
||||
*/
|
||||
std::condition_variable m_cv;
|
||||
std::vector<T> m_base;
|
||||
|
||||
/** Cached items vector.Does not change after construction */
|
||||
std::vector<T> m_base;
|
||||
|
||||
/**
|
||||
Pointers to cached items. Protected by m_mtx. Does not grow after
|
||||
construction. Elements in position [0,m_pos-1] are "borrowed",
|
||||
elements in position [m_pos,capacity()-1] are "free"
|
||||
*/
|
||||
std::vector<T*> m_cache;
|
||||
cache_notification_mode m_notification_mode;
|
||||
|
||||
/** Number of threads waiting for "cache full" condition (s. wait())
|
||||
Protected by m_mtx */
|
||||
int m_waiters;
|
||||
|
||||
/** Current cache size. Protected by m_mtx*/
|
||||
size_t m_pos;
|
||||
|
||||
private:
|
||||
|
||||
inline size_t capacity()
|
||||
{
|
||||
return m_base.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if cache is full (no items are borrowed)
|
||||
*/
|
||||
bool is_full()
|
||||
{
|
||||
return m_cache.size() == m_base.size();
|
||||
return m_pos == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if cache is empty (all items are borrowed)
|
||||
*/
|
||||
bool is_empty()
|
||||
{
|
||||
return m_pos == capacity();
|
||||
}
|
||||
|
||||
public:
|
||||
cache(size_t count, cache_notification_mode mode= tpool::cache_notification_mode::NOTIFY_ALL):
|
||||
m_mtx(), m_cv(), m_base(count),m_cache(count), m_notification_mode(mode),m_waiters()
|
||||
/**
|
||||
Constructor
|
||||
@param size - maximum number of items in cache
|
||||
*/
|
||||
cache(size_t size) : m_mtx(), m_cv(), m_base(size), m_cache(size),
|
||||
m_waiters(), m_pos(0)
|
||||
{
|
||||
for(size_t i = 0 ; i < count; i++)
|
||||
m_cache[i]=&m_base[i];
|
||||
for(size_t i= 0 ; i < size; i++)
|
||||
m_cache[i]= &m_base[i];
|
||||
}
|
||||
|
||||
T* get(bool blocking=true)
|
||||
/**
|
||||
Retrieve an item from cache. Waits for free item, if cache is
|
||||
currently empty.
|
||||
@return borrowed item
|
||||
*/
|
||||
T* get()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
if (blocking)
|
||||
{
|
||||
while(m_cache.empty())
|
||||
m_cv.wait(lk);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_cache.empty())
|
||||
return nullptr;
|
||||
}
|
||||
T* ret = m_cache.back();
|
||||
m_cache.pop_back();
|
||||
return ret;
|
||||
while(is_empty())
|
||||
m_cv.wait(lk);
|
||||
assert(m_pos < capacity());
|
||||
// return last element
|
||||
return m_cache[m_pos++];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Put back an item to cache.
|
||||
@param item - item to put back
|
||||
*/
|
||||
void put(T *ele)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
m_cache.push_back(ele);
|
||||
if (m_notification_mode == NOTIFY_ONE)
|
||||
m_cv.notify_one();
|
||||
else if(m_cache.size() == 1)
|
||||
m_cv.notify_all(); // Signal cache is not empty
|
||||
else if(m_waiters && is_full())
|
||||
m_cv.notify_all(); // Signal cache is full
|
||||
assert(!is_full());
|
||||
// put element to the logical end of the array
|
||||
m_cache[--m_pos] = ele;
|
||||
|
||||
/* Notify waiters when the cache becomes
|
||||
not empty, or when it becomes full */
|
||||
if (m_pos == 1 || (m_waiters && is_full()))
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
/** Check if pointer represents cached element */
|
||||
bool contains(T* ele)
|
||||
{
|
||||
return ele >= &m_base[0] && ele <= &m_base[m_base.size() -1];
|
||||
// No locking required, m_base does not change after construction.
|
||||
return ele >= &m_base[0] && ele <= &m_base[capacity() - 1];
|
||||
}
|
||||
|
||||
/* Wait until cache is full.*/
|
||||
/** Wait until cache is full.*/
|
||||
void wait()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_mtx);
|
||||
@@ -122,9 +164,13 @@ public:
|
||||
m_waiters--;
|
||||
}
|
||||
|
||||
TPOOL_SUPPRESS_TSAN size_t size()
|
||||
/**
|
||||
@return approximate number of "borrowed" items.
|
||||
A "dirty" read, not used in any critical functionality.
|
||||
*/
|
||||
TPOOL_SUPPRESS_TSAN size_t pos()
|
||||
{
|
||||
return m_cache.size();
|
||||
return m_pos;
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user