1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-09 22:24:09 +03:00

Merge 10.6 into 10.7

This commit is contained in:
Marko Mäkelä
2022-08-30 13:02:42 +03:00
52 changed files with 1053 additions and 332 deletions

View File

@@ -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. */ @return true on success. */
bool get_mysql_vars(MYSQL *connection) 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() bool write_backup_config_file()
{ {
int rc= backup_file_printf("backup-my.cnf", int rc= backup_file_printf("backup-my.cnf",
"# This MySQL options file was generated by innobackupex.\n\n" "# This options file was generated by innobackupex.\n\n"
"# The MySQL server\n" "# The server\n"
"[mysqld]\n" "[mysqld]\n"
"innodb_checksum_algorithm=%s\n" "innodb_checksum_algorithm=%s\n"
"innodb_data_file_path=%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. */ @return true on success. */
void void
backup_cleanup() backup_cleanup()

View File

@@ -6265,22 +6265,28 @@ static bool check_all_privileges()
} }
/* KILL ... */ /* KILL ... */
if ((!opt_no_lock && (opt_kill_long_queries_timeout || opt_lock_ddl_per_table)) if (!opt_no_lock && (opt_kill_long_queries_timeout || opt_kill_long_query_type)) {
/* START SLAVE SQL_THREAD */
/* STOP SLAVE SQL_THREAD */
|| opt_safe_slave_backup) {
check_result |= check_privilege( check_result |= check_privilege(
granted_privileges, 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); PRIVILEGE_WARNING);
} }
/* SHOW MASTER STATUS */ /* SHOW MASTER STATUS */
/* SHOW SLAVE STATUS */ /* SHOW SLAVE STATUS */
if (opt_galera_info || opt_slave_info if (opt_galera_info || opt_slave_info
|| (opt_no_lock && opt_safe_slave_backup)) { || opt_safe_slave_backup) {
check_result |= check_privilege(granted_privileges, check_result |= check_privilege(granted_privileges,
"REPLICATION CLIENT", "*", "*", "SLAVE MONITOR", "*", "*",
PRIVILEGE_WARNING); PRIVILEGE_WARNING);
} }

View File

@@ -174,7 +174,7 @@ INSERT INTO global_suppressions VALUES
/* Added 2009-08-XX after fixing Bug #42408 */ /* 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"), ("Backup: Operation aborted"),
("Restore: Operation aborted"), ("Restore: Operation aborted"),
("Restore: The grant .* was skipped because the user does not exist"), ("Restore: The grant .* was skipped because the user does not exist"),

View File

@@ -3042,6 +3042,122 @@ a
3 3
2 2
drop table t1,t2,t3; 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 # End of 10.3 tests
# #
# MDEV-19134: EXISTS() slower if ORDER BY is defined # MDEV-19134: EXISTS() slower if ORDER BY is defined

View File

@@ -2481,6 +2481,80 @@ eval $q3;
drop table t1,t2,t3; 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 # End of 10.3 tests
--echo # --echo #

View File

@@ -14,7 +14,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -32,7 +32,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -50,7 +50,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -68,7 +68,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -86,7 +86,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -104,7 +104,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -122,7 +122,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -140,7 +140,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -158,7 +158,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -176,7 +176,7 @@ connection node_1_u;
begin; begin;
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
connection node_1_i; 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); insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -202,7 +202,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -220,7 +220,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -238,7 +238,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -256,7 +256,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -274,7 +274,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -292,7 +292,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -310,7 +310,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -328,7 +328,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -346,7 +346,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';
@@ -364,7 +364,7 @@ connection node_1_u;
begin; begin;
execute upd; execute upd;
connection node_1_i; 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; execute ins1;
connection node_1; connection node_1;
set debug_sync='now WAIT_FOR ins_waiting'; set debug_sync='now WAIT_FOR ins_waiting';

View File

@@ -72,7 +72,7 @@ while($counter > 0)
update user set j = j + 1 WHERE id > 0; update user set j = j + 1 WHERE id > 0;
--connection node_1_i --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); send insert into user_session(id,fk1,fk2) values (2, 2, 2);
--connection node_1 --connection node_1
@@ -126,7 +126,7 @@ while($counter > 0)
#update user set j = j + 1 WHERE id > 0; #update user set j = j + 1 WHERE id > 0;
--connection node_1_i --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; send execute ins1;
--connection node_1 --connection node_1

View File

@@ -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; DELETE FROM t WHERE b = 20;
connect con_ins_1,localhost,root,,; connect con_ins_1,localhost,root,,;
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished'; 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'; SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
INSERT INTO t VALUES(10, 20); INSERT INTO t VALUES(10, 20);
connect con_del_2,localhost,root,,; connect con_del_2,localhost,root,,;
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked'; 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; DELETE FROM t WHERE b = 20;
connection default; connection default;
SET DEBUG_SYNC = 'now WAIT_FOR second_del_locked'; SET DEBUG_SYNC = 'now WAIT_FOR second_del_locked';

View 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;

View File

@@ -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 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`.`##################################################` CREATE TABLE `d255`.`##################################################`
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; (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 DATABASE `d255`;
DROP TABLE t;
# End of 10.3 tests # End of 10.3 tests

View File

@@ -38,7 +38,7 @@ select f1, f2 from t2 for update;
f1 f2 f1 f2
1 2 1 2
connection default; 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; update t1 set f1 = 10 where f1 = 2;
connection con1; connection con1;
set debug_sync='now WAIT_FOR upd_waiting'; set debug_sync='now WAIT_FOR upd_waiting';
@@ -97,7 +97,7 @@ select f1, f2 from t2 for update;
f1 f2 f1 f2
1 91 1 91
connection default; 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; update t1 set f2 = 28 where f2 = 91;
connection con1; connection con1;
set debug_sync='now WAIT_FOR upd_waiting'; set debug_sync='now WAIT_FOR upd_waiting';
@@ -164,7 +164,7 @@ select f1 from t3 for update;
f1 f1
2 2
connection default; 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; update t1 set f1 = 10 where f1 = 2;
connection con1; connection con1;
set debug_sync='now WAIT_FOR upd_waiting'; set debug_sync='now WAIT_FOR upd_waiting';
@@ -253,7 +253,7 @@ select f1 from t3 for update;
f1 f1
2 2
connection default; 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; update t1 set f2 = 28 where f2 = 91;
connection con1; connection con1;
set debug_sync='now WAIT_FOR upd_waiting'; set debug_sync='now WAIT_FOR upd_waiting';

View File

@@ -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,,) --connect(con_ins_1,localhost,root,,)
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished'; SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished';
# It's supposed the following INSERT will be suspended just after # 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 previous DELETE commits. ib_after_row_insert will be executed
# after the INSERT is woken up. The previous DELETE will wait for # 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. # 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 # 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. # 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'; SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
--send INSERT INTO t VALUES(10, 20) --send INSERT INTO t VALUES(10, 20)
--connect(con_del_2,localhost,root,,) --connect(con_del_2,localhost,root,,)
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked'; 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 # 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 # committed, it will still be locked by the next INSERT on delete-marked

View 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

View File

@@ -38,8 +38,40 @@ eval CREATE TABLE `$d255`.`_$d250`
--replace_result $d255 d255 --replace_result $d255 d255
eval CREATE TABLE `$d255`.`$d250` eval CREATE TABLE `$d255`.`$d250`
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB; (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 --replace_result $d255 d255
eval DROP DATABASE `$d255`; eval DROP DATABASE `$d255`;
DROP TABLE t;
--echo # End of 10.3 tests --echo # End of 10.3 tests

View File

@@ -28,7 +28,7 @@ start transaction;
select f1, f2 from t2 for update; select f1, f2 from t2 for update;
connection default; 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; send update t1 set f1 = 10 where f1 = 2;
connection con1; connection con1;
@@ -72,7 +72,7 @@ start transaction;
select f1, f2 from t2 for update; select f1, f2 from t2 for update;
connection default; 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; send update t1 set f2 = 28 where f2 = 91;
connection con1; connection con1;
@@ -120,7 +120,7 @@ start transaction;
select f1 from t3 for update; select f1 from t3 for update;
connection default; 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; send update t1 set f1 = 10 where f1 = 2;
connection con1; connection con1;
@@ -183,7 +183,7 @@ start transaction;
select f1 from t3 for update; select f1 from t3 for update;
connection default; 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; send update t1 set f2 = 28 where f2 = 91;
connection con1; connection con1;

View File

@@ -1,4 +1,5 @@
INSTALL PLUGIN simple_parser SONAME 'mypluglib'; INSTALL PLUGIN simple_parser SONAME 'mypluglib';
FLUSH TABLES;
# Test Part 1: Grammar Test # Test Part 1: Grammar Test
CREATE TABLE articles ( CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, 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'), ('1001 MySQL Tricks','How to use full-text search engine'),
('Go MySQL Tricks','How to use full text search engine'); ('Go MySQL Tricks','How to use full text search engine');
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('mysql'); MATCH(title, body) AGAINST('mysql') ORDER BY id;
id title body id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ... 1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ... 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'); ('Go MySQL Tricks','How to use full text search engine');
ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser; ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('mysql'); MATCH(title, body) AGAINST('mysql') ORDER BY id;
id title body id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ... 1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ... 2 How To Use MySQL Well After you went through a ...
@@ -88,21 +89,23 @@ MATCH(title, body) AGAINST('full text');
id title body id title body
5 Go MySQL Tricks How to use full text search engine 5 Go MySQL Tricks How to use full text search engine
SELECT * FROM articles WHERE 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 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 4 1001 MySQL Tricks How to use full-text search engine
5 Go 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 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 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 ... 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 ... 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 SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('"mysql database"' IN BOOLEAN MODE); MATCH(title, body) AGAINST('"mysql database"' IN BOOLEAN MODE);
id title body id title body
@@ -138,27 +141,27 @@ INSERT INTO articles (title, body) VALUES
('Go MariaDB Tricks','How to use full text search engine'); ('Go MariaDB Tricks','How to use full text search engine');
# restart # restart
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('MySQL'); MATCH(title, body) AGAINST('MySQL') ORDER BY id;
id title body id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ... 1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ... 2 How To Use MySQL Well After you went through a ...
3 Optimizing MySQL In this tutorial we will show ... 3 Optimizing MySQL In this tutorial we will show ...
4 1001 MySQL Tricks How to use full-text search engine 4 1001 MySQL Tricks How to use full-text search engine
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('tutorial'); MATCH(title, body) AGAINST('tutorial') ORDER BY id;
id title body id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ... 1 MySQL Tutorial DBMS stands for MySQL DataBase ...
3 Optimizing MySQL In this tutorial we will show ... 3 Optimizing MySQL In this tutorial we will show ...
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('Tricks'); MATCH(title, body) AGAINST('Tricks') ORDER BY id;
id title body id title body
4 1001 MySQL 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 5 Go MariaDB Tricks How to use full text search engine
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('full text search'); MATCH(title, body) AGAINST('full text search') ORDER BY id;
id title body 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 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; SELECT COUNT(*) FROM articles;
COUNT(*) COUNT(*)
5 5
@@ -186,7 +189,8 @@ UNINSTALL PLUGIN simple_parser;
Warnings: Warnings:
Warning 1620 Plugin is busy and will be uninstalled on shutdown Warning 1620 Plugin is busy and will be uninstalled on shutdown
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('mysql'); MATCH(title, body) AGAINST('mysql')
ORDER BY id;
id title body id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ... 1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ... 2 How To Use MySQL Well After you went through a ...

View File

@@ -6,6 +6,9 @@
# Install fts parser plugin # Install fts parser plugin
INSTALL PLUGIN simple_parser SONAME 'mypluglib'; 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 -- echo # Test Part 1: Grammar Test
# Create a myisam table and alter it to innodb table # Create a myisam table and alter it to innodb table
CREATE TABLE articles ( CREATE TABLE articles (
@@ -52,7 +55,7 @@ INSERT INTO articles (title, body) VALUES
# Simple term search # Simple term search
SELECT * FROM articles WHERE 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 # Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
@@ -90,7 +93,7 @@ ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
# Simple term search # Simple term search
SELECT * FROM articles WHERE 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 # Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
@@ -105,10 +108,12 @@ SELECT * FROM articles WHERE
# Test query expansion # Test query expansion
SELECT * FROM articles WHERE 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 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 # No result here, we get '"mysql' 'database"' by simple parser
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
@@ -152,13 +157,13 @@ INSERT INTO articles (title, body) VALUES
--source include/restart_mysqld.inc --source include/restart_mysqld.inc
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('MySQL'); MATCH(title, body) AGAINST('MySQL') ORDER BY id;
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('tutorial'); MATCH(title, body) AGAINST('tutorial') ORDER BY id;
SELECT * FROM articles WHERE SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('Tricks'); MATCH(title, body) AGAINST('Tricks') ORDER BY id;
SELECT * FROM articles WHERE 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; SELECT COUNT(*) FROM articles;
INSERT INTO articles (title, body) VALUES ('111', '1234 1234 1234'); INSERT INTO articles (title, body) VALUES ('111', '1234 1234 1234');
@@ -195,7 +200,8 @@ UNINSTALL PLUGIN simple_parser;
# Simple term search # Simple term search
SELECT * FROM articles WHERE 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 # Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE SELECT * FROM articles WHERE

View File

@@ -3,4 +3,12 @@ FOUND 1 /missing required privilege RELOAD/ in backup.log
FOUND 1 /missing required privilege PROCESS/ in backup.log FOUND 1 /missing required privilege PROCESS/ in backup.log
FOUND 1 /GRANT USAGE ON/ in backup.log FOUND 1 /GRANT USAGE ON/ in backup.log
GRANT RELOAD, PROCESS on *.* to backup@localhost; 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; DROP USER backup@localhost;

View File

@@ -25,7 +25,62 @@ GRANT RELOAD, PROCESS on *.* to backup@localhost;
--disable_result_log --disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --target-dir=$targetdir; exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --target-dir=$targetdir;
--enable_result_log --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; DROP USER backup@localhost;
# Cleanup
rmdir $targetdir;

View File

@@ -191,6 +191,20 @@ ERROR 23000: Duplicate entry '1-2020-03-01-2020-03-02' for key 'PRIMARY'
alter table t1 add system versioning; alter table t1 add system versioning;
drop table t1; 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 # MDEV-21941 RENAME doesn't work for system time or period fields
# #
create or replace table t1 ( create or replace table t1 (

View File

@@ -152,6 +152,29 @@ alter table t1 add system versioning;
# cleanup # cleanup
drop table t1; 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 #
--echo # MDEV-21941 RENAME doesn't work for system time or period fields --echo # MDEV-21941 RENAME doesn't work for system time or period fields
--echo # --echo #

View File

@@ -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

View 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

View File

@@ -92,7 +92,7 @@ extern "C" FILE *my_win_popen(const char *cmd, const char *mode)
goto error; goto error;
break; break;
default: default:
/* Unknown mode, éxpected "r", "rt", "w", "wt" */ /* Unknown mode, expected "r", "rt", "w", "wt" */
abort(); abort();
} }
if (!SetHandleInformation(parent_pipe_end, HANDLE_FLAG_INHERIT, 0)) if (!SetHandleInformation(parent_pipe_end, HANDLE_FLAG_INHERIT, 0))

View File

@@ -392,7 +392,8 @@ bool Item_subselect::mark_as_eliminated_processor(void *arg)
bool Item_subselect::eliminate_subselect_processor(void *arg) bool Item_subselect::eliminate_subselect_processor(void *arg)
{ {
unit->item= NULL; unit->item= NULL;
unit->exclude(); if (!unit->is_excluded())
unit->exclude();
eliminated= TRUE; eliminated= TRUE;
return FALSE; return FALSE;
} }

View File

@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates. /* 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 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 it under the terms of the GNU General Public License as published by
@@ -1937,10 +1937,17 @@ class Grant_tables
public: public:
Grant_tables() : p_user_table(&m_user_table_json) { } 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"); DBUG_ENTER("Grant_tables::build_table_list");
TABLE_LIST tables[USER_TABLE+1], *first= NULL;
DBUG_ASSERT(which_tables); /* At least one table must be opened. */ 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; tl->updating= lock_type >= TL_FIRST_WRITE;
if (i >= FIRST_OPTIONAL_TABLE) if (i >= FIRST_OPTIONAL_TABLE)
tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
tl->next_global= tl->next_local= first; tl->next_global= tl->next_local= *ptr_first;
first= tl; *ptr_first= tl;
} }
else else
tl->table= NULL; 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; uint counter;
int res= really_open(thd, first, &counter); int res= really_open(thd, first, &counter);
@@ -2041,6 +2059,48 @@ class Grant_tables
inline const Roles_mapping_table& roles_mapping_table() const inline const Roles_mapping_table& roles_mapping_table() const
{ return m_roles_mapping_table; } { 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: private:
/* Before any operation is possible on grant tables, they must be opened. /* 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:"); DBUG_ENTER("Grant_tables::really_open:");
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
if (tables->lock_type >= TL_FIRST_WRITE && if (rpl_ignore_tables(thd, tables))
thd->slave_thread && !thd->spcont)
{ {
/* DBUG_RETURN(1);
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);
} }
#endif #endif
if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT)) 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) const char *role)
{ {
DBUG_ENTER("acl_check_set_default_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) || DBUG_RETURN(check_alter_user(thd, host, user) ||
check_user_can_set_role(thd, user, host, NULL, role, NULL)); check_user_can_set_role(thd, user, host, NULL, role, NULL));
} }

View File

@@ -36,6 +36,7 @@
#include "sql_limit.h" // Select_limit_counters #include "sql_limit.h" // Select_limit_counters
#include "json_table.h" // Json_table_column #include "json_table.h" // Json_table_column
#include "sql_schema.h" #include "sql_schema.h"
#include "table.h"
/* Used for flags of nesting constructs */ /* Used for flags of nesting constructs */
#define SELECT_NESTING_MAP_SIZE 64 #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) 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) if (lex_string_cmp(system_charset_info, &start, &end) == 0)
{ {
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str); my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str);

View File

@@ -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, length= build_table_filename(to, sizeof(to) - 1, new_db->str,
new_name->str, "", flags & FN_TO_IS_TMP); new_name->str, "", flags & FN_TO_IS_TMP);
// Check if we hit FN_REFLEN bytes along with file extension. // 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); my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(to)-1, to);
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);

View File

@@ -4926,7 +4926,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
int int
rename_file_ext(const char * from,const char * to,const char * ext) 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(from_b,from,ext,NullS);
(void) strxmov(to_b,to,ext,NullS); (void) strxmov(to_b,to,ext,NullS);
return mysql_file_rename(key_file_frm, from_b, to_b, MYF(0)); 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 Checks whether a table is intact. Should be done *just* after the table has
been opened. been opened.

View File

@@ -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); void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
bool check_db_name(LEX_STRING *db); bool check_db_name(LEX_STRING *db);
bool check_column_name(const char *name); 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); 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); int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field); char *get_field(MEM_ROOT *mem, Field *field);

View File

@@ -40,6 +40,8 @@ struct Compare_identifiers
{ {
int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const 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(a.str[a.length] == 0);
DBUG_ASSERT(b.str[b.length] == 0); DBUG_ASSERT(b.str[b.length] == 0);
return my_strcasecmp(system_charset_info, a.str, b.str); return my_strcasecmp(system_charset_info, a.str, b.str);

View File

@@ -642,7 +642,6 @@ bool DOMNODELIST::DropItem(PGLOBAL g, int n)
if (Listp == NULL || Listp->length < n) if (Listp == NULL || Listp->length < n)
return true; return true;
//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set'
return false; return false;
} // end of DeleteItem } // end of DeleteItem

View File

@@ -530,8 +530,7 @@ btr_page_alloc_low(
mtr->u_lock_register(savepoint); mtr->u_lock_register(savepoint);
root->page.lock.u_lock(); root->page.lock.u_lock();
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (root->index) btr_search_drop_page_hash_index(root, true);
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
#endif #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); mtr->u_lock_register(savepoint);
root->page.lock.u_lock(); root->page.lock.u_lock();
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (root->index) btr_search_drop_page_hash_index(root, true);
mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
#endif #endif
} }
err= fseg_free_page(&root->page.frame[blob || err= fseg_free_page(&root->page.frame[blob ||

View File

@@ -278,10 +278,10 @@ latch_block:
latch_leaves->blocks[1] = block; latch_leaves->blocks[1] = block;
} }
mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
block->page.fix(); block->page.fix();
block->page.lock.x_lock(); block->page.lock.x_lock();
mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(block)); ut_ad(!btr_search_check_marked_free_index(block));
#endif #endif
@@ -7017,10 +7017,11 @@ btr_store_big_rec_extern_fields(
mtr.start(); mtr.start();
index->set_modified(mtr); index->set_modified(mtr);
mtr.set_log_mode_sub(*btr_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.fix();
rec_block->page.lock.x_lock(); rec_block->page.lock.x_lock();
mtr.memo_push(rec_block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(rec_block)); ut_ad(!btr_search_check_marked_free_index(rec_block));
#endif #endif
@@ -7396,9 +7397,10 @@ skip_free:
/* The buffer pool block containing the BLOB pointer is /* The buffer pool block containing the BLOB pointer is
exclusively latched by local_mtr. To satisfy some design exclusively latched by local_mtr. To satisfy some design
constraints, we must recursively latch it in mtr as well. */ constraints, we must recursively latch it in mtr as well. */
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
block->fix(); block->fix();
block->page.lock.x_lock(); block->page.lock.x_lock();
mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(block)); ut_ad(!btr_search_check_marked_free_index(block));
#endif #endif

View File

@@ -1292,10 +1292,6 @@ void btr_search_drop_page_hash_index(buf_block_t* block,
rec_offs* offsets; rec_offs* offsets;
retry: 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) { if (!block->index) {
return; return;
} }

View File

@@ -2815,12 +2815,10 @@ get_latch:
goto page_id_mismatch; goto page_id_mismatch;
} }
get_latch_valid: 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); 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; break;
case RW_SX_LATCH: case RW_SX_LATCH:
fix_type = MTR_MEMO_PAGE_SX_FIX; fix_type = MTR_MEMO_PAGE_SX_FIX;

View File

@@ -1098,7 +1098,7 @@ dict_create_table_step(
} }
if (node->state == TABLE_ADD_TO_CACHE) { 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(); node->table->add_to_cache();
err = DB_SUCCESS; err = DB_SUCCESS;

View File

@@ -1665,7 +1665,7 @@ dict_table_rename_in_cache(
in UTF-8 charset. The variable fkid here is used in UTF-8 charset. The variable fkid here is used
to store foreign key constraint name in charset to store foreign key constraint name in charset
my_charset_filename for comparison further below. */ 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 /* The old table name in my_charset_filename is stored
in old_name_cs_filename */ 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( const bool on_tmp = dict_table_t::is_temporary_name(
fkid); fkid);
@@ -3522,10 +3523,11 @@ dict_table_get_highest_foreign_id(
for (dict_foreign_set::iterator it = table->foreign_set.begin(); for (dict_foreign_set::iterator it = table->foreign_set.begin();
it != table->foreign_set.end(); it != table->foreign_set.end();
++it) { ++it) {
char fkid[MAX_TABLE_NAME_LEN+20]; char fkid[MAX_TABLE_NAME_LEN * 2 + 20];
foreign = *it; 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 /* Convert foreign key identifier on dictionary memory
cache to filename charset. */ cache to filename charset. */
innobase_convert_to_filename_charset( innobase_convert_to_filename_charset(

View File

@@ -245,7 +245,8 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
ut_ad(ib_vector_is_empty(autoinc_locks)); ut_ad(ib_vector_is_empty(autoinc_locks));
mem_heap_empty(lock.lock_heap); mem_heap_empty(lock.lock_heap);
lock.table_locks.clear(); 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; lock.n_rec_locks= 0;
while (dict_table_t *table= UT_LIST_GET_FIRST(lock.evicted_tables)) while (dict_table_t *table= UT_LIST_GET_FIRST(lock.evicted_tables))
{ {

View File

@@ -14826,9 +14826,11 @@ ha_innobase::info_low(
stats.index_file_length stats.index_file_length
= ulonglong(stat_sum_of_other_index_sizes) = ulonglong(stat_sum_of_other_index_sizes)
* size; * size;
space->s_lock();
stats.delete_length = 1024 stats.delete_length = 1024
* fsp_get_available_space_in_free_extents( * fsp_get_available_space_in_free_extents(
*space); *space);
space->s_unlock();
} }
stats.check_time = 0; stats.check_time = 0;
stats.mrr_length_per_rec= (uint)ref_length + 8; // 8 = max(sizeof(void *)); stats.mrr_length_per_rec= (uint)ref_length + 8; // 8 = max(sizeof(void *));

View File

@@ -622,12 +622,6 @@ public:
PAGE_FLUSH_SYNC 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: private:
/** Log a write of a byte string to a page. /** Log a write of a byte string to a page.
@param block buffer page @param block buffer page

View File

@@ -339,27 +339,11 @@ struct trx_lock_t
/** lock wait start time */ /** lock wait start time */
Atomic_relaxed<my_hrtime_t> suspend_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; /** 2=high priority WSREP thread has marked this trx to abort;
1=another transaction chose this as a victim in deadlock resolution. */ 1=another transaction chose this as a victim in deadlock resolution. */
Atomic_relaxed<byte> was_chosen_as_deadlock_victim; 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. */ /** Flag the lock owner as a victim in Galera conflict resolution. */
void set_wsrep_victim() void set_wsrep_victim()
{ {
@@ -373,7 +357,17 @@ struct trx_lock_t
was_chosen_as_deadlock_victim.fetch_or(2); was_chosen_as_deadlock_victim.fetch_or(2);
# endif # 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 */ /** Next available rec_pool[] entry */
byte rec_cached; byte rec_cached;

View File

@@ -44,6 +44,7 @@ Created 5/7/1996 Heikki Tuuri
#include "row0vers.h" #include "row0vers.h"
#include "pars0pars.h" #include "pars0pars.h"
#include "srv0mon.h" #include "srv0mon.h"
#include "scope.h"
#include <set> #include <set>
@@ -1275,6 +1276,14 @@ lock_rec_enqueue_waiting(
trx_t* trx = thr_get_trx(thr); trx_t* trx = thr_get_trx(thr);
ut_ad(xtest() || trx->mutex_is_owner()); ut_ad(xtest() || trx->mutex_is_owner());
ut_ad(!trx->dict_operation_lock_mode); 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) { if (trx->mysql_thd && thd_lock_wait_timeout(trx->mysql_thd) == 0) {
trx->error_state = DB_LOCK_WAIT_TIMEOUT; trx->error_state = DB_LOCK_WAIT_TIMEOUT;
@@ -1292,7 +1301,6 @@ lock_rec_enqueue_waiting(
} }
trx->lock.wait_thr = thr; trx->lock.wait_thr = thr;
trx->lock.clear_deadlock_victim();
DBUG_LOG("ib_lock", "trx " << ib::hex(trx->id) DBUG_LOG("ib_lock", "trx " << ib::hex(trx->id)
<< " waits for lock in index " << index->name << " waits for lock in index " << index->name
@@ -1475,7 +1483,14 @@ lock_rec_lock(
que_thr_t* thr) /*!< in: query thread */ que_thr_t* thr) /*!< in: query thread */
{ {
trx_t *trx= thr_get_trx(thr); 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(!srv_read_only_mode);
ut_ad(((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_S || ut_ad(((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_S ||
((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_X); ((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 #ifdef HAVE_REPLICATION
ATTRIBUTE_NOINLINE MY_ATTRIBUTE((nonnull)) 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 trx transaction that may be waiting for a lock
@param wait_lock lock that is being waited for */ @param wait_lock lock that is being waited for */
static void lock_wait_rpl_report(trx_t *trx) 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)); ut_ad(!(wait_lock->type_mode & LOCK_AUTO_INC));
/* This would likely be too large to attempt to use a memory transaction, /* This would likely be too large to attempt to use a memory transaction,
even for wait_lock->is_table(). */ 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); mysql_mutex_unlock(&lock_sys.wait_mutex);
lock_sys.wr_lock(SRW_LOCK_CALL); lock_sys.wr_lock(SRW_LOCK_CALL);
@@ -1652,6 +1670,10 @@ static void lock_wait_rpl_report(trx_t *trx)
{ {
func_exit: func_exit:
lock_sys.wr_unlock(); 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; return;
} }
ut_ad(wait_lock->is_waiting()); 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); trx_t *trx= thr_get_trx(thr);
if (trx->mysql_thd) 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 system transactions may use the global value of
innodb_lock_wait_timeout, because trx->mysql_thd == NULL. */ 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 /* The lock has already been released or this transaction
was chosen as a deadlock victim: no need to wait */ 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=
trx->error_state= DB_DEADLOCK; trx->lock.was_chosen_as_deadlock_victim ? DB_DEADLOCK : DB_SUCCESS;
else
trx->error_state= DB_SUCCESS;
return trx->error_state; 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); wait_lock->un_member.tab_lock.table->id <= DICT_FIELDS_ID);
thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE) thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE)
? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK); ? 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); mysql_mutex_lock(&lock_sys.wait_mutex);
if (trx->lock.wait_lock) if (trx->lock.wait_lock)
@@ -1778,23 +1803,28 @@ dberr_t lock_wait(que_thr_t *thr)
if (Deadlock::check_and_resolve(trx)) if (Deadlock::check_and_resolve(trx))
{ {
ut_ad(!trx->lock.wait_lock); ut_ad(!trx->lock.wait_lock);
error_state= DB_DEADLOCK; trx->error_state= DB_DEADLOCK;
goto end_wait; goto end_wait;
} }
} }
else 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; goto end_wait;
}
if (row_lock_wait) if (row_lock_wait)
lock_sys.wait_start(); lock_sys.wait_start();
trx->error_state= DB_SUCCESS;
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
if (rpl) if (rpl)
lock_wait_rpl_report(trx); lock_wait_rpl_report(trx);
#endif #endif
trx->error_state= DB_SUCCESS;
while (trx->lock.wait_lock) while (trx->lock.wait_lock)
{ {
int err; int err;
@@ -1807,20 +1837,19 @@ dberr_t lock_wait(que_thr_t *thr)
else else
err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex, err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex,
&abstime); &abstime);
error_state= trx->error_state; switch (trx->error_state) {
switch (error_state) {
case DB_DEADLOCK: case DB_DEADLOCK:
case DB_INTERRUPTED: case DB_INTERRUPTED:
break; break;
default: 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 /* Dictionary transactions must ignore KILL, because they could
be executed as part of a multi-transaction DDL operation, be executed as part of a multi-transaction DDL operation,
such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */ such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */
if (!trx->dict_operation && trx_is_interrupted(trx)) if (!trx->dict_operation && trx_is_interrupted(trx))
/* innobase_kill_query() can only set trx->error_state=DB_INTERRUPTED /* innobase_kill_query() can only set trx->error_state=DB_INTERRUPTED
for any transaction that is attached to a connection. */ for any transaction that is attached to a connection. */
error_state= DB_INTERRUPTED; trx->error_state= DB_INTERRUPTED;
else if (!err) else if (!err)
continue; continue;
#ifdef WITH_WSREP #ifdef WITH_WSREP
@@ -1828,7 +1857,7 @@ dberr_t lock_wait(que_thr_t *thr)
#endif #endif
else else
{ {
error_state= DB_LOCK_WAIT_TIMEOUT; trx->error_state= DB_LOCK_WAIT_TIMEOUT;
lock_sys.timeouts++; lock_sys.timeouts++;
} }
} }
@@ -1848,8 +1877,7 @@ end_wait:
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
thd_wait_end(trx->mysql_thd); thd_wait_end(trx->mysql_thd);
trx->error_state= error_state; return trx->error_state;
return 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(state == TRX_STATE_ACTIVE || state == TRX_STATE_PREPARED);
ut_ad(trx->lock.wait_thr); 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); ut_ad(state == TRX_STATE_ACTIVE);
trx->error_state= DB_DEADLOCK; trx->error_state= DB_DEADLOCK;
@@ -3401,17 +3429,18 @@ lock_table_enqueue_waiting(
ut_ad(trx->mutex_is_owner()); ut_ad(trx->mutex_is_owner());
ut_ad(!trx->dict_operation_lock_mode); 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 */ /* Enqueue the lock request that will wait to be granted */
lock_table_create(table, mode | LOCK_WAIT, trx, c_lock); lock_table_create(table, mode | LOCK_WAIT, trx, c_lock);
trx->lock.wait_thr = thr; 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); MONITOR_INC(MONITOR_TABLELOCK_WAIT);
return(DB_LOCK_WAIT); return(DB_LOCK_WAIT);
@@ -3949,7 +3978,6 @@ released:
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
} }
trx->lock.was_chosen_as_deadlock_victim= false;
trx->lock.n_rec_locks= 0; trx->lock.n_rec_locks= 0;
#ifdef UNIV_DEBUG #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); lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
lock= trx->lock.wait_lock; lock= trx->lock.wait_lock;
if (!lock); /* Even if waiting lock was cancelled while lock_sys.wait_mutex was
else if (check_victim && trx->lock.was_chosen_as_deadlock_victim) 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; err= DB_DEADLOCK;
else else if (lock)
goto resolve_table_lock; goto resolve_table_lock;
} }
else else
@@ -5769,10 +5799,12 @@ retreat:
lock_sys.wr_lock(SRW_LOCK_CALL); lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
lock= trx->lock.wait_lock; lock= trx->lock.wait_lock;
if (!lock); /* Even if waiting lock was cancelled while lock_sys.wait_mutex was
else if (check_victim && trx->lock.was_chosen_as_deadlock_victim) 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; err= DB_DEADLOCK;
else else if (lock)
goto resolve_record_lock; goto resolve_record_lock;
} }
else else
@@ -5850,6 +5882,7 @@ while holding a clustered index leaf page latch.
lock request was released */ lock request was released */
dberr_t lock_trx_handle_wait(trx_t *trx) 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) if (trx->lock.was_chosen_as_deadlock_victim)
return DB_DEADLOCK; return DB_DEADLOCK;
if (!trx->lock.wait_lock) if (!trx->lock.wait_lock)

View File

@@ -1287,43 +1287,6 @@ bool mtr_t::memo_contains(const fil_space_t& space, bool shared)
return true; 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 */ /** Upgrade U-latched pages to X */
struct UpgradeX 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_d(const auto state= block->page.state());
ut_ad(state > buf_page_t::FREED); ut_ad(state > buf_page_t::FREED);
ut_ad(state > buf_page_t::WRITE_FIX || state < buf_page_t::READ_FIX); ut_ad(state > buf_page_t::WRITE_FIX || state < buf_page_t::READ_FIX);
switch (rw_latch) switch (rw_latch) {
{
case RW_NO_LATCH: case RW_NO_LATCH:
fix_type= MTR_MEMO_BUF_FIX; fix_type= MTR_MEMO_BUF_FIX;
goto done; goto done;
@@ -1405,10 +1367,8 @@ void mtr_t::page_lock(buf_block_t *block, ulint rw_latch)
} }
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (dict_index_t *index= block->index) btr_search_drop_page_hash_index(block, true);
if (index->freed()) #endif
defer_drop_ahi(block, fix_type);
#endif /* BTR_CUR_HASH_ADAPT */
done: done:
ut_ad(state < buf_page_t::UNFIXED || 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()) if (is_logged())
{ {
m_memo.for_each_block_in_reverse CIterate<MarkFreed> mf{MarkFreed{{space.id, offset}}};
(CIterate<MarkFreed>((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)); m_log.close(log_write<FREE_PAGE>({space.id, offset}, nullptr));
} }
} }

View File

@@ -118,7 +118,7 @@ public:
size_t pending_io_count() size_t pending_io_count()
{ {
return (size_t)m_max_aio - m_cache.size(); return m_cache.pos();
} }
tpool::task_group* get_task_group() tpool::task_group* get_task_group()

View File

@@ -399,11 +399,11 @@ static dberr_t trx_purge_free_segment(trx_rseg_t *rseg, fil_addr_t hdr_addr)
mtr.commit(); mtr.commit();
mtr.start(); mtr.start();
mtr.flag_modified(); 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->latch.wr_lock(SRW_LOCK_CALL);
rseg_hdr->page.lock.x_lock(); rseg_hdr->page.lock.x_lock();
block->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 /* The page list may now be inconsistent, but the length field

View File

@@ -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; byte* const start = block->page.frame + first_free;
size_t len = strlen(table->name.m_name); size_t len = strlen(table->name.m_name);
const size_t fixed = 2 + 1 + 11 + 11 + 2; 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() */ /* 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 + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE
< UNIV_PAGE_SIZE_MIN - 10 - FIL_PAGE_DATA_END); < UNIV_PAGE_SIZE_MIN - 10 - FIL_PAGE_DATA_END);

View File

@@ -135,6 +135,9 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
} }
else 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); ut_a(error_state == DB_SUCCESS);
const undo_no_t limit= savept->least_undo_no; const undo_no_t limit= savept->least_undo_no;
apply_online_log= false; apply_online_log= false;
@@ -211,6 +214,10 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
trx->will_lock = false; trx->will_lock = false;
ut_ad(trx->mysql_thd); 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 #ifdef WITH_WSREP
trx->wsrep= false; trx->wsrep= false;
trx->lock.was_chosen_as_deadlock_victim= 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_mark_sql_stat_end(trx);
trx->op_info = ""; trx->op_info = "";
#ifdef WITH_WSREP
trx->lock.was_chosen_as_deadlock_victim = false;
#endif
return(err); return(err);
} }

View File

@@ -1379,9 +1379,8 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr)
wsrep= false; wsrep= false;
wsrep_commit_ordered(mysql_thd); 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 */ #endif /* WITH_WSREP */
lock.was_chosen_as_deadlock_victim= false;
} }
void trx_t::commit_cleanup() void trx_t::commit_cleanup()

View File

@@ -23,13 +23,13 @@
solution was needed than the one-to-one conversion table. To solution was needed than the one-to-one conversion table. To
note a few, here is an example of a Czech sorting sequence: 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 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 sorted after 'a' and before 'b', but only if the word is
otherwise the same. However, because 's' is sorted before 'v' 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. more rules.
This file defines functions my_strxfrm and my_strcoll for 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 passes, that's why we need four times more space for expanded
string. string.
This file also contains the ISO-Latin-2 definitions of The non-ASCII literal strings in this file are encoded
characters. 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 Author: (c) 1997--1998 Jan Pazdziora, adelton@fi.muni.cz
Jan Pazdziora has a shared copyright for this code 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. 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. 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. 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. exists behind it, find its value.
We append 0 to the end. 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) \ #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 we pass the chain from left to right
konec řetězce poznáme podle *p == 0 we know the end of the string by *p == 0
pokud jsme došli na konec řetězce při průchodu 0, nejdeme na if we reached the end of the string on transition 0, then we don't go to
začátek, ale na uloženou pozici, protože první a druhý start, but to the saved position, because the first and second
průchod běží současně the passage runs concurrently
konec vstupu (průchodu) označíme na výstupu hodnotou 1 we mark the end of the input (transition) with the value 1 on the output
načteme hodnotu z třídící tabulky then we load the value from the sorting table
jde-li o hodnotu ignorovat (0), skočíme na další průchod if the value is ignore (0), we jump to the next pass
jde-li o hodnotu konec slova (2) a je to průchod 0 nebo 1, if the value is the end of the word (2) and it is a 0 or 1 transition,
přeskočíme všechny další 0 -- 2 a prohodíme we skip all the other 0 -- 2 and switch transitions
průchody if it is a composite character (255), we test whether it follows
jde-li o kompozitní znak (255), otestujeme, zda následuje correct to the pair, we find the correct value
správný do dvojice, dohledáme správnou hodnotu
na konci připojíme znak 0 then we add the character 0 at the end
*/ */

View File

@@ -504,19 +504,19 @@ struct charset_info_st my_charset_latin1_nopad=
* *
* The modern sort order is used, where: * The modern sort order is used, where:
* *
* 'ä' -> "ae" * 'ä' -> "ae"
* 'ö' -> "oe" * 'ö' -> "oe"
* 'ü' -> "ue" * 'ü' -> "ue"
* 'ß' -> "ss" * 'ß' -> "ss"
*/ */
/* /*
* This is a simple latin1 mapping table, which maps all accented * This is a simple latin1 mapping table, which maps all accented
* characters to their non-accented equivalents. Note: in this * 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. * accented characters except the following are treated the same way.
* Ü, ü, Ö, ö, Ä, ä * Ü, ü, Ö, ö, Ä, ä
*/ */
static const uchar sort_order_latin1_de[] = { 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. my_strnxfrm_latin_de() on both strings and compared the result strings.
This means that: This means that:
Ä must also matches ÁE and , because my_strxn_frm_latin_de() will convert Ä must also matches ÁE and , because my_strxn_frm_latin_de() will convert
both to AE. both to AE.
The other option would be to not do any accent removal in 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 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); end= skip_trailing_space(key, len);

View File

@@ -40,79 +40,121 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
namespace tpool namespace tpool
{ {
enum cache_notification_mode
{
NOTIFY_ONE,
NOTIFY_ALL
};
/** /**
Generic "pointer" cache of a fixed size Generic "pointer" cache of a fixed size
with fast put/get operations. with fast put/get operations.
Compared to STL containers, is faster/does not Compared to STL containers,e.g stack or queue
do allocations. However, put() operation will wait is faster/does not do allocations.
if there is no free items.
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 template<typename T> class cache
{ {
/** Protects updates of m_pos and m_cache members */
std::mutex m_mtx; 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::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; 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; 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() 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: 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++) for(size_t i= 0 ; i < size; i++)
m_cache[i]=&m_base[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); std::unique_lock<std::mutex> lk(m_mtx);
if (blocking) while(is_empty())
{ m_cv.wait(lk);
while(m_cache.empty()) assert(m_pos < capacity());
m_cv.wait(lk); // return last element
} return m_cache[m_pos++];
else
{
if(m_cache.empty())
return nullptr;
}
T* ret = m_cache.back();
m_cache.pop_back();
return ret;
} }
/**
Put back an item to cache.
@param item - item to put back
*/
void put(T *ele) void put(T *ele)
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
m_cache.push_back(ele); assert(!is_full());
if (m_notification_mode == NOTIFY_ONE) // put element to the logical end of the array
m_cv.notify_one(); m_cache[--m_pos] = ele;
else if(m_cache.size() == 1)
m_cv.notify_all(); // Signal cache is not empty /* Notify waiters when the cache becomes
else if(m_waiters && is_full()) not empty, or when it becomes full */
m_cv.notify_all(); // Signal cache is full if (m_pos == 1 || (m_waiters && is_full()))
m_cv.notify_all();
} }
/** Check if pointer represents cached element */
bool contains(T* ele) 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() void wait()
{ {
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);
@@ -122,9 +164,13 @@ public:
m_waiters--; 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;
} }
}; };