mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
Merge 11.4 into 11.8
This commit is contained in:
@@ -24,12 +24,6 @@
|
||||
#define HAVE_OPENSSL11 1
|
||||
#define SSL_LIBRARY OpenSSL_version(OPENSSL_VERSION)
|
||||
#define ERR_remove_state(X) ERR_clear_error()
|
||||
#define EVP_CIPHER_CTX_SIZE 200
|
||||
#define EVP_MD_CTX_SIZE 80
|
||||
#undef EVP_MD_CTX_init
|
||||
#define EVP_MD_CTX_init(X) do { memset((X), 0, EVP_MD_CTX_SIZE); EVP_MD_CTX_reset(X); } while(0)
|
||||
#undef EVP_CIPHER_CTX_init
|
||||
#define EVP_CIPHER_CTX_init(X) do { memset((X), 0, EVP_CIPHER_CTX_SIZE); EVP_CIPHER_CTX_reset(X); } while(0)
|
||||
|
||||
/*
|
||||
Macros below are deprecated. OpenSSL 1.1 may define them or not,
|
||||
@@ -66,24 +60,16 @@
|
||||
#endif /* HAVE_OPENSSL11 */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WOLFSSL
|
||||
#define EVP_MD_CTX_SIZE sizeof(wc_Md5)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENSSL11
|
||||
#ifndef ASN1_STRING_get0_data
|
||||
#define ASN1_STRING_get0_data(X) ASN1_STRING_data(X)
|
||||
#endif
|
||||
#ifndef EVP_MD_CTX_SIZE
|
||||
#define EVP_MD_CTX_SIZE sizeof(EVP_MD_CTX)
|
||||
#endif
|
||||
|
||||
#ifndef DH_set0_pqg
|
||||
#define DH_set0_pqg(D,P,Q,G) ((D)->p= (P), (D)->g= (G))
|
||||
#endif
|
||||
|
||||
#define EVP_CIPHER_CTX_encrypting(ctx) ((ctx)->encrypt)
|
||||
#define EVP_CIPHER_CTX_SIZE sizeof(EVP_CIPHER_CTX)
|
||||
|
||||
#ifndef HAVE_WOLFSSL
|
||||
#define OPENSSL_init_ssl(X,Y) SSL_library_init()
|
||||
@@ -97,13 +83,3 @@
|
||||
#ifndef TLS1_3_VERSION
|
||||
#define SSL_CTX_set_ciphersuites(X,Y) 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int check_openssl_compatibility();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
47
mysql-test/main/events_debug.result
Normal file
47
mysql-test/main/events_debug.result
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
|
||||
#
|
||||
CREATE USER definer@localhost;
|
||||
GRANT EVENT ON *.* TO definer@localhost;
|
||||
GRANT SUPER ON *.* TO definer@localhost;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
# Per-session DEBUG_SYNC: must arm from inside the event body (its own
|
||||
# thd), not from another connection.
|
||||
CREATE DEFINER=definer@localhost EVENT ev
|
||||
ON SCHEDULE AT CURRENT_TIMESTAMP
|
||||
DO
|
||||
BEGIN
|
||||
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
|
||||
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
|
||||
END|
|
||||
Warnings:
|
||||
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
|
||||
SET GLOBAL event_scheduler= ON;
|
||||
# Connect 'waiter' now: iterate() holds server_threads' rwlock for the
|
||||
# whole scan; connecting after would deadlock (needs that lock's
|
||||
# write side).
|
||||
connect waiter, localhost, root,,;
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
|
||||
# Pause right after processlist_callback() captures the
|
||||
# DEFINER-switched security_ctx, before USER/HOST are read.
|
||||
SET @saved_dbug= @@SESSION.debug_dbug;
|
||||
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
|
||||
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
|
||||
# Filter by name: checks the value read through the captured pointer.
|
||||
SELECT user, host FROM information_schema.processlist WHERE user = 'definer';
|
||||
connection waiter;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
|
||||
# Let the event finish and execute() return.
|
||||
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
|
||||
# Release the scanner to dereference the pointer it captured.
|
||||
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
|
||||
disconnect waiter;
|
||||
connection default;
|
||||
user host
|
||||
definer localhost
|
||||
SET SESSION debug_dbug= @saved_dbug;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
SET GLOBAL event_scheduler= OFF;
|
||||
DROP USER definer@localhost;
|
||||
# End of 10.11 tests
|
||||
68
mysql-test/main/events_debug.test
Normal file
68
mysql-test/main/events_debug.test
Normal file
@@ -0,0 +1,68 @@
|
||||
--source include/have_debug.inc
|
||||
--source include/have_debug_sync.inc
|
||||
--source include/not_embedded.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
|
||||
--echo #
|
||||
|
||||
CREATE USER definer@localhost;
|
||||
GRANT EVENT ON *.* TO definer@localhost;
|
||||
GRANT SUPER ON *.* TO definer@localhost;
|
||||
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
|
||||
--echo # Per-session DEBUG_SYNC: must arm from inside the event body (its own
|
||||
--echo # thd), not from another connection.
|
||||
--delimiter |
|
||||
CREATE DEFINER=definer@localhost EVENT ev
|
||||
ON SCHEDULE AT CURRENT_TIMESTAMP
|
||||
DO
|
||||
BEGIN
|
||||
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
|
||||
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
|
||||
END|
|
||||
--delimiter ;
|
||||
|
||||
SET GLOBAL event_scheduler= ON;
|
||||
|
||||
--echo # Connect 'waiter' now: iterate() holds server_threads' rwlock for the
|
||||
--echo # whole scan; connecting after would deadlock (needs that lock's
|
||||
--echo # write side).
|
||||
connect (waiter, localhost, root,,);
|
||||
connection default;
|
||||
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
|
||||
|
||||
--echo # Pause right after processlist_callback() captures the
|
||||
--echo # DEFINER-switched security_ctx, before USER/HOST are read.
|
||||
SET @saved_dbug= @@SESSION.debug_dbug;
|
||||
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
|
||||
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
|
||||
|
||||
--echo # Filter by name: checks the value read through the captured pointer.
|
||||
--send SELECT user, host FROM information_schema.processlist WHERE user = 'definer'
|
||||
|
||||
connection waiter;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
|
||||
|
||||
--echo # Let the event finish and execute() return.
|
||||
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
|
||||
|
||||
--echo # Release the scanner to dereference the pointer it captured.
|
||||
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
|
||||
|
||||
--disconnect waiter
|
||||
--connection default
|
||||
--reap
|
||||
|
||||
SET SESSION debug_dbug= @saved_dbug;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
|
||||
SET GLOBAL event_scheduler= OFF;
|
||||
--source include/check_events_off.inc
|
||||
let $count_sessions= 1;
|
||||
--source include/wait_until_count_sessions.inc
|
||||
DROP USER definer@localhost;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
@@ -1143,11 +1143,11 @@ connection con1;
|
||||
set time_zone= '+00:00';
|
||||
select row_start, row_end from t;
|
||||
row_start row_end
|
||||
2001-01-01 00:00:00.000000 2106-02-07 06:28:15.999999
|
||||
2001-01-01 00:00:00.000000 #
|
||||
handler t open;
|
||||
handler t read first;
|
||||
id a b row_start row_end DB_ROW_HASH_1
|
||||
1 10 x 2001-01-01 00:00:00.000000 2106-02-07 06:28:15.999999 #
|
||||
1 10 x 2001-01-01 00:00:00.000000 # #
|
||||
handler t close;
|
||||
disconnect con1;
|
||||
connection default;
|
||||
|
||||
@@ -1179,9 +1179,10 @@ connection default;
|
||||
grant select (a) on db.t to u@localhost;
|
||||
connection con1;
|
||||
set time_zone= '+00:00';
|
||||
--replace_column 2 #
|
||||
select row_start, row_end from t;
|
||||
handler t open;
|
||||
--replace_column 6 #
|
||||
--replace_column 5 # 6 #
|
||||
handler t read first;
|
||||
handler t close;
|
||||
disconnect con1;
|
||||
|
||||
@@ -3043,7 +3043,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"pseudo_bits_condition": "if(uuid_short(),NULL,1)",
|
||||
"outer_ref_condition": "if(uuid_short(),NULL,1)",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
@@ -3069,7 +3069,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"pseudo_bits_condition": "10 = rand()",
|
||||
"outer_ref_condition": "10 = rand()",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
|
||||
@@ -921,3 +921,19 @@ MAX(c)
|
||||
DROP TABLE t1;
|
||||
# End of 10.11 tests
|
||||
ALTER DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
# rand() < 0 is never true, so only the ROLLUP total row is returned.
|
||||
select distinct a from t1 where a=1 and rand() < 0 group by a with rollup;
|
||||
a
|
||||
NULL
|
||||
select distinct a from t1 where a=1 and rand() >= 0 group by a with rollup;
|
||||
a
|
||||
1
|
||||
NULL
|
||||
drop table t1;
|
||||
# End of 11.4 tests
|
||||
|
||||
@@ -546,4 +546,20 @@ SELECT MAX(c) FROM t1 GROUP BY c WITH ROLLUP limit 5;
|
||||
DROP TABLE t1;
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--source include/test_db_charset_restore.inc
|
||||
ALTER DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41146: WHERE is not checked when every table is const and
|
||||
--echo # the query needs a temporary table
|
||||
--echo #
|
||||
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
|
||||
--echo # rand() < 0 is never true, so only the ROLLUP total row is returned.
|
||||
select distinct a from t1 where a=1 and rand() < 0 group by a with rollup;
|
||||
select distinct a from t1 where a=1 and rand() >= 0 group by a with rollup;
|
||||
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
|
||||
@@ -2007,6 +2007,29 @@ drop table t1;
|
||||
# End of 10.4 tests
|
||||
#
|
||||
#
|
||||
# MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
#
|
||||
# INSERT ... SELECT from explicit PARTITION() without WHERE must not
|
||||
# read partitions outside of it (read_partitions widened by auto-increment)
|
||||
#
|
||||
create table t1 (id int auto_increment primary key, x int)
|
||||
partition by range (id) (
|
||||
partition p0 values less than (10),
|
||||
partition p1 values less than maxvalue);
|
||||
insert into t1 values (1, 1), (2, 2), (11, 11), (12, 12);
|
||||
create table t2 (id int, x int);
|
||||
# Fresh share: auto-increment is not initialized yet
|
||||
flush tables;
|
||||
insert into t2 select * from t1 partition (p0);
|
||||
select * from t2;
|
||||
id x
|
||||
1 1
|
||||
2 2
|
||||
drop tables t1, t2;
|
||||
#
|
||||
# End of 10.11 tests
|
||||
#
|
||||
#
|
||||
# MDEV-33988 DELETE (single table) to support table aliases
|
||||
#
|
||||
CREATE TABLE `t1` (
|
||||
|
||||
@@ -942,6 +942,29 @@ drop table t1;
|
||||
--echo # End of 10.4 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
--echo #
|
||||
--echo # INSERT ... SELECT from explicit PARTITION() without WHERE must not
|
||||
--echo # read partitions outside of it (read_partitions widened by auto-increment)
|
||||
--echo #
|
||||
create table t1 (id int auto_increment primary key, x int)
|
||||
partition by range (id) (
|
||||
partition p0 values less than (10),
|
||||
partition p1 values less than maxvalue);
|
||||
insert into t1 values (1, 1), (2, 2), (11, 11), (12, 12);
|
||||
create table t2 (id int, x int);
|
||||
--echo # Fresh share: auto-increment is not initialized yet
|
||||
flush tables;
|
||||
insert into t2 select * from t1 partition (p0);
|
||||
--sorted_result
|
||||
select * from t2;
|
||||
drop tables t1, t2;
|
||||
|
||||
--echo #
|
||||
--echo # End of 10.11 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-33988 DELETE (single table) to support table aliases
|
||||
--echo #
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
|
||||
--echo # try logging in with a certificate in the server's --ssl-crl : should fail
|
||||
# OpenSSL 1.1.1a and later releases correctly rejects the certificate, but the error message is different
|
||||
--replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: sslv3 alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/
|
||||
--replace_regex /(ERROR 2013 \(HY000\): Lost connection to server at '.*', system error: [0-9]+|ERROR 2026 \(HY000\): TLS\/SSL error: (sslv3|tls) alert certificate revoked)/ERROR 2026 (HY000): TLS\/SSL error: ssl\/tls alert certificate revoked/
|
||||
--error 1
|
||||
--exec $MYSQL --ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem --ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem --ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem test -e "SHOW STATUS LIKE 'Ssl_version'" 2>&1
|
||||
|
||||
@@ -7671,7 +7671,7 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"pseudo_bits_condition": "1 = t1.a or <in_optimizer>(1,<exists>(subquery#3))",
|
||||
"outer_ref_condition": "1 = t1.a or <in_optimizer>(1,<exists>(subquery#3))",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
|
||||
1
mysql-test/main/subselect_sj_mat_rand.opt
Normal file
1
mysql-test/main/subselect_sj_mat_rand.opt
Normal file
@@ -0,0 +1 @@
|
||||
--memory.optimizer_row_copy_cost=0
|
||||
421
mysql-test/main/subselect_sj_mat_rand.result
Normal file
421
mysql-test/main/subselect_sj_mat_rand.result
Normal file
@@ -0,0 +1,421 @@
|
||||
#
|
||||
# MDEV-41146: rand() in WHERE is not checked when every base table
|
||||
# is const and a materialized semi-join terminates the join order
|
||||
#
|
||||
# A WHERE conjunct that calls rand() and names no field is checked on
|
||||
# the last top level base table that is not const. When every base
|
||||
# table is const, the conjunct is checked once in outer_ref_condition.
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int primary key);
|
||||
insert into t2 values (1),(2),(3),(4);
|
||||
create table t3 (c int, d int);
|
||||
insert into t3 values (1,1),(2,2),(3,3),(4,4),(5,5),
|
||||
(6,6),(7,7),(8,8),(9,9),(10,10);
|
||||
insert into t3 select c+10, d+10 from t3;
|
||||
insert into t3 select c+20, d+20 from t3;
|
||||
insert into t3 select c+40, d+40 from t3;
|
||||
create table t4 (e int, f int);
|
||||
insert into t4 select c, d from t3;
|
||||
create table t5 (g int);
|
||||
insert into t5 select c+1000 from t3;
|
||||
set @save_optimizer_switch= @@optimizer_switch;
|
||||
set @save_optimizer_where_cost= @@optimizer_where_cost;
|
||||
set optimizer_switch='firstmatch=off,loosescan=off';
|
||||
set optimizer_where_cost=0;
|
||||
# One const table. rand() < 0 is never true, so the result is empty.
|
||||
explain
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
a
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() >= 0;
|
||||
a
|
||||
1
|
||||
# The IN-equality is still checked. t5 holds no value below 1001.
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select g from t5) and rand() >= 0;
|
||||
a
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select g from t5) and rand() < 0;
|
||||
a
|
||||
# Two rand() conjuncts. outer_ref_condition shows both.
|
||||
explain format=json
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() < 0 and rand() < 2;
|
||||
EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"cost": "COST_REPLACED",
|
||||
"outer_ref_condition": "rand() < 0 and rand() < 2",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "const",
|
||||
"possible_keys": ["PRIMARY"],
|
||||
"key": "PRIMARY",
|
||||
"key_length": "4",
|
||||
"used_key_parts": ["a"],
|
||||
"ref": ["const"],
|
||||
"rows": 1,
|
||||
"filtered": 100,
|
||||
"using_index": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"table_name": "<subquery2>",
|
||||
"access_type": "eq_ref",
|
||||
"possible_keys": ["distinct_key"],
|
||||
"key": "distinct_key",
|
||||
"key_length": "4",
|
||||
"used_key_parts": ["c"],
|
||||
"ref": ["func"],
|
||||
"rows": 1,
|
||||
"filtered": 100,
|
||||
"materialized": {
|
||||
"unique": 1,
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "t3",
|
||||
"access_type": "ALL",
|
||||
"loops": 1,
|
||||
"rows": 80,
|
||||
"cost": "COST_REPLACED",
|
||||
"filtered": 100,
|
||||
"attached_condition": "t3.c = 1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() < 0 and rand() < 2;
|
||||
a
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() >= 0 and rand() < 2;
|
||||
a
|
||||
1
|
||||
# Two const tables joined, as in the original report.
|
||||
explain
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY t2 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
x y
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() >= 0;
|
||||
x y
|
||||
1 1
|
||||
# Two materialized nests terminate the join order.
|
||||
explain
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
1 PRIMARY <subquery3> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
3 MATERIALIZED t4 ALL NULL NULL NULL NULL 80 Using where
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() < 0;
|
||||
a
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() >= 0;
|
||||
a
|
||||
1
|
||||
# The const table column is bigint and the subquery column is int.
|
||||
create table t6 (a bigint primary key);
|
||||
insert into t6 values (1),(2),(3);
|
||||
explain
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t6 const PRIMARY PRIMARY 8 const 1 Using index
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() < 0;
|
||||
a
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() >= 0;
|
||||
a
|
||||
1
|
||||
# An outer table that is not const precedes the nest. The conjunct
|
||||
# is checked on that table.
|
||||
create table t7 (a int);
|
||||
insert into t7 values (1),(2),(3),(100);
|
||||
explain
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t7 ALL NULL NULL NULL NULL 4 Using where
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() < 0;
|
||||
a
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() >= 0;
|
||||
a
|
||||
1
|
||||
2
|
||||
3
|
||||
# The conjunct also reads a column of the enclosing query. It is true
|
||||
# when t7.a is 4 or more, which holds only for the row where t7.a is
|
||||
# 100.
|
||||
explain
|
||||
select t7.a,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < t7.a - 3) x
|
||||
from t7;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t7 ALL NULL NULL NULL NULL 4
|
||||
2 DEPENDENT SUBQUERY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
2 DEPENDENT SUBQUERY <subquery3> eq_ref distinct_key distinct_key 4 func 1
|
||||
3 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
select t7.a,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < t7.a - 3) x
|
||||
from t7;
|
||||
a x
|
||||
1 NULL
|
||||
100 1
|
||||
2 NULL
|
||||
3 NULL
|
||||
# rand(7) continues one sequence across the executions of the
|
||||
# subquery. The conjunct takes one value for each row of t8,
|
||||
# including the rows where t8.a is 100 and the lookup finds no match.
|
||||
# A plan where t1 is not const checks the conjunct on t1 before the
|
||||
# lookup and returns the same rows.
|
||||
create table t8 (id int, a int);
|
||||
insert into t8 values (1,1),(2,100),(3,2),(4,100),(5,3),(6,100),(7,4),
|
||||
(8,100),(9,5),(10,100),(11,6),(12,7),(13,8),(14,9);
|
||||
explain
|
||||
select t8.id,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t8.a in (select c from t3) and rand(7) < 0.5) x
|
||||
from t8;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t8 ALL NULL NULL NULL NULL 14
|
||||
2 DEPENDENT SUBQUERY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
2 DEPENDENT SUBQUERY <subquery3> eq_ref distinct_key distinct_key 4 func 1
|
||||
3 MATERIALIZED t3 ALL NULL NULL NULL NULL 80
|
||||
select t8.id,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t8.a in (select c from t3) and rand(7) < 0.5) x
|
||||
from t8 order by t8.id;
|
||||
id x
|
||||
1 NULL
|
||||
2 NULL
|
||||
3 1
|
||||
4 NULL
|
||||
5 NULL
|
||||
6 NULL
|
||||
7 1
|
||||
8 NULL
|
||||
9 1
|
||||
10 NULL
|
||||
11 NULL
|
||||
12 1
|
||||
13 1
|
||||
14 NULL
|
||||
# rand() in the WHERE of the subquery instead of the outer query.
|
||||
# Every row of t9 has c=1. The first four values of rand(1) are not
|
||||
# below 0.09 and the fifth is, so checking the condition on each row
|
||||
# of t9 matches t1.a=1. A subquery that calls rand() is not converted
|
||||
# to a semi-join, so the condition is not moved to the outer query and
|
||||
# checked once there. Adding 0*t9.d makes the condition read a column
|
||||
# of t9, which gives the same result in any plan.
|
||||
create table t9 (c int, d int);
|
||||
insert into t9 select 1, 1 from seq_1_to_80;
|
||||
explain
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09);
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY <subquery2> const distinct_key distinct_key 4 const 1
|
||||
2 MATERIALIZED t9 ALL NULL NULL NULL NULL 80 Using where
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09);
|
||||
count(*)
|
||||
1
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09 + 0*t9.d);
|
||||
count(*)
|
||||
1
|
||||
# The conjunct is checked once, in outer_ref_condition, only when
|
||||
# the join returns at most one row. f1() counts its calls. With
|
||||
# the const t1 and a lookup nest, f1() is called once. Adding t10,
|
||||
# which holds two rows, makes the join return two rows, and f1() is
|
||||
# called once for each.
|
||||
create table t10 (b int);
|
||||
insert into t10 values (1),(2);
|
||||
create function f1() returns int not deterministic
|
||||
begin
|
||||
set @calls= @calls + 1;
|
||||
return 1;
|
||||
end|
|
||||
explain
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
set @calls= 0;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
a
|
||||
1
|
||||
select @calls;
|
||||
@calls
|
||||
1
|
||||
explain
|
||||
select t1.a, t10.b from t1, t10
|
||||
where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 Using index
|
||||
1 PRIMARY t10 ALL NULL NULL NULL NULL 2 Using where
|
||||
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
|
||||
2 MATERIALIZED t3 ALL NULL NULL NULL NULL 80 Using where
|
||||
set @calls= 0;
|
||||
select t1.a, t10.b from t1, t10
|
||||
where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
a b
|
||||
1 1
|
||||
1 2
|
||||
select @calls;
|
||||
@calls
|
||||
2
|
||||
drop function f1;
|
||||
# A materialized nest read by a scan precedes t11, which is not
|
||||
# const, so the conjunct is checked on t11. The optimizer chooses
|
||||
# the scan only with the statistics from ANALYZE, which give 200
|
||||
# rows of t11 for each value of a.
|
||||
create table t11 (a int, key(a));
|
||||
insert into t11 select seq mod 10 from seq_1_to_2000;
|
||||
create table t12 (b int);
|
||||
insert into t12 select seq mod 10 from seq_1_to_20;
|
||||
analyze table t11, t12 persistent for all;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t11 analyze status Engine-independent statistics collected
|
||||
test.t11 analyze status Table is already up to date
|
||||
test.t12 analyze status Engine-independent statistics collected
|
||||
test.t12 analyze status OK
|
||||
explain format=json
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() < 0;
|
||||
EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"cost": "COST_REPLACED",
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "const",
|
||||
"possible_keys": ["PRIMARY"],
|
||||
"key": "PRIMARY",
|
||||
"key_length": "4",
|
||||
"used_key_parts": ["a"],
|
||||
"ref": ["const"],
|
||||
"rows": 1,
|
||||
"filtered": 100,
|
||||
"using_index": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"table_name": "<subquery2>",
|
||||
"access_type": "ALL",
|
||||
"possible_keys": ["distinct_key"],
|
||||
"rows": 20,
|
||||
"filtered": 100,
|
||||
"materialized": {
|
||||
"unique": 1,
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"nested_loop": [
|
||||
{
|
||||
"table": {
|
||||
"table_name": "t12",
|
||||
"access_type": "ALL",
|
||||
"loops": 1,
|
||||
"rows": 20,
|
||||
"cost": "COST_REPLACED",
|
||||
"filtered": 100,
|
||||
"attached_condition": "t12.b is not null"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": {
|
||||
"table_name": "t11",
|
||||
"access_type": "ref",
|
||||
"possible_keys": ["a"],
|
||||
"key": "a",
|
||||
"key_length": "5",
|
||||
"used_key_parts": ["a"],
|
||||
"ref": ["test.t12.b"],
|
||||
"loops": 20,
|
||||
"rows": 200,
|
||||
"cost": "COST_REPLACED",
|
||||
"filtered": 100,
|
||||
"attached_condition": "rand() < 0",
|
||||
"using_index": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() < 0;
|
||||
count(*)
|
||||
0
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() >= 0;
|
||||
count(*)
|
||||
2000
|
||||
# A prepared statement executed twice, with the bound for rand()
|
||||
# passed as a parameter. The first execution returns no row and
|
||||
# the second returns t1.a=1.
|
||||
prepare stmt from "select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < ?";
|
||||
execute stmt using 0;
|
||||
a
|
||||
execute stmt using 2;
|
||||
a
|
||||
1
|
||||
deallocate prepare stmt;
|
||||
# The same query in a stored procedure called twice.
|
||||
create procedure p1(x int)
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < x;
|
||||
call p1(0);
|
||||
a
|
||||
call p1(2);
|
||||
a
|
||||
1
|
||||
drop procedure p1;
|
||||
set optimizer_switch= @save_optimizer_switch;
|
||||
set optimizer_where_cost= @save_optimizer_where_cost;
|
||||
drop table t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12;
|
||||
# End of 11.4 tests
|
||||
214
mysql-test/main/subselect_sj_mat_rand.test
Normal file
214
mysql-test/main/subselect_sj_mat_rand.test
Normal file
@@ -0,0 +1,214 @@
|
||||
--source include/have_sequence.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41146: rand() in WHERE is not checked when every base table
|
||||
--echo # is const and a materialized semi-join terminates the join order
|
||||
--echo #
|
||||
--echo # A WHERE conjunct that calls rand() and names no field is checked on
|
||||
--echo # the last top level base table that is not const. When every base
|
||||
--echo # table is const, the conjunct is checked once in outer_ref_condition.
|
||||
--echo #
|
||||
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int primary key);
|
||||
insert into t2 values (1),(2),(3),(4);
|
||||
create table t3 (c int, d int);
|
||||
insert into t3 values (1,1),(2,2),(3,3),(4,4),(5,5),
|
||||
(6,6),(7,7),(8,8),(9,9),(10,10);
|
||||
insert into t3 select c+10, d+10 from t3;
|
||||
insert into t3 select c+20, d+20 from t3;
|
||||
insert into t3 select c+40, d+40 from t3;
|
||||
create table t4 (e int, f int);
|
||||
insert into t4 select c, d from t3;
|
||||
create table t5 (g int);
|
||||
insert into t5 select c+1000 from t3;
|
||||
|
||||
set @save_optimizer_switch= @@optimizer_switch;
|
||||
set @save_optimizer_where_cost= @@optimizer_where_cost;
|
||||
set optimizer_switch='firstmatch=off,loosescan=off';
|
||||
set optimizer_where_cost=0;
|
||||
|
||||
--echo # One const table. rand() < 0 is never true, so the result is empty.
|
||||
explain
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and rand() >= 0;
|
||||
|
||||
--echo # The IN-equality is still checked. t5 holds no value below 1001.
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select g from t5) and rand() >= 0;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select g from t5) and rand() < 0;
|
||||
|
||||
--echo # Two rand() conjuncts. outer_ref_condition shows both.
|
||||
--source include/explain-no-costs.inc
|
||||
explain format=json
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() < 0 and rand() < 2;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() < 0 and rand() < 2;
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3)
|
||||
and rand() >= 0 and rand() < 2;
|
||||
|
||||
--echo # Two const tables joined, as in the original report.
|
||||
explain
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() < 0;
|
||||
select t1.a x, t2.b y from t1 left join t2 on t1.a=t2.b
|
||||
where t1.a=1 and t2.b=1 and t1.a in (select c from t3) and rand() >= 0;
|
||||
|
||||
--echo # Two materialized nests terminate the join order.
|
||||
explain
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() < 0;
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() < 0;
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and t1.a in (select e from t4)
|
||||
and rand() >= 0;
|
||||
|
||||
--echo # The const table column is bigint and the subquery column is int.
|
||||
create table t6 (a bigint primary key);
|
||||
insert into t6 values (1),(2),(3);
|
||||
explain
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() < 0;
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() < 0;
|
||||
select t6.a from t6 where t6.a=1 and t6.a in (select c from t3) and rand() >= 0;
|
||||
|
||||
--echo # An outer table that is not const precedes the nest. The conjunct
|
||||
--echo # is checked on that table.
|
||||
create table t7 (a int);
|
||||
insert into t7 values (1),(2),(3),(100);
|
||||
explain
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() < 0;
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() < 0;
|
||||
--sorted_result
|
||||
select t7.a from t7 where t7.a in (select c from t3) and rand() >= 0;
|
||||
|
||||
--echo # The conjunct also reads a column of the enclosing query. It is true
|
||||
--echo # when t7.a is 4 or more, which holds only for the row where t7.a is
|
||||
--echo # 100.
|
||||
explain
|
||||
select t7.a,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < t7.a - 3) x
|
||||
from t7;
|
||||
--sorted_result
|
||||
select t7.a,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < t7.a - 3) x
|
||||
from t7;
|
||||
|
||||
--echo # rand(7) continues one sequence across the executions of the
|
||||
--echo # subquery. The conjunct takes one value for each row of t8,
|
||||
--echo # including the rows where t8.a is 100 and the lookup finds no match.
|
||||
--echo # A plan where t1 is not const checks the conjunct on t1 before the
|
||||
--echo # lookup and returns the same rows.
|
||||
create table t8 (id int, a int);
|
||||
insert into t8 values (1,1),(2,100),(3,2),(4,100),(5,3),(6,100),(7,4),
|
||||
(8,100),(9,5),(10,100),(11,6),(12,7),(13,8),(14,9);
|
||||
explain
|
||||
select t8.id,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t8.a in (select c from t3) and rand(7) < 0.5) x
|
||||
from t8;
|
||||
select t8.id,
|
||||
(select t1.a from t1
|
||||
where t1.a=1 and t8.a in (select c from t3) and rand(7) < 0.5) x
|
||||
from t8 order by t8.id;
|
||||
|
||||
--echo # rand() in the WHERE of the subquery instead of the outer query.
|
||||
--echo # Every row of t9 has c=1. The first four values of rand(1) are not
|
||||
--echo # below 0.09 and the fifth is, so checking the condition on each row
|
||||
--echo # of t9 matches t1.a=1. A subquery that calls rand() is not converted
|
||||
--echo # to a semi-join, so the condition is not moved to the outer query and
|
||||
--echo # checked once there. Adding 0*t9.d makes the condition read a column
|
||||
--echo # of t9, which gives the same result in any plan.
|
||||
create table t9 (c int, d int);
|
||||
insert into t9 select 1, 1 from seq_1_to_80;
|
||||
explain
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09);
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09);
|
||||
select count(*) from t1
|
||||
where t1.a=1 and t1.a in (select c from t9 where rand(1) < 0.09 + 0*t9.d);
|
||||
|
||||
--echo # The conjunct is checked once, in outer_ref_condition, only when
|
||||
--echo # the join returns at most one row. f1() counts its calls. With
|
||||
--echo # the const t1 and a lookup nest, f1() is called once. Adding t10,
|
||||
--echo # which holds two rows, makes the join return two rows, and f1() is
|
||||
--echo # called once for each.
|
||||
create table t10 (b int);
|
||||
insert into t10 values (1),(2);
|
||||
delimiter |;
|
||||
create function f1() returns int not deterministic
|
||||
begin
|
||||
set @calls= @calls + 1;
|
||||
return 1;
|
||||
end|
|
||||
delimiter ;|
|
||||
explain
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
set @calls= 0;
|
||||
# A second execution under --ps-protocol would double @calls.
|
||||
--disable_ps2_protocol
|
||||
select t1.a from t1 where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
--enable_ps2_protocol
|
||||
select @calls;
|
||||
explain
|
||||
select t1.a, t10.b from t1, t10
|
||||
where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
set @calls= 0;
|
||||
# A second execution under --ps-protocol would double @calls.
|
||||
--disable_ps2_protocol
|
||||
--sorted_result
|
||||
select t1.a, t10.b from t1, t10
|
||||
where t1.a=1 and t1.a in (select c from t3) and f1() > 0;
|
||||
--enable_ps2_protocol
|
||||
select @calls;
|
||||
drop function f1;
|
||||
|
||||
--echo # A materialized nest read by a scan precedes t11, which is not
|
||||
--echo # const, so the conjunct is checked on t11. The optimizer chooses
|
||||
--echo # the scan only with the statistics from ANALYZE, which give 200
|
||||
--echo # rows of t11 for each value of a.
|
||||
create table t11 (a int, key(a));
|
||||
insert into t11 select seq mod 10 from seq_1_to_2000;
|
||||
create table t12 (b int);
|
||||
insert into t12 select seq mod 10 from seq_1_to_20;
|
||||
analyze table t11, t12 persistent for all;
|
||||
--source include/explain-no-costs.inc
|
||||
explain format=json
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() < 0;
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() < 0;
|
||||
select count(*) from t1, t11
|
||||
where t1.a=1 and t11.a in (select b from t12) and rand() >= 0;
|
||||
|
||||
--echo # A prepared statement executed twice, with the bound for rand()
|
||||
--echo # passed as a parameter. The first execution returns no row and
|
||||
--echo # the second returns t1.a=1.
|
||||
prepare stmt from "select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < ?";
|
||||
execute stmt using 0;
|
||||
execute stmt using 2;
|
||||
deallocate prepare stmt;
|
||||
|
||||
--echo # The same query in a stored procedure called twice.
|
||||
create procedure p1(x int)
|
||||
select t1.a from t1
|
||||
where t1.a=1 and t1.a in (select c from t3) and rand() < x;
|
||||
call p1(0);
|
||||
call p1(2);
|
||||
drop procedure p1;
|
||||
|
||||
set optimizer_switch= @save_optimizer_switch;
|
||||
set optimizer_where_cost= @save_optimizer_where_cost;
|
||||
drop table t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
@@ -19,11 +19,11 @@ source include/restart_mysqld.inc;
|
||||
--exec $MYSQL --host=localhost --ssl-cipher=ECDHE-RSA-AES256-GCM-SHA384 --tls-version=TLSv1.2 -e "SHOW STATUS LIKE 'Ssl_cipher%';"
|
||||
|
||||
# Check that other ciphers are correctly not supported by the server
|
||||
--replace_regex /sslv3 alert handshake failure/ssl\/tls alert handshake failure/
|
||||
--replace_regex /error: (sslv3|tls) alert/error: ssl\/tls alert/
|
||||
--error 1
|
||||
--exec $MYSQL --host=localhost --ssl-cipher=TLS_AES_256_GCM_SHA384 --tls-version=TLSv1.3 -e "SHOW STATUS LIKE 'Ssl_cipher';" 2>&1
|
||||
|
||||
--replace_regex /sslv3 alert handshake failure/ssl\/tls alert handshake failure/
|
||||
--replace_regex /error: (sslv3|tls) alert/error: ssl\/tls alert/
|
||||
--error 1
|
||||
--exec $MYSQL --host=localhost --ssl-cipher=ECDHE-RSA-AES128-GCM-SHA256 --tls-version=TLSv1.2 -e "SHOW STATUS LIKE 'Ssl_cipher';" 2>&1
|
||||
|
||||
|
||||
@@ -4875,3 +4875,27 @@ DROP TABLE t1;
|
||||
#
|
||||
# End of 10.11 tests
|
||||
#
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
# rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
a row_number() over ()
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
a row_number() over ()
|
||||
1 1
|
||||
# t2.b > 5 is true only for the row where t2.b is 10.
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
b x
|
||||
1 NULL
|
||||
10 1
|
||||
drop table t1, t2;
|
||||
#
|
||||
# End of 11.4 tests
|
||||
#
|
||||
|
||||
@@ -3150,3 +3150,28 @@ DROP TABLE t1;
|
||||
--echo #
|
||||
--echo # End of 10.11 tests
|
||||
--echo #
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41146: WHERE is not checked when every table is const and
|
||||
--echo # the query needs a temporary table
|
||||
--echo #
|
||||
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
|
||||
--echo # rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
|
||||
--echo # t2.b > 5 is true only for the row where t2.b is 10.
|
||||
--sorted_result
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
|
||||
drop table t1, t2;
|
||||
|
||||
--echo #
|
||||
--echo # End of 11.4 tests
|
||||
--echo #
|
||||
|
||||
@@ -104,3 +104,74 @@ BINLOG '-2079193929';
|
||||
ERROR HY000: Decoding of base64 string failed
|
||||
BINLOG 'xç↓%~∙D╒ƒ╡';
|
||||
ERROR HY000: Decoding of base64 string failed
|
||||
#
|
||||
# MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
|
||||
#
|
||||
create table t0 (a int);
|
||||
create table t1 (a int);
|
||||
## Case 1: Trigger (disabled)
|
||||
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
create trigger trig_t0_insert after insert on t0 for each row
|
||||
binlog @a, @b;
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 2: SP (disabled)
|
||||
create procedure proc_t1_insert() binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 3: SF (disabled)
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
return 0;
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog @a, @b;
|
||||
return 0;
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 4: Event (disabled)
|
||||
create event ev_t1_insert on schedule every 1 day do
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 5: Anonymous block (disabled)
|
||||
begin not atomic
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 6: Dynamic SQL in SP (disabled)
|
||||
create procedure proc_t1_insert()
|
||||
execute immediate "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
call proc_t1_insert();
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
drop procedure proc_t1_insert;
|
||||
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
create procedure proc_t1_insert() execute stmt;
|
||||
call proc_t1_insert();
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
drop procedure proc_t1_insert;
|
||||
deallocate prepare stmt;
|
||||
## Case 7: Row event in PS (works)
|
||||
binlog 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC';
|
||||
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
a
|
||||
3
|
||||
3
|
||||
## Case 8: Statement event in PS (works)
|
||||
prepare stmt from "binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
a
|
||||
3
|
||||
3
|
||||
99
|
||||
99
|
||||
drop table t0, t1;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -164,3 +164,101 @@ BINLOG '123';
|
||||
BINLOG '-2079193929';
|
||||
--error ER_BASE64_DECODE_ERROR
|
||||
BINLOG 'xç↓%~∙D╒ƒ╡';
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
|
||||
--echo #
|
||||
|
||||
create table t0 (a int);
|
||||
create table t1 (a int);
|
||||
|
||||
# Format_description_log_event, same as in the FD test above
|
||||
--let $FORMAT_DESCR_LOG_EVENT= 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'
|
||||
|
||||
# table_map+write_rows_log_event corresponding to: insert into t1 values (3)
|
||||
--let $ROW_INSERT_T1= 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='
|
||||
|
||||
# Query_log_event (no checksum, matches the checksum-less FD event above)
|
||||
# corresponding to: insert into t1 values (99)
|
||||
--let $STMT_INSERT_T1= 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'
|
||||
|
||||
--echo ## Case 1: Trigger (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create trigger trig_t0_insert after insert on t0 for each row
|
||||
binlog @a, @b;
|
||||
|
||||
--echo ## Case 2: SP (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create procedure proc_t1_insert() binlog $STMT_INSERT_T1;
|
||||
|
||||
--echo ## Case 3: SF (disabled)
|
||||
|
||||
delimiter |;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog $ROW_INSERT_T1;
|
||||
return 0;
|
||||
end|
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog @a, @b;
|
||||
return 0;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
--echo ## Case 4: Event (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create event ev_t1_insert on schedule every 1 day do
|
||||
binlog $ROW_INSERT_T1;
|
||||
|
||||
--echo ## Case 5: Anonymous block (disabled)
|
||||
|
||||
delimiter |;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval begin not atomic
|
||||
binlog $ROW_INSERT_T1;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
--echo ## Case 6: Dynamic SQL in SP (disabled)
|
||||
|
||||
eval create procedure proc_t1_insert()
|
||||
execute immediate "binlog $ROW_INSERT_T1";
|
||||
--error ER_SP_BADSTATEMENT
|
||||
call proc_t1_insert();
|
||||
drop procedure proc_t1_insert;
|
||||
|
||||
eval prepare stmt from "binlog $ROW_INSERT_T1";
|
||||
create procedure proc_t1_insert() execute stmt;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
call proc_t1_insert();
|
||||
drop procedure proc_t1_insert;
|
||||
deallocate prepare stmt;
|
||||
|
||||
--echo ## Case 7: Row event in PS (works)
|
||||
|
||||
eval binlog $FORMAT_DESCR_LOG_EVENT;
|
||||
eval prepare stmt from "binlog $ROW_INSERT_T1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
|
||||
--echo ## Case 8: Statement event in PS (works)
|
||||
|
||||
eval prepare stmt from "binlog $STMT_INSERT_T1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
|
||||
drop table t0, t1;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
@@ -4882,6 +4882,30 @@ DROP TABLE t1;
|
||||
# End of 10.11 tests
|
||||
#
|
||||
#
|
||||
# MDEV-41146: WHERE is not checked when every table is const and
|
||||
# the query needs a temporary table
|
||||
#
|
||||
create table t1 (a int primary key);
|
||||
insert into t1 values (1),(2),(3);
|
||||
create table t2 (b int);
|
||||
insert into t2 values (1),(10);
|
||||
# rand() < 0 is never true, so the result is empty.
|
||||
select a, row_number() over () from t1 where a=1 and rand() < 0;
|
||||
a row_number() over ()
|
||||
select a, row_number() over () from t1 where a=1 and rand() >= 0;
|
||||
a row_number() over ()
|
||||
1 1
|
||||
# t2.b > 5 is true only for the row where t2.b is 10.
|
||||
select b, (select row_number() over () from t1 where t1.a=1 and t2.b > 5) x
|
||||
from t2;
|
||||
b x
|
||||
1 NULL
|
||||
10 1
|
||||
drop table t1, t2;
|
||||
#
|
||||
# End of 11.4 tests
|
||||
#
|
||||
#
|
||||
# MDEV-23867: select crash in compute_window_func
|
||||
#
|
||||
set @save_sort_buffer_size=@@sort_buffer_size;
|
||||
|
||||
@@ -300,6 +300,16 @@ if (0 && $have_debug) { # these messages sometimes fail to appear
|
||||
|
||||
--source include/start_mysqld.inc
|
||||
|
||||
# The check of pages_old needs old pages in the buffer pool LRU list.
|
||||
# They exist only when the list holds at least BUF_LRU_OLD_MIN_LEN pages.
|
||||
# Without the asynchronous load of the buffer pool dump, the list can be
|
||||
# shorter than that.
|
||||
let $wait_condition =
|
||||
SELECT SUBSTR(variable_value, 1, 33) = 'Buffer pool(s) load completed at '
|
||||
FROM information_schema.global_status
|
||||
WHERE LOWER(variable_name) = 'innodb_buffer_pool_load_status';
|
||||
--source include/wait_condition.inc
|
||||
|
||||
--echo # Trigger AHI and test pages_hashed, pages_old as non zero from sys view
|
||||
|
||||
SELECT STRAIGHT_JOIN COUNT(t6a.a)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
connection node_2;
|
||||
connection node_1;
|
||||
# Correct Galera library found
|
||||
CALL mtr.add_suppression("Aborting");
|
||||
CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
|
||||
connection node_1;
|
||||
@@ -22,7 +23,10 @@ f1 f2
|
||||
2 b
|
||||
3 c
|
||||
connection node_2;
|
||||
Starting server ...
|
||||
Starting server with an unknown option ...
|
||||
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
|
||||
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
|
||||
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
|
||||
Starting server ...
|
||||
SELECT * FROM t1;
|
||||
f1 f2
|
||||
@@ -39,7 +43,10 @@ f1 f2
|
||||
2 d
|
||||
3 d
|
||||
connection node_2;
|
||||
Starting server ...
|
||||
Starting server with an unknown option ...
|
||||
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
|
||||
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
|
||||
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
|
||||
Starting server ...
|
||||
call mtr.add_suppression("WSREP: Failed to scan the last segment to the end\\. Last events may be missing\\. Last recovered event: ");
|
||||
SELECT * FROM t1;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
--source include/galera_cluster.inc
|
||||
--source include/have_innodb.inc
|
||||
|
||||
#
|
||||
# Galera library version with the graceful shutdown on an interrupted state
|
||||
# transfer (MDEV-31517), which this test expects
|
||||
#
|
||||
--let $galera_version=26.4.25
|
||||
source ../wsrep/include/check_galera_version.inc;
|
||||
|
||||
# Suppress expected warnings:
|
||||
|
||||
CALL mtr.add_suppression("Aborting");
|
||||
@@ -11,22 +18,6 @@ CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
|
||||
--let $node_2=node_2
|
||||
--source include/auto_increment_offset_save.inc
|
||||
|
||||
#
|
||||
# We should count the number of "Assertion failed" warnings
|
||||
# in the log file before and after testing. To do this we need
|
||||
# to save original log file before testing:
|
||||
#
|
||||
--let TEST_LOG=$MYSQLTEST_VARDIR/log/mysqld.2.err
|
||||
--perl
|
||||
use strict;
|
||||
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
|
||||
my $test_log_copy=$test_log . '.copy';
|
||||
if (-e $test_log_copy) {
|
||||
unlink $test_log_copy;
|
||||
}
|
||||
EOF
|
||||
--copy_file $TEST_LOG $TEST_LOG.copy
|
||||
|
||||
--connection node_2
|
||||
|
||||
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
|
||||
@@ -59,14 +50,26 @@ SELECT * FROM t1;
|
||||
|
||||
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
|
||||
|
||||
--let $start_mysqld_params=--galera-unknown-option
|
||||
# The server must refuse to start, and exit without an assertion.
|
||||
# Start it directly rather than through the expect file: mtr would treat
|
||||
# a server that fails to start as a test failure.
|
||||
# The unknown option is found only after the state transfer has started,
|
||||
# which is then interrupted, and Galera aborts the process on purpose
|
||||
# (SIGABRT, exit code 134).
|
||||
|
||||
--echo Starting server ...
|
||||
--write_line "try:$start_mysqld_params" $_expect_file_name
|
||||
|
||||
# Sleep to ensure that server exited...
|
||||
|
||||
--sleep 30
|
||||
--echo Starting server with an unknown option ...
|
||||
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
|
||||
--error 134
|
||||
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
|
||||
--let SEARCH_FILE= $unknown_option_log
|
||||
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= Assertion .* failed
|
||||
--source include/search_pattern_in_file.inc
|
||||
--remove_file $unknown_option_log
|
||||
--remove_file $unknown_option_log.out
|
||||
|
||||
# Restart node 2 without unknown option:
|
||||
|
||||
@@ -104,14 +107,26 @@ SELECT * FROM t1;
|
||||
|
||||
--connection node_2
|
||||
|
||||
--let $start_mysqld_params=--galera-unknown-option
|
||||
# The server must refuse to start, and exit without an assertion.
|
||||
# Start it directly rather than through the expect file: mtr would treat
|
||||
# a server that fails to start as a test failure.
|
||||
# The unknown option is found only after the state transfer has started,
|
||||
# which is then interrupted, and Galera aborts the process on purpose
|
||||
# (SIGABRT, exit code 134).
|
||||
|
||||
--echo Starting server ...
|
||||
--write_line "try:$start_mysqld_params" $_expect_file_name
|
||||
|
||||
# Sleep to ensure that server exited...
|
||||
|
||||
--sleep 30
|
||||
--echo Starting server with an unknown option ...
|
||||
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
|
||||
--error 134
|
||||
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
|
||||
--let SEARCH_FILE= $unknown_option_log
|
||||
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= Assertion .* failed
|
||||
--source include/search_pattern_in_file.inc
|
||||
--remove_file $unknown_option_log
|
||||
--remove_file $unknown_option_log.out
|
||||
|
||||
# Restart node 2 without unknown option:
|
||||
|
||||
@@ -133,27 +148,4 @@ SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
--source include/auto_increment_offset_restore.inc
|
||||
|
||||
#
|
||||
# We should count the number of "Assertion failed" warnings
|
||||
# in the log file during test phase - to print the error message
|
||||
# if quantity of such warnings in log file increased at the end
|
||||
# of the test:
|
||||
#
|
||||
--perl
|
||||
use strict;
|
||||
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
|
||||
my $test_log_copy=$test_log . '.copy';
|
||||
open(FILE, $test_log_copy) or die("Unable to open $test_log_copy: $!\n");
|
||||
my $initial=grep(/Assertion * failed/gi,<FILE>);
|
||||
close(FILE);
|
||||
open(FILE, $test_log) or die("Unable to open $test_log: $!\n");
|
||||
my $count_warnings=grep(/Assertion * failed/gi,<FILE>);
|
||||
close(FILE);
|
||||
if ($count_warnings != $initial) {
|
||||
my $diff=$count_warnings-$initial;
|
||||
print "Assertion failed $diff times.\n";
|
||||
}
|
||||
EOF
|
||||
--remove_file $TEST_LOG.copy
|
||||
|
||||
--source include/auto_increment_offset_restore.inc
|
||||
|
||||
122
mysql-test/suite/innodb/r/buf_pool_resize_pcur_restore.result
Normal file
122
mysql-test/suite/innodb/r/buf_pool_resize_pcur_restore.result
Normal file
@@ -0,0 +1,122 @@
|
||||
create table t1 (pk int primary key, val varchar(255) not null)
|
||||
engine=innodb stats_persistent=0 charset=latin1;
|
||||
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t2 like t1;
|
||||
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t3 (pk int primary key) engine=innodb stats_persistent=0
|
||||
partition by hash(pk) partitions 256;
|
||||
insert into t3 select seq from seq_0_to_255;
|
||||
create table t4 (a int primary key, b varchar(7000)) engine=innodb
|
||||
stats_persistent=0;
|
||||
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
|
||||
create function hold(x int) returns int
|
||||
begin
|
||||
if x = 1 then
|
||||
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
|
||||
end if;
|
||||
return 1;
|
||||
end|
|
||||
create procedure read_into_first_new_block(tbl varchar(64))
|
||||
begin
|
||||
declare attempts int default 5;
|
||||
declare blk bigint default -1;
|
||||
declare p int;
|
||||
set @read_tbl= concat('select count(*) into @n from ', tbl);
|
||||
prepare read_tbl from @read_tbl;
|
||||
while blk != @first_new do
|
||||
if attempts = 0 then
|
||||
signal sqlstate '45000' set message_text= 'Wrong placement';
|
||||
end if;
|
||||
set attempts= attempts - 1;
|
||||
if blk != -1 then
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
end if;
|
||||
# Evict all pages of t1, t2 and t3.
|
||||
select sum(length(b)) into @n from t4;
|
||||
if @first_new = 0 then
|
||||
set @first_new= (select pool_size
|
||||
from information_schema.innodb_buffer_pool_stats);
|
||||
end if;
|
||||
set global innodb_buffer_pool_size= @new_size;
|
||||
# A grow appends the new blocks to the end of the free list. Read pages
|
||||
# of t3 until only the new blocks are free.
|
||||
set p= 0;
|
||||
while (select free_buffers > pool_size - @first_new
|
||||
from information_schema.innodb_buffer_pool_stats) do
|
||||
if p = 256 then
|
||||
signal sqlstate '45000' set message_text= 'Out of partitions';
|
||||
end if;
|
||||
select pk into @n from t3 where pk= p;
|
||||
set p= p + 1;
|
||||
end while;
|
||||
execute read_tbl;
|
||||
select block_id into blk from information_schema.innodb_buffer_page
|
||||
where table_name = concat('`test`.`', tbl, '`');
|
||||
end while;
|
||||
deallocate prepare read_tbl;
|
||||
end|
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
set @old_size= @@global.innodb_buffer_pool_size;
|
||||
set @new_size= 12582912;
|
||||
set @first_new= 0;
|
||||
set @save_scan_depth= @@global.innodb_lru_scan_depth;
|
||||
set global innodb_lru_scan_depth= 100;
|
||||
#
|
||||
# A read that continues from a stale position
|
||||
#
|
||||
# Read the page of t1 into the first new block.
|
||||
call read_into_first_new_block('t1');
|
||||
connect con1,localhost,root;
|
||||
select group_concat(pk) from t1 where hold(pk);
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
# Reorganize the page while the read holds a stored position on pk=1.
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t1 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
# Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Read the page of t1 into the same block again.
|
||||
call read_into_first_new_block('t1');
|
||||
# Resume the read, which must return pk=2 to pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
group_concat(pk)
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
|
||||
connection default;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
#
|
||||
# An UPDATE that continues from a stale position
|
||||
#
|
||||
# Read the page of t2 into the first new block.
|
||||
call read_into_first_new_block('t2');
|
||||
connection con1;
|
||||
update t2 set val= 'b' where hold(pk);
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
# Reorganize the page while the UPDATE holds a stored position on pk=1.
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t2 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
# Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Read the page of t2 into the same block again.
|
||||
call read_into_first_new_block('t2');
|
||||
# Resume the UPDATE, which must change pk=1, not pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
affected rows: 21
|
||||
info: Rows matched: 21 Changed: 21 Warnings: 0
|
||||
disconnect con1;
|
||||
connection default;
|
||||
select group_concat(pk) from t2 where val = 'b';
|
||||
group_concat(pk)
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
|
||||
set debug_sync= 'reset';
|
||||
drop procedure read_into_first_new_block;
|
||||
drop function hold;
|
||||
drop table t1, t2, t3, t4;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_lru_scan_depth= @save_scan_depth;
|
||||
116
mysql-test/suite/innodb/r/skip_locked_wait_granted.result
Normal file
116
mysql-test/suite/innodb/r/skip_locked_wait_granted.result
Normal file
@@ -0,0 +1,116 @@
|
||||
#
|
||||
# MDEV-40123 Assertion failure after lock wait is unblocked in SKIP LOCKED
|
||||
#
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, b INT, KEY(a)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1,1,1),(2,2,2),(3,3,3);
|
||||
connect con1,localhost,root;
|
||||
connect con2,localhost,root;
|
||||
# The lock on the clustered index record is granted after the wait
|
||||
connection con1;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 WHERE pk=1 FOR UPDATE;
|
||||
b
|
||||
1
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con2;
|
||||
b
|
||||
1
|
||||
2
|
||||
3
|
||||
connection con1;
|
||||
SELECT pk FROM t1 WHERE pk=1 FOR UPDATE SKIP LOCKED;
|
||||
pk
|
||||
connection con2;
|
||||
COMMIT;
|
||||
# The granted lock covers a row whose non-key column was updated
|
||||
connection con1;
|
||||
BEGIN;
|
||||
UPDATE t1 SET b=10 WHERE pk=1;
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con2;
|
||||
b
|
||||
10
|
||||
2
|
||||
3
|
||||
connection con1;
|
||||
SELECT pk FROM t1 WHERE pk=1 FOR UPDATE SKIP LOCKED;
|
||||
pk
|
||||
connection con2;
|
||||
COMMIT;
|
||||
# The lock on the delete-marked secondary index record is granted
|
||||
connection con1;
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE pk=1;
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con2;
|
||||
b
|
||||
2
|
||||
3
|
||||
COMMIT;
|
||||
# The lock is still held: the record is skipped
|
||||
connection con1;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 WHERE pk=2 FOR UPDATE;
|
||||
b
|
||||
2
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
b
|
||||
3
|
||||
COMMIT;
|
||||
connection con1;
|
||||
COMMIT;
|
||||
# A deadlock is reported while waiting for the lock
|
||||
CREATE TABLE t2 (x INT) ENGINE=InnoDB;
|
||||
connection con1;
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES (1),(2),(3),(4),(5);
|
||||
SELECT b FROM t1 WHERE pk=2 FOR UPDATE;
|
||||
b
|
||||
2
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
SET DEBUG_SYNC='deadlock_report_before_lock_releasing SIGNAL deadlock';
|
||||
SELECT pk FROM t1 FORCE INDEX (a) WHERE a=2 FOR UPDATE;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR deadlock';
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con2;
|
||||
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
|
||||
connection con1;
|
||||
pk
|
||||
2
|
||||
disconnect con2;
|
||||
connection con1;
|
||||
COMMIT;
|
||||
disconnect con1;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='RESET';
|
||||
DROP TABLE t1, t2;
|
||||
# End of 11.4 tests
|
||||
@@ -0,0 +1,3 @@
|
||||
--innodb-buffer-pool-size=8M
|
||||
--innodb-buffer-pool-size-max=16M
|
||||
--innodb-read-ahead-threshold=0
|
||||
173
mysql-test/suite/innodb/t/buf_pool_resize_pcur_restore.test
Normal file
173
mysql-test/suite/innodb/t/buf_pool_resize_pcur_restore.test
Normal file
@@ -0,0 +1,173 @@
|
||||
#
|
||||
# A persistent cursor that is restored after the buffer pool shrank and
|
||||
# grew again
|
||||
#
|
||||
# btr_pcur_t::restore_position() accepts the stored position without a
|
||||
# search when the block descriptor that it remembers holds the same page
|
||||
# id and the same modify_clock. The descriptors are at fixed addresses,
|
||||
# and a grow initializes the descriptors that an earlier shrink withdrew.
|
||||
# If that reset modify_clock, a page that is read into such a descriptor
|
||||
# after the grow could have the modify_clock that a cursor stored before
|
||||
# the shrink, when the page had been read into the same descriptor after
|
||||
# an earlier grow. The page can have changed in between, and the cursor
|
||||
# would continue from a stale record pointer.
|
||||
#
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_innodb_16k.inc
|
||||
--source include/have_partition.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/have_debug_sync.inc
|
||||
--source include/not_embedded.inc
|
||||
--source include/count_sessions.inc
|
||||
|
||||
# One leaf page each, with the records in descending key order in the page.
|
||||
# A reorganization writes them in key order, so pk=20 moves to the offset
|
||||
# of pk=1.
|
||||
create table t1 (pk int primary key, val varchar(255) not null)
|
||||
engine=innodb stats_persistent=0 charset=latin1;
|
||||
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t2 like t1;
|
||||
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
|
||||
# One page per partition. A lookup of an evicted partition reads one page.
|
||||
# The page cleaner keeps about innodb_lru_scan_depth blocks free, and the
|
||||
# partitions must outnumber them.
|
||||
create table t3 (pk int primary key) engine=innodb stats_persistent=0
|
||||
partition by hash(pk) partitions 256;
|
||||
insert into t3 select seq from seq_0_to_255;
|
||||
|
||||
# Three times innodb_buffer_pool_size, to evict everything else.
|
||||
create table t4 (a int primary key, b varchar(7000)) engine=innodb
|
||||
stats_persistent=0;
|
||||
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
|
||||
|
||||
delimiter |;
|
||||
# Stop the statement after it read pk=1 and stored its position.
|
||||
create function hold(x int) returns int
|
||||
begin
|
||||
if x = 1 then
|
||||
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
|
||||
end if;
|
||||
return 1;
|
||||
end|
|
||||
|
||||
# Grow the buffer pool from @old_size to @new_size and read the only page
|
||||
# of the table tbl into the first block that the grow adds. A concurrent
|
||||
# page read can take that block; then shrink and try again. The first call
|
||||
# sets @first_new, the block id of the first new block. Later calls require
|
||||
# the same @first_new.
|
||||
create procedure read_into_first_new_block(tbl varchar(64))
|
||||
begin
|
||||
declare attempts int default 5;
|
||||
declare blk bigint default -1;
|
||||
declare p int;
|
||||
set @read_tbl= concat('select count(*) into @n from ', tbl);
|
||||
prepare read_tbl from @read_tbl;
|
||||
while blk != @first_new do
|
||||
if attempts = 0 then
|
||||
signal sqlstate '45000' set message_text= 'Wrong placement';
|
||||
end if;
|
||||
set attempts= attempts - 1;
|
||||
if blk != -1 then
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
end if;
|
||||
# Evict all pages of t1, t2 and t3.
|
||||
select sum(length(b)) into @n from t4;
|
||||
if @first_new = 0 then
|
||||
set @first_new= (select pool_size
|
||||
from information_schema.innodb_buffer_pool_stats);
|
||||
end if;
|
||||
set global innodb_buffer_pool_size= @new_size;
|
||||
# A grow appends the new blocks to the end of the free list. Read pages
|
||||
# of t3 until only the new blocks are free.
|
||||
set p= 0;
|
||||
while (select free_buffers > pool_size - @first_new
|
||||
from information_schema.innodb_buffer_pool_stats) do
|
||||
if p = 256 then
|
||||
signal sqlstate '45000' set message_text= 'Out of partitions';
|
||||
end if;
|
||||
select pk into @n from t3 where pk= p;
|
||||
set p= p + 1;
|
||||
end while;
|
||||
execute read_tbl;
|
||||
select block_id into blk from information_schema.innodb_buffer_page
|
||||
where table_name = concat('`test`.`', tbl, '`');
|
||||
end while;
|
||||
deallocate prepare read_tbl;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
# Purge resets DB_TRX_ID in the inserted records. Let it finish, so that it
|
||||
# reads no pages into the free blocks that the procedure counts.
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
|
||||
set @old_size= @@global.innodb_buffer_pool_size;
|
||||
set @new_size= 12582912;
|
||||
set @first_new= 0;
|
||||
# The minimum, which keeps fewer blocks free than t3 has partitions.
|
||||
set @save_scan_depth= @@global.innodb_lru_scan_depth;
|
||||
set global innodb_lru_scan_depth= 100;
|
||||
|
||||
--echo #
|
||||
--echo # A read that continues from a stale position
|
||||
--echo #
|
||||
--echo # Read the page of t1 into the first new block.
|
||||
call read_into_first_new_block('t1');
|
||||
connect (con1,localhost,root);
|
||||
--send select group_concat(pk) from t1 where hold(pk)
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
--echo # Reorganize the page while the read holds a stored position on pk=1.
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t1 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
--echo # Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
--echo # Read the page of t1 into the same block again.
|
||||
call read_into_first_new_block('t1');
|
||||
--echo # Resume the read, which must return pk=2 to pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
--reap
|
||||
connection default;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Purge was blocked by the read view of the paused read. Let it finish, so
|
||||
# that it reads no pages into the new blocks of the next case.
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
|
||||
--echo #
|
||||
--echo # An UPDATE that continues from a stale position
|
||||
--echo #
|
||||
--echo # Read the page of t2 into the first new block.
|
||||
call read_into_first_new_block('t2');
|
||||
connection con1;
|
||||
--send update t2 set val= 'b' where hold(pk)
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
--echo # Reorganize the page while the UPDATE holds a stored position on pk=1.
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t2 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
--echo # Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
--echo # Read the page of t2 into the same block again.
|
||||
call read_into_first_new_block('t2');
|
||||
--echo # Resume the UPDATE, which must change pk=1, not pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
--enable_info
|
||||
--reap
|
||||
--disable_info
|
||||
disconnect con1;
|
||||
connection default;
|
||||
select group_concat(pk) from t2 where val = 'b';
|
||||
|
||||
set debug_sync= 'reset';
|
||||
drop procedure read_into_first_new_block;
|
||||
drop function hold;
|
||||
drop table t1, t2, t3, t4;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_lru_scan_depth= @save_scan_depth;
|
||||
--source include/wait_until_count_sessions.inc
|
||||
@@ -36,7 +36,9 @@ send SET GLOBAL innodb_buffer_pool_size=8388608;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR blocked';
|
||||
# IA-32: 504 blocks; IA-32 glibc SUX_LOCK_GENERIC: 500; macOS SUX_LOCK_GENERIC: 498
|
||||
--replace_regex /([45]..)\/\1/505\/505/
|
||||
# Other threads may already have withdrawn some blocks before
|
||||
# buf_pool_shrink_before_wakeup.
|
||||
--replace_regex /\([0-9]+\/[45][0-9][0-9]\)/(505\/505)/
|
||||
SHOW STATUS LIKE 'innodb_buffer_pool_resize_status';
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con1;
|
||||
|
||||
128
mysql-test/suite/innodb/t/skip_locked_wait_granted.test
Normal file
128
mysql-test/suite/innodb/t/skip_locked_wait_granted.test
Normal file
@@ -0,0 +1,128 @@
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_debug_sync.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40123 Assertion failure after lock wait is unblocked in SKIP LOCKED
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, a INT, b INT, KEY(a)) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1,1,1),(2,2,2),(3,3,3);
|
||||
|
||||
connect con1,localhost,root;
|
||||
connect con2,localhost,root;
|
||||
|
||||
--echo # The lock on the clustered index record is granted after the wait
|
||||
connection con1;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 WHERE pk=1 FOR UPDATE;
|
||||
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
send SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
|
||||
connection con2;
|
||||
reap;
|
||||
connection con1;
|
||||
SELECT pk FROM t1 WHERE pk=1 FOR UPDATE SKIP LOCKED;
|
||||
connection con2;
|
||||
COMMIT;
|
||||
|
||||
--echo # The granted lock covers a row whose non-key column was updated
|
||||
connection con1;
|
||||
BEGIN;
|
||||
UPDATE t1 SET b=10 WHERE pk=1;
|
||||
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
send SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
|
||||
connection con2;
|
||||
reap;
|
||||
connection con1;
|
||||
SELECT pk FROM t1 WHERE pk=1 FOR UPDATE SKIP LOCKED;
|
||||
connection con2;
|
||||
COMMIT;
|
||||
|
||||
--echo # The lock on the delete-marked secondary index record is granted
|
||||
connection con1;
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE pk=1;
|
||||
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
send SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
COMMIT;
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
|
||||
connection con2;
|
||||
reap;
|
||||
COMMIT;
|
||||
|
||||
--echo # The lock is still held: the record is skipped
|
||||
connection con1;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 WHERE pk=2 FOR UPDATE;
|
||||
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
COMMIT;
|
||||
|
||||
connection con1;
|
||||
COMMIT;
|
||||
|
||||
--echo # A deadlock is reported while waiting for the lock
|
||||
CREATE TABLE t2 (x INT) ENGINE=InnoDB;
|
||||
connection con1;
|
||||
BEGIN;
|
||||
# Make con1 heavier than con2, so that con2 is chosen as the deadlock victim
|
||||
INSERT INTO t2 VALUES (1),(2),(3),(4),(5);
|
||||
SELECT b FROM t1 WHERE pk=2 FOR UPDATE;
|
||||
|
||||
connection con2;
|
||||
BEGIN;
|
||||
SET DEBUG_SYNC='lock_trx_handle_wait_enter SIGNAL waiting WAIT_FOR go';
|
||||
send SELECT b FROM t1 FORCE INDEX (a) ORDER BY a FOR UPDATE SKIP LOCKED;
|
||||
|
||||
connection con1;
|
||||
SET DEBUG_SYNC='now WAIT_FOR waiting';
|
||||
SET DEBUG_SYNC='deadlock_report_before_lock_releasing SIGNAL deadlock';
|
||||
send SELECT pk FROM t1 FORCE INDEX (a) WHERE a=2 FOR UPDATE;
|
||||
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR deadlock';
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
|
||||
connection con2;
|
||||
--error ER_LOCK_DEADLOCK
|
||||
reap;
|
||||
|
||||
connection con1;
|
||||
reap;
|
||||
|
||||
disconnect con2;
|
||||
connection con1;
|
||||
COMMIT;
|
||||
disconnect con1;
|
||||
|
||||
connection default;
|
||||
SET DEBUG_SYNC='RESET';
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
84
mysql-test/suite/multi_source/mix_semisync_async.inc
Normal file
84
mysql-test/suite/multi_source/mix_semisync_async.inc
Normal file
@@ -0,0 +1,84 @@
|
||||
# Test replicating from an additional connection of semi-sync mode `$mode2`
|
||||
# does not interfere with the active connection of mode `$mode1`.
|
||||
#
|
||||
# Pre-Conditions:
|
||||
# * On connection `slave`.
|
||||
# * CHANGE MASTER `master1` and `master2` set up to
|
||||
# replicate from the connection with the same name.
|
||||
# * None of the replication threads are running.
|
||||
# * User variables `$mode1` and `$mode2` contain
|
||||
# corresponding `@@rpl_semi_sync_slave_enabled` values.
|
||||
#
|
||||
# Post-Conditions:
|
||||
# * On connection `slave`.
|
||||
# * None of the replication threads are running.
|
||||
# * @@default_master_connection = 'master1',
|
||||
# @@rpl_semi_sync_slave_enabled = $mode2
|
||||
|
||||
--let $status_var= Rpl_semi_sync_master_clients
|
||||
--let $status_var_value= 1
|
||||
|
||||
|
||||
# Start `master1`
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
--eval SET @@GLOBAL.rpl_semi_sync_slave_enabled= $mode1
|
||||
--source include/start_slave.inc
|
||||
|
||||
# Start `master2`
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
--eval SET @@GLOBAL.rpl_semi_sync_slave_enabled= $mode2
|
||||
--source include/start_slave.inc
|
||||
|
||||
|
||||
# TODO MDEV-40941:
|
||||
# `Rpl_semi_sync_slave_status` does not refer to `@@default_master_connection`,
|
||||
# but rather the most recent START SLAVE (`$mode2` in this case).
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
|
||||
|
||||
# Verify `master2`
|
||||
--connection master2
|
||||
if ($mode2)
|
||||
{
|
||||
--source include/wait_for_status_var.inc
|
||||
}
|
||||
CREATE TABLE t2 (a INT);
|
||||
DROP TABLE t2;
|
||||
--save_master_pos
|
||||
--connection slave
|
||||
--sync_with_master 0, master2
|
||||
|
||||
# Verify `master1`
|
||||
--connection master1
|
||||
if ($mode1)
|
||||
{
|
||||
--source include/wait_for_status_var.inc
|
||||
}
|
||||
CREATE TABLE t1 (a INT);
|
||||
--save_master_pos
|
||||
--connection slave
|
||||
--sync_with_master 0, master1
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
|
||||
|
||||
# Stop `master2`
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
--source include/stop_slave.inc
|
||||
# TODO MDEV-40941:
|
||||
# `Rpl_semi_sync_slave_status` shows OFF after STOP SLAVE on any connection.
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
|
||||
# Verify `master1`
|
||||
--connection master1
|
||||
DROP TABLE t1;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_master_clients';
|
||||
--save_master_pos
|
||||
--connection slave
|
||||
--sync_with_master 0, master1
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
|
||||
# Stop `master1`
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
--source include/stop_slave.inc
|
||||
109
mysql-test/suite/multi_source/mix_semisync_async.result
Normal file
109
mysql-test/suite/multi_source/mix_semisync_async.result
Normal file
@@ -0,0 +1,109 @@
|
||||
# Setup
|
||||
connect master1, 127.0.0.1, root, , , $SERVER_MYPORT_1;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 1;
|
||||
SET @@SESSION.gtid_domain_id= 1;
|
||||
RESET MASTER;
|
||||
connect master2, 127.0.0.1, root, , , $SERVER_MYPORT_2;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 1;
|
||||
SET @@SESSION.gtid_domain_id= 2;
|
||||
RESET MASTER;
|
||||
connect slave, 127.0.0.1, root, , , $SERVER_MYPORT_3;
|
||||
SET @@GLOBAL.gtid_slave_pos= '';
|
||||
CHANGE MASTER 'master1' TO
|
||||
master_host='127.0.0.1', master_port=SERVER_MYPORT_1, master_user='root';
|
||||
CHANGE MASTER 'master2' TO
|
||||
master_host='127.0.0.1', master_port=SERVER_MYPORT_2, master_user='root';
|
||||
# Test semi-sync replication during async replication
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0;
|
||||
include/start_slave.inc
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
SET @@GLOBAL.rpl_semi_sync_slave_enabled= 1;
|
||||
include/start_slave.inc
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status ON
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status ON
|
||||
connection master2;
|
||||
CREATE TABLE t2 (a INT);
|
||||
DROP TABLE t2;
|
||||
connection slave;
|
||||
connection master1;
|
||||
CREATE TABLE t1 (a INT);
|
||||
connection slave;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_send_ack 2
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
include/stop_slave.inc
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status OFF
|
||||
connection master1;
|
||||
DROP TABLE t1;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_master_clients';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_master_clients 0
|
||||
connection slave;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_send_ack 2
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
include/stop_slave.inc
|
||||
# Test the other order
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
SET @@GLOBAL.rpl_semi_sync_slave_enabled= 1;
|
||||
include/start_slave.inc
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
SET @@GLOBAL.rpl_semi_sync_slave_enabled= 0;
|
||||
include/start_slave.inc
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status OFF
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status OFF
|
||||
connection master2;
|
||||
CREATE TABLE t2 (a INT);
|
||||
DROP TABLE t2;
|
||||
connection slave;
|
||||
connection master1;
|
||||
CREATE TABLE t1 (a INT);
|
||||
connection slave;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_send_ack 1
|
||||
SET @@SESSION.default_master_connection= 'master2';
|
||||
include/stop_slave.inc
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_status OFF
|
||||
connection master1;
|
||||
DROP TABLE t1;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_master_clients';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_master_clients 1
|
||||
connection slave;
|
||||
SHOW STATUS LIKE 'Rpl_semi_sync_slave_send_ack';
|
||||
Variable_name Value
|
||||
Rpl_semi_sync_slave_send_ack 2
|
||||
SET @@SESSION.default_master_connection= 'master1';
|
||||
include/stop_slave.inc
|
||||
# Cleanup
|
||||
RESET SLAVE 'master1' ALL;
|
||||
RESET SLAVE 'master2' ALL;
|
||||
disconnect slave;
|
||||
connection master1;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 1;
|
||||
disconnect master1;
|
||||
connection master2;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 1;
|
||||
disconnect master2;
|
||||
63
mysql-test/suite/multi_source/mix_semisync_async.test
Normal file
63
mysql-test/suite/multi_source/mix_semisync_async.test
Normal file
@@ -0,0 +1,63 @@
|
||||
# MDEV-32947 Async conflicts with semi-sync in multi-source
|
||||
#
|
||||
# With a async master and a semi-sync master,
|
||||
# test replication does not mix async and semi-sync regardless of order
|
||||
|
||||
--source include/have_binlog_format_mixed.inc # format-agnostic
|
||||
--let $orig_wait_no_slave= `SELECT @@GLOBAL.rpl_semi_sync_master_wait_no_slave`
|
||||
|
||||
--echo # Setup
|
||||
# Enable semi-sync on both masters;
|
||||
# the slave will disable semi-sync on one of its connections.
|
||||
|
||||
--connect (master1, 127.0.0.1, root, , , $SERVER_MYPORT_1)
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 1;
|
||||
SET @@SESSION.gtid_domain_id= 1;
|
||||
RESET MASTER;
|
||||
|
||||
--connect (master2, 127.0.0.1, root, , , $SERVER_MYPORT_2)
|
||||
SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= 0;
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 1;
|
||||
SET @@SESSION.gtid_domain_id= 2;
|
||||
RESET MASTER;
|
||||
|
||||
--connect (slave, 127.0.0.1, root, , , $SERVER_MYPORT_3)
|
||||
--disable_warnings
|
||||
SET @@GLOBAL.gtid_slave_pos= '';
|
||||
--enable_warnings
|
||||
|
||||
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
|
||||
eval CHANGE MASTER 'master1' TO
|
||||
master_host='127.0.0.1', master_port=$SERVER_MYPORT_1, master_user='root';
|
||||
--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2
|
||||
eval CHANGE MASTER 'master2' TO
|
||||
master_host='127.0.0.1', master_port=$SERVER_MYPORT_2, master_user='root';
|
||||
|
||||
|
||||
--echo # Test semi-sync replication during async replication
|
||||
--let $mode1= 0
|
||||
--let $mode2= 1
|
||||
--source mix_semisync_async.inc
|
||||
|
||||
--echo # Test the other order
|
||||
--let $mode1= 1
|
||||
--let $mode2= 0
|
||||
--source mix_semisync_async.inc
|
||||
|
||||
|
||||
--echo # Cleanup
|
||||
|
||||
RESET SLAVE 'master1' ALL;
|
||||
RESET SLAVE 'master2' ALL;
|
||||
--disconnect slave
|
||||
|
||||
--connection master1
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 0;
|
||||
--eval SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= $orig_wait_no_slave
|
||||
--disconnect master1
|
||||
|
||||
--connection master2
|
||||
SET @@GLOBAL.rpl_semi_sync_master_enabled= 0;
|
||||
--eval SET @@GLOBAL.rpl_semi_sync_master_wait_no_slave= $orig_wait_no_slave
|
||||
--disconnect master2
|
||||
@@ -1,9 +1,10 @@
|
||||
SET @saved_replicate_do_db= @@GLOBAL.replicate_do_db;
|
||||
SET @saved_slave_parallel_mode= @@GLOBAL.slave_parallel_mode;
|
||||
SET @saved_default_master_connection= @@SESSION.default_master_connection;
|
||||
CHANGE MASTER 'name' TO master_host='127.0.0.1', master_user='root';
|
||||
SET @@GLOBAL.replicate_do_db= 'unnamed';
|
||||
SET @@SESSION.default_master_connection= 'name';
|
||||
SET @@GLOBAL.replicate_do_db= 'named';
|
||||
SET @@GLOBAL.``.replicate_do_db= 'unnamed';
|
||||
SELECT @@GLOBAL.name.replicate_do_db;
|
||||
@@GLOBAL.name.replicate_do_db
|
||||
named
|
||||
@@ -13,7 +14,19 @@ named
|
||||
SELECT @@GLOBAL.``.replicate_do_db;
|
||||
@@GLOBAL.``.replicate_do_db
|
||||
unnamed
|
||||
RESET SLAVE ALL;
|
||||
SET @@GLOBAL.slave_parallel_mode= 'minimal';
|
||||
SET @@GLOBAL.``.slave_parallel_mode= 'aggressive';
|
||||
SELECT @@GLOBAL.name.slave_parallel_mode;
|
||||
@@GLOBAL.name.slave_parallel_mode
|
||||
minimal
|
||||
SELECT @@GLOBAL.slave_parallel_mode;
|
||||
@@GLOBAL.slave_parallel_mode
|
||||
minimal
|
||||
SELECT @@GLOBAL.``.slave_parallel_mode;
|
||||
@@GLOBAL.``.slave_parallel_mode
|
||||
aggressive
|
||||
RESET SLAVE 'name' ALL;
|
||||
SET @@SESSION.default_master_connection= @saved_default_master_connection;
|
||||
SET @@GLOBAL.slave_parallel_mode= @saved_slave_parallel_mode;
|
||||
SET @@GLOBAL.replicate_do_db= @saved_replicate_do_db;
|
||||
# End of 10.11 tests
|
||||
# End of 11.4 tests
|
||||
@@ -1,25 +1,37 @@
|
||||
--source include/not_embedded.inc
|
||||
|
||||
# MDEV-40296: SELECT @@GLOBAL.replicate_do_db ignores default_master_connection
|
||||
# MDEV-40297: SET changes @@default_master_connection's system variable if the explicit connection name is blank
|
||||
|
||||
# Setup
|
||||
SET @saved_replicate_do_db= @@GLOBAL.replicate_do_db;
|
||||
SET @saved_slave_parallel_mode= @@GLOBAL.slave_parallel_mode;
|
||||
SET @saved_default_master_connection= @@SESSION.default_master_connection;
|
||||
|
||||
CHANGE MASTER 'name' TO master_host='127.0.0.1', master_user='root';
|
||||
|
||||
SET @@GLOBAL.replicate_do_db= 'unnamed';
|
||||
SET @@SESSION.default_master_connection= 'name';
|
||||
|
||||
# replicate_do_db
|
||||
SET @@GLOBAL.replicate_do_db= 'named';
|
||||
SET @@GLOBAL.``.replicate_do_db= 'unnamed';
|
||||
|
||||
SELECT @@GLOBAL.name.replicate_do_db;
|
||||
SELECT @@GLOBAL.replicate_do_db;
|
||||
# MDEV-40297: Explicit empty connection name
|
||||
SELECT @@GLOBAL.``.replicate_do_db;
|
||||
|
||||
# slave_parallel_mode
|
||||
SET @@GLOBAL.slave_parallel_mode= 'minimal';
|
||||
SET @@GLOBAL.``.slave_parallel_mode= 'aggressive';
|
||||
|
||||
SELECT @@GLOBAL.name.slave_parallel_mode;
|
||||
SELECT @@GLOBAL.slave_parallel_mode;
|
||||
SELECT @@GLOBAL.``.slave_parallel_mode;
|
||||
|
||||
# Cleanup
|
||||
RESET SLAVE ALL;
|
||||
RESET SLAVE 'name' ALL;
|
||||
SET @@SESSION.default_master_connection= @saved_default_master_connection;
|
||||
SET @@GLOBAL.slave_parallel_mode= @saved_slave_parallel_mode;
|
||||
SET @@GLOBAL.replicate_do_db= @saved_replicate_do_db;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
--echo # End of 11.4 tests
|
||||
@@ -5,6 +5,10 @@ source include/have_innodb.inc;
|
||||
source include/have_debug_sync.inc;
|
||||
create table t1 (f text, vf tinytext as (f), key (vf(64))) engine=innodb;
|
||||
insert t1 (f) values ('foo');
|
||||
# Purge may open t1 into the table cache after FLUSH TABLES
|
||||
--disable_query_log
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
--enable_query_log
|
||||
flush tables;
|
||||
connect con1,localhost,root,,test;
|
||||
set debug_sync='TABLE_after_field_clone WAIT_FOR go';
|
||||
|
||||
@@ -221,3 +221,12 @@
|
||||
# INSERT .. ON DUPLICATE KEY UPDATE (ODKU)
|
||||
set timestamp= unix_timestamp('2000-01-01 00:00:00');
|
||||
create or replace table t1 (x int primary key) with system versioning
|
||||
@@ -3663,7 +3456,7 @@
|
||||
# (except heap combination)
|
||||
explain extended select max(id) from t1;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
-1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
|
||||
+1 SIMPLE t1 ALL NULL NULL NULL NULL 2 100.00
|
||||
Warnings:
|
||||
Note 1003 select max(`test`.`t1`.`id`) AS `max(id)` from `test`.`t1`
|
||||
drop tables t1;
|
||||
|
||||
@@ -373,10 +373,10 @@ set timestamp=unix_timestamp('2001-02-04 10:30:10');
|
||||
update t1 set i=6 where i=5;
|
||||
select subpartition_name, partition_description, table_rows from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
subpartition_name partition_description table_rows
|
||||
p1sp0 2001-02-04 00:00:00 1
|
||||
p1sp1 2001-02-04 00:00:00 0
|
||||
p0sp0 2001-02-05 00:00:00 1
|
||||
p0sp1 2001-02-05 00:00:00 1
|
||||
p1sp0 2001-02-04 00:00:00 1
|
||||
p1sp1 2001-02-04 00:00:00 0
|
||||
p2sp0 2001-02-06 00:00:00 0
|
||||
p2sp1 2001-02-06 00:00:00 0
|
||||
pnsp0 CURRENT 0
|
||||
@@ -386,8 +386,8 @@ i
|
||||
1
|
||||
select * from t1 partition (p0);
|
||||
i
|
||||
5
|
||||
2
|
||||
5
|
||||
select * from t1 partition (p2);
|
||||
i
|
||||
alter table t1 rebuild partition p0, p1, p2;
|
||||
@@ -396,8 +396,8 @@ i
|
||||
1
|
||||
select * from t1 partition (p0);
|
||||
i
|
||||
5
|
||||
2
|
||||
5
|
||||
select * from t1 partition (p2);
|
||||
i
|
||||
## pruning check
|
||||
@@ -409,7 +409,7 @@ i
|
||||
6
|
||||
explain partitions select * from t1;
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 pn_pnsp0,pn_pnsp1 ALL NULL NULL NULL NULL # Using where
|
||||
1 SIMPLE t1 pn_pnsp0,pn_pnsp1 ALL NULL NULL NULL NULL #
|
||||
explain partitions select * from t1 for system_time as of '2001-02-04 10:20:30';
|
||||
id select_type table partitions type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 p0_p0sp0,p0_p0sp1,p2_p2sp0,p2_p2sp1,pn_pnsp0,pn_pnsp1 ALL NULL NULL NULL NULL # Using where
|
||||
@@ -1124,6 +1124,23 @@ update v1 set f= '';
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
delete from v1;
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
# system_versioning_asof is ignored by DML, so it does not cause ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
create or replace view v1 as select * from t1;
|
||||
set system_versioning_asof= '2000-01-01 00:00:00';
|
||||
select * from v1;
|
||||
f
|
||||
select * from v1 for update;
|
||||
f
|
||||
update v1 set f= '';
|
||||
affected rows: 1
|
||||
info: Rows matched: 1 Changed: 1 Inserted: 1 Warnings: 0
|
||||
Warnings:
|
||||
Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p2`) is out of LIMIT, need more HISTORY partitions
|
||||
delete from v1;
|
||||
affected rows: 1
|
||||
Warnings:
|
||||
Warning 4114 Versioned table `test`.`t1`: last HISTORY partition (`p2`) is out of LIMIT, need more HISTORY partitions
|
||||
set system_versioning_asof= default;
|
||||
drop view v1;
|
||||
drop table t1;
|
||||
#
|
||||
@@ -2200,9 +2217,9 @@ t1 CREATE TABLE `t1` (
|
||||
PARTITION `pn` CURRENT ENGINE = X)
|
||||
select * from t1;
|
||||
x
|
||||
3
|
||||
13
|
||||
23
|
||||
3
|
||||
select * from t1 for system_time all order by x;
|
||||
x
|
||||
3
|
||||
@@ -3733,22 +3750,199 @@ alter table t1 add y int partition by system_time interval 1 hour
|
||||
starts '2000-01-01 00:00:00';
|
||||
affected rows: 3
|
||||
info: Records: 3 Duplicates: 0 Warnings: 0
|
||||
select x, y, row_end from t1 for system_time all order by x;
|
||||
select x, y, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
|
||||
from t1 for system_time all order by x;
|
||||
x y row_end
|
||||
0 NULL 2000-01-01 01:30:00.000000
|
||||
1 NULL 2000-01-01 02:30:00.000000
|
||||
2 NULL 2106-02-07 06:28:15.999999
|
||||
2 NULL CURRENT
|
||||
# LIMIT with DROP COLUMN: the table is rebuilt
|
||||
alter table t1 drop y partition by system_time limit 100;
|
||||
affected rows: 3
|
||||
info: Records: 3 Duplicates: 0 Warnings: 0
|
||||
select x, row_end from t1 for system_time all order by x;
|
||||
select x, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
|
||||
from t1 for system_time all order by x;
|
||||
x row_end
|
||||
0 2000-01-01 01:30:00.000000
|
||||
1 2000-01-01 02:30:00.000000
|
||||
2 2106-02-07 06:28:15.999999
|
||||
2 CURRENT
|
||||
set timestamp= default;
|
||||
set time_zone= default;
|
||||
drop table t1;
|
||||
# End of 10.11 tests
|
||||
#
|
||||
# MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
#
|
||||
create table t1 (
|
||||
id int,
|
||||
ts timestamp(6) generated always as row start invisible,
|
||||
te timestamp(6) generated always as row end invisible,
|
||||
primary key (id, te),
|
||||
period for system_time (ts, te)
|
||||
) with system versioning
|
||||
partition by system_time interval 1 month (
|
||||
partition p0 history, partition p1 history, partition pcur current);
|
||||
insert t1 values (1), (2), (0);
|
||||
delete from t1 where id = 1;
|
||||
select * from t1;
|
||||
id
|
||||
0
|
||||
2
|
||||
# Below there should be: Select tables optimized away
|
||||
# (except heap combination)
|
||||
explain extended select max(id) from t1;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
|
||||
Warnings:
|
||||
Note 1003 select max(`test`.`t1`.`id`) AS `max(id)` from `test`.`t1`
|
||||
# Exact COUNT(*) (MyISAM, heap) must not count history rows
|
||||
select count(*) from t1;
|
||||
count(*)
|
||||
2
|
||||
# UPDATE without WHERE changes only current rows
|
||||
update t1 set id= id + 10;
|
||||
select * from t1;
|
||||
id
|
||||
10
|
||||
12
|
||||
select count(*) from t1 for system_time all;
|
||||
count(*)
|
||||
5
|
||||
# sql_safe_updates rejects DELETE without WHERE even with LIMIT,
|
||||
# as for a non-versioned table
|
||||
set sql_safe_updates= 1;
|
||||
delete from t1 limit 1;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
set sql_safe_updates= default;
|
||||
# DELETE without WHERE removes only current rows
|
||||
delete from t1;
|
||||
select count(*) from t1;
|
||||
count(*)
|
||||
0
|
||||
select count(*) from t1 for system_time all;
|
||||
count(*)
|
||||
5
|
||||
drop tables t1;
|
||||
# Multi-table DELETE/UPDATE under sql_safe_updates: checks use the
|
||||
# written WHERE and the join access types, as before
|
||||
create table t1 (x int) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
create table t2 (y int);
|
||||
insert into t1 values (1), (2);
|
||||
insert into t2 values (1), (2);
|
||||
set sql_safe_updates= 1;
|
||||
delete t1 from t1, t2;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
update t1, t2 set t1.x= t2.y;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
# LIMIT does not exempt multi-table UPDATE from the full join check
|
||||
update t1, t2 set t1.x= t2.y order by t1.x limit 1;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
set sql_safe_updates= default;
|
||||
drop tables t1, t2;
|
||||
# As for a non-versioned table, an index on row_end does not exempt
|
||||
# UPDATE/DELETE without WHERE: UPDATE needs LIMIT, DELETE is rejected
|
||||
create table t1 (
|
||||
x int,
|
||||
ts timestamp(6) as row start invisible,
|
||||
te timestamp(6) as row end invisible,
|
||||
period for system_time (ts, te),
|
||||
key (te)
|
||||
) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
insert into t1 values (1), (2);
|
||||
set sql_safe_updates= 1;
|
||||
update t1 set x= x + 1;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
delete from t1;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
update t1 set x= x + 1 limit 1;
|
||||
delete from t1 limit 1;
|
||||
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
|
||||
set sql_safe_updates= default;
|
||||
drop table t1;
|
||||
# Corner cases: outer and sysvar-set FOR SYSTEM_TIME
|
||||
set timestamp= unix_timestamp('2000-01-01 00:00:00');
|
||||
create or replace table t1 (id int)
|
||||
with system versioning
|
||||
partition by system_time interval 1 hour
|
||||
starts timestamp'2000-01-01 00:00:00' (
|
||||
partition p0 history,
|
||||
partition p1 history,
|
||||
partition pn current);
|
||||
insert into t1 values (1);
|
||||
set timestamp= unix_timestamp('2000-01-01 01:30:00');
|
||||
update t1 set id= 2;
|
||||
# implicit query reads only the current partition (id=2):
|
||||
select id from t1;
|
||||
id
|
||||
2
|
||||
# outer FOR SYSTEM_TIME must propagate into derived t1 and read history (id=1):
|
||||
select * from (select id from t1) for system_time as of timestamp'2000-01-01 01:00:00' as dt;
|
||||
id
|
||||
1
|
||||
# system_versioning_asof must be honored and read history (id=1):
|
||||
set system_versioning_asof= '2000-01-01 01:00:00';
|
||||
select id from t1;
|
||||
id
|
||||
1
|
||||
set system_versioning_asof= default;
|
||||
# re-execution reads only the current partition (id=2):
|
||||
prepare s from 'select id from t1';
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
deallocate prepare s;
|
||||
create procedure sp() select id from t1;
|
||||
call sp;
|
||||
id
|
||||
2
|
||||
call sp;
|
||||
id
|
||||
2
|
||||
drop procedure sp;
|
||||
# the same through merged derived table and view (id=2):
|
||||
create view v1 as select id from t1;
|
||||
prepare s from 'select id from (select id from t1) dt';
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
deallocate prepare s;
|
||||
prepare s from 'select id from v1';
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
execute s;
|
||||
id
|
||||
2
|
||||
deallocate prepare s;
|
||||
create procedure sp() select id from (select id from t1) dt;
|
||||
call sp;
|
||||
id
|
||||
2
|
||||
call sp;
|
||||
id
|
||||
2
|
||||
drop procedure sp;
|
||||
drop view v1;
|
||||
# re-execution with system_versioning_asof reads history (id=1):
|
||||
set system_versioning_asof= '2000-01-01 01:00:00';
|
||||
prepare s from 'select id from t1';
|
||||
execute s;
|
||||
id
|
||||
1
|
||||
execute s;
|
||||
id
|
||||
1
|
||||
deallocate prepare s;
|
||||
set system_versioning_asof= default;
|
||||
set timestamp= default;
|
||||
drop table t1;
|
||||
# End of 11.4 tests
|
||||
set global innodb_stats_persistent= @save_persistent;
|
||||
|
||||
@@ -254,3 +254,24 @@ a
|
||||
DROP TABLE t1;
|
||||
SET system_versioning_asof= DEFAULT;
|
||||
# End of 10.4 tests
|
||||
#
|
||||
# MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
#
|
||||
# system_versioning_asof does not act as explicit FOR SYSTEM_TIME AS OF
|
||||
create table t1 (x int) with system versioning;
|
||||
insert into t1 values (1);
|
||||
set system_versioning_asof= '2000-01-01 00:00:00';
|
||||
# Allowed, unlike explicit FOR SYSTEM_TIME AS OF:
|
||||
select * from t1 for update;
|
||||
x
|
||||
select * from t1 for system_time as of timestamp'2000-01-01 00:00:00' for update;
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
# Not printed as FOR SYSTEM_TIME AS OF:
|
||||
explain extended select * from t1;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
|
||||
Warnings:
|
||||
Note 1003 select 1 AS `x` from dual where 0
|
||||
set system_versioning_asof= default;
|
||||
drop table t1;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -26,14 +26,17 @@ partition by range columns (x) (
|
||||
partition p1 values less than (1000));
|
||||
|
||||
insert into t1 values (3), (300);
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
select * from t1 partition (p0);
|
||||
select * from t1 partition (p1);
|
||||
|
||||
delete from t1;
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
select * from t1 partition (p0);
|
||||
select * from t1 partition (p1);
|
||||
--sorted_result
|
||||
select * from t1 for system_time all;
|
||||
select * from t1 partition (p0) for system_time all;
|
||||
select * from t1 partition (p1) for system_time all;
|
||||
@@ -140,6 +143,7 @@ alter table t1 drop partition p1;
|
||||
--error ER_VERS_WRONG_PARTS
|
||||
alter table t1 drop partition p0;
|
||||
|
||||
--sorted_result
|
||||
select x from t1;
|
||||
|
||||
--echo # rename works
|
||||
@@ -217,6 +221,7 @@ update t1 set x = x + 1;
|
||||
|
||||
--source suite/versioning/wait_system_clock.inc
|
||||
|
||||
--sorted_result
|
||||
execute select_p0;
|
||||
execute select_pn;
|
||||
|
||||
@@ -252,11 +257,14 @@ show create table t1;
|
||||
alter table t1 drop partition non_existent;
|
||||
|
||||
insert into t1 values (1), (2), (3), (4), (5), (6);
|
||||
--sorted_result
|
||||
select * from t1 partition (pn);
|
||||
delete from t1 where x < 4;
|
||||
delete from t1;
|
||||
--echo # You see warning above ^
|
||||
--sorted_result
|
||||
select * from t1 partition (p0);
|
||||
--sorted_result
|
||||
select * from t1 partition (p1);
|
||||
|
||||
insert into t1 values (7), (8);
|
||||
@@ -289,13 +297,17 @@ set @ts=(select partition_description from information_schema.partitions
|
||||
where table_schema='test' and table_name='t1' and partition_name='p0');
|
||||
|
||||
alter table t1 add column b int;
|
||||
--sorted_result
|
||||
select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
alter table t1 add partition (partition p1 history, partition p2 history);
|
||||
--sorted_result
|
||||
select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
alter table t1 drop partition p0;
|
||||
--sorted_result
|
||||
select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
--error ER_VERS_DROP_PARTITION_INTERVAL
|
||||
alter table t1 drop partition p2;
|
||||
--sorted_result
|
||||
select partition_name,partition_ordinal_position,partition_method,timediff(partition_description, @ts) from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
|
||||
#
|
||||
@@ -311,7 +323,9 @@ insert t1 values (1); delete from t1;
|
||||
set timestamp=unix_timestamp('2001-02-04 10:20:50');
|
||||
insert t1 values (2); delete from t1;
|
||||
|
||||
--sorted_result
|
||||
select subpartition_name, partition_description, table_rows from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
--sorted_result
|
||||
select * from t1 partition (p1);
|
||||
|
||||
set timestamp=unix_timestamp('2001-02-04 10:20:55');
|
||||
@@ -321,13 +335,16 @@ insert t1 values (4),(5);
|
||||
set timestamp=unix_timestamp('2001-02-04 10:30:10');
|
||||
update t1 set i=6 where i=5;
|
||||
|
||||
--sorted_result
|
||||
select subpartition_name, partition_description, table_rows from information_schema.partitions where table_schema='test' and table_name='t1';
|
||||
select * from t1 partition (p1);
|
||||
--sorted_result
|
||||
select * from t1 partition (p0);
|
||||
select * from t1 partition (p2);
|
||||
|
||||
alter table t1 rebuild partition p0, p1, p2;
|
||||
select * from t1 partition (p1);
|
||||
--sorted_result
|
||||
select * from t1 partition (p0);
|
||||
select * from t1 partition (p2);
|
||||
|
||||
@@ -467,6 +484,7 @@ set timestamp= unix_timestamp('2001-01-01 00:00:02');
|
||||
update t1 set i= i + 1;
|
||||
|
||||
select *, row_end from t1 partition (p0);
|
||||
--sorted_result
|
||||
select *, row_end from t1 partition (p1);
|
||||
|
||||
set timestamp= unix_timestamp('2000-01-01 00:00:00');
|
||||
@@ -485,6 +503,7 @@ update t1 set i= i + 1;
|
||||
set timestamp= unix_timestamp('2000-01-04 00:00:01');
|
||||
update t1 set i= i + 1;
|
||||
|
||||
--sorted_result
|
||||
select *, row_end from t1 partition (p0);
|
||||
select *, row_end from t1 partition (p1);
|
||||
|
||||
@@ -520,7 +539,9 @@ subpartition by key (x)
|
||||
subpartitions 2;
|
||||
|
||||
insert into t1 (x) values (1), (2), (3), (4), (5);
|
||||
--sorted_result
|
||||
select * from t1 partition (pnsp0);
|
||||
--sorted_result
|
||||
select * from t1 partition (pnsp1);
|
||||
|
||||
delete from t1 where x < 3;
|
||||
@@ -530,6 +551,7 @@ delete from t1;
|
||||
--echo # You see warning above ^ (no matter if nothing was deleted)
|
||||
select * from t1 partition (p0sp0);
|
||||
select * from t1 partition (p0sp1);
|
||||
--sorted_result
|
||||
select * from t1 partition (p1sp0);
|
||||
select * from t1 partition (p1sp1);
|
||||
|
||||
@@ -874,6 +896,7 @@ create or replace table t1 (
|
||||
insert into t1 (x) values (1), (2), (3), (4);
|
||||
update t1 set a= 'foo' limit 3;
|
||||
update t1 set a= 'bar' limit 4;
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
|
||||
@@ -931,6 +954,7 @@ create or replace table t1 (pk int primary key, f int) engine=innodb
|
||||
partition by key() partitions 2;
|
||||
insert into t1 values (1,10),(2,20);
|
||||
--echo # expected to hit same partition
|
||||
--sorted_result
|
||||
select * from t1 partition (p0);
|
||||
alter table t1 drop system versioning;
|
||||
|
||||
@@ -970,6 +994,17 @@ update v1 set f= '';
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
delete from v1;
|
||||
|
||||
--echo # system_versioning_asof is ignored by DML, so it does not cause ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
create or replace view v1 as select * from t1;
|
||||
set system_versioning_asof= '2000-01-01 00:00:00';
|
||||
select * from v1;
|
||||
select * from v1 for update;
|
||||
--enable_info
|
||||
update v1 set f= '';
|
||||
delete from v1;
|
||||
--disable_info
|
||||
set system_versioning_asof= default;
|
||||
|
||||
# cleanup
|
||||
drop view v1;
|
||||
drop table t1;
|
||||
@@ -1131,17 +1166,20 @@ replace into t1 select seq from seq_1_to_70;
|
||||
# Puts 60 rows into p1
|
||||
replace into t1 select seq from seq_1_to_60;
|
||||
|
||||
--sorted_result
|
||||
select partition_name, table_rows
|
||||
from information_schema.partitions
|
||||
where table_name = 't1';
|
||||
rollback;
|
||||
|
||||
--sorted_result
|
||||
select partition_name, table_rows
|
||||
from information_schema.partitions
|
||||
where table_name = 't1';
|
||||
|
||||
# Should put 10 rows into the empty partition p0
|
||||
replace into t1 select seq from seq_1_to_10;
|
||||
--sorted_result
|
||||
select partition_name, table_rows
|
||||
from information_schema.partitions
|
||||
where table_name = 't1';
|
||||
@@ -1623,6 +1661,7 @@ starts '2000-01-01 00:00:00';
|
||||
--replace_result $default_engine DEFAULT_ENGINE
|
||||
show create table t1;
|
||||
select * from t1 partition (p0);
|
||||
--sorted_result
|
||||
select * from t1 partition (p1);
|
||||
select * from t1 partition (p2);
|
||||
select * from t1 partition (pn);
|
||||
@@ -1638,6 +1677,7 @@ update t1 set x= x + 1;
|
||||
update t1 set x= x + 1;
|
||||
--echo # You see warning above ^
|
||||
select * from t1 partition (p0);
|
||||
--sorted_result
|
||||
select * from t1 partition (p1);
|
||||
select * from t1 partition (p2);
|
||||
select * from t1 partition (p3);
|
||||
@@ -1792,6 +1832,7 @@ select * from tp1 for system_time all;
|
||||
|
||||
--replace_result $default_engine X ' PAGE_CHECKSUM=1' ''
|
||||
show create table t1;
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
select * from t1 for system_time all order by x;
|
||||
|
||||
@@ -2647,6 +2688,7 @@ eval insert into t1 (a,row_start,row_end) values
|
||||
('p1', '2021-09-30', '2021-10-01 10:00:00'),
|
||||
('overflows, so also p1','2021-09-30', '2021-10-10 10:00:00'),
|
||||
('pn, current', '2021-09-30', $sys_time_max);
|
||||
--sorted_result
|
||||
select table_name,partition_name,partition_ordinal_position,partition_method,partition_description,table_rows
|
||||
from information_schema.partitions where table_schema='test';
|
||||
drop table t1;
|
||||
@@ -2790,6 +2832,7 @@ update t1 set x= 3;
|
||||
|
||||
--echo # CREATE result: row 1 got into p1
|
||||
select *, row_start, row_end from t1 partition (p0);
|
||||
--sorted_result
|
||||
select *, row_start, row_end from t1 partition (p1);
|
||||
flush tables;
|
||||
--echo # For CREATE pruning is affected by current timestamp, but SELECT works
|
||||
@@ -2809,6 +2852,7 @@ partition by system_time interval 1 hour (
|
||||
|
||||
--echo # ALTER result: row 1 got into p1
|
||||
Select *, row_start, row_end from t1 partition (p0);
|
||||
--sorted_result
|
||||
Select *, row_start, row_end from t1 partition (p1);
|
||||
flush tables;
|
||||
Explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59';
|
||||
@@ -2948,17 +2992,155 @@ select x, row_end from t1 partition (p2);
|
||||
alter table t1 add y int partition by system_time interval 1 hour
|
||||
starts '2000-01-01 00:00:00';
|
||||
--disable_info
|
||||
select x, y, row_end from t1 for system_time all order by x;
|
||||
select x, y, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
|
||||
from t1 for system_time all order by x;
|
||||
--echo # LIMIT with DROP COLUMN: the table is rebuilt
|
||||
--enable_info
|
||||
alter table t1 drop y partition by system_time limit 100;
|
||||
--disable_info
|
||||
select x, row_end from t1 for system_time all order by x;
|
||||
select x, if(current_row_ts(row_end), 'CURRENT', row_end) as row_end
|
||||
from t1 for system_time all order by x;
|
||||
set timestamp= default;
|
||||
set time_zone= default;
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
--echo #
|
||||
create table t1 (
|
||||
id int,
|
||||
ts timestamp(6) generated always as row start invisible,
|
||||
te timestamp(6) generated always as row end invisible,
|
||||
primary key (id, te),
|
||||
period for system_time (ts, te)
|
||||
) with system versioning
|
||||
partition by system_time interval 1 month (
|
||||
partition p0 history, partition p1 history, partition pcur current);
|
||||
|
||||
insert t1 values (1), (2), (0);
|
||||
delete from t1 where id = 1;
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
--echo # Below there should be: Select tables optimized away
|
||||
--echo # (except heap combination)
|
||||
explain extended select max(id) from t1;
|
||||
--echo # Exact COUNT(*) (MyISAM, heap) must not count history rows
|
||||
select count(*) from t1;
|
||||
--echo # UPDATE without WHERE changes only current rows
|
||||
update t1 set id= id + 10;
|
||||
--sorted_result
|
||||
select * from t1;
|
||||
select count(*) from t1 for system_time all;
|
||||
--echo # sql_safe_updates rejects DELETE without WHERE even with LIMIT,
|
||||
--echo # as for a non-versioned table
|
||||
set sql_safe_updates= 1;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
delete from t1 limit 1;
|
||||
set sql_safe_updates= default;
|
||||
--echo # DELETE without WHERE removes only current rows
|
||||
delete from t1;
|
||||
select count(*) from t1;
|
||||
select count(*) from t1 for system_time all;
|
||||
|
||||
drop tables t1;
|
||||
|
||||
--echo # Multi-table DELETE/UPDATE under sql_safe_updates: checks use the
|
||||
--echo # written WHERE and the join access types, as before
|
||||
create table t1 (x int) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
create table t2 (y int);
|
||||
insert into t1 values (1), (2);
|
||||
insert into t2 values (1), (2);
|
||||
set sql_safe_updates= 1;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
delete t1 from t1, t2;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
update t1, t2 set t1.x= t2.y;
|
||||
--echo # LIMIT does not exempt multi-table UPDATE from the full join check
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
update t1, t2 set t1.x= t2.y order by t1.x limit 1;
|
||||
set sql_safe_updates= default;
|
||||
drop tables t1, t2;
|
||||
|
||||
--echo # As for a non-versioned table, an index on row_end does not exempt
|
||||
--echo # UPDATE/DELETE without WHERE: UPDATE needs LIMIT, DELETE is rejected
|
||||
create table t1 (
|
||||
x int,
|
||||
ts timestamp(6) as row start invisible,
|
||||
te timestamp(6) as row end invisible,
|
||||
period for system_time (ts, te),
|
||||
key (te)
|
||||
) with system versioning
|
||||
partition by system_time (partition p0 history, partition pn current);
|
||||
insert into t1 values (1), (2);
|
||||
set sql_safe_updates= 1;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
update t1 set x= x + 1;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
delete from t1;
|
||||
update t1 set x= x + 1 limit 1;
|
||||
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
||||
delete from t1 limit 1;
|
||||
set sql_safe_updates= default;
|
||||
drop table t1;
|
||||
|
||||
--echo # Corner cases: outer and sysvar-set FOR SYSTEM_TIME
|
||||
set timestamp= unix_timestamp('2000-01-01 00:00:00');
|
||||
create or replace table t1 (id int)
|
||||
with system versioning
|
||||
partition by system_time interval 1 hour
|
||||
starts timestamp'2000-01-01 00:00:00' (
|
||||
partition p0 history,
|
||||
partition p1 history,
|
||||
partition pn current);
|
||||
insert into t1 values (1);
|
||||
set timestamp= unix_timestamp('2000-01-01 01:30:00');
|
||||
update t1 set id= 2;
|
||||
--echo # implicit query reads only the current partition (id=2):
|
||||
select id from t1;
|
||||
--echo # outer FOR SYSTEM_TIME must propagate into derived t1 and read history (id=1):
|
||||
select * from (select id from t1) for system_time as of timestamp'2000-01-01 01:00:00' as dt;
|
||||
--echo # system_versioning_asof must be honored and read history (id=1):
|
||||
set system_versioning_asof= '2000-01-01 01:00:00';
|
||||
select id from t1;
|
||||
set system_versioning_asof= default;
|
||||
--echo # re-execution reads only the current partition (id=2):
|
||||
prepare s from 'select id from t1';
|
||||
execute s;
|
||||
execute s;
|
||||
deallocate prepare s;
|
||||
create procedure sp() select id from t1;
|
||||
call sp;
|
||||
call sp;
|
||||
drop procedure sp;
|
||||
--echo # the same through merged derived table and view (id=2):
|
||||
create view v1 as select id from t1;
|
||||
prepare s from 'select id from (select id from t1) dt';
|
||||
execute s;
|
||||
execute s;
|
||||
deallocate prepare s;
|
||||
prepare s from 'select id from v1';
|
||||
execute s;
|
||||
execute s;
|
||||
deallocate prepare s;
|
||||
create procedure sp() select id from (select id from t1) dt;
|
||||
call sp;
|
||||
call sp;
|
||||
drop procedure sp;
|
||||
drop view v1;
|
||||
--echo # re-execution with system_versioning_asof reads history (id=1):
|
||||
set system_versioning_asof= '2000-01-01 01:00:00';
|
||||
prepare s from 'select id from t1';
|
||||
execute s;
|
||||
execute s;
|
||||
deallocate prepare s;
|
||||
set system_versioning_asof= default;
|
||||
set timestamp= default;
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 11.4 tests
|
||||
|
||||
set global innodb_stats_persistent= @save_persistent;
|
||||
--source suite/versioning/common_finish.inc
|
||||
|
||||
@@ -204,3 +204,21 @@ DROP TABLE t1;
|
||||
SET system_versioning_asof= DEFAULT;
|
||||
|
||||
--echo # End of 10.4 tests
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-17613 Read sole now-partition for non-historical versioned query
|
||||
--echo #
|
||||
--echo # system_versioning_asof does not act as explicit FOR SYSTEM_TIME AS OF
|
||||
create table t1 (x int) with system versioning;
|
||||
insert into t1 values (1);
|
||||
set system_versioning_asof= '2000-01-01 00:00:00';
|
||||
--echo # Allowed, unlike explicit FOR SYSTEM_TIME AS OF:
|
||||
select * from t1 for update;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
select * from t1 for system_time as of timestamp'2000-01-01 00:00:00' for update;
|
||||
--echo # Not printed as FOR SYSTEM_TIME AS OF:
|
||||
explain extended select * from t1;
|
||||
set system_versioning_asof= default;
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
@@ -28,7 +28,6 @@ SET(MYSYS_SSL_HIDDEN_SOURCES
|
||||
my_sha384.cc
|
||||
my_sha512.cc
|
||||
my_md5.cc
|
||||
openssl.c
|
||||
)
|
||||
|
||||
SET(MYSYS_SSL_SOURCES
|
||||
|
||||
@@ -30,27 +30,17 @@
|
||||
|
||||
#include <random>
|
||||
|
||||
#define CTX_ALIGN 16
|
||||
|
||||
class MyCTX
|
||||
{
|
||||
public:
|
||||
char ctx_buf[EVP_CIPHER_CTX_SIZE + CTX_ALIGN];
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
MyCTX()
|
||||
{
|
||||
#if CTX_ALIGN > 0
|
||||
uintptr_t p= ((uintptr_t)ctx_buf + (CTX_ALIGN - 1)) & ~(CTX_ALIGN - 1);
|
||||
ctx = reinterpret_cast<EVP_CIPHER_CTX*>(p);
|
||||
#else
|
||||
ctx = (EVP_CIPHER_CTX*)ctx_buf;
|
||||
#endif
|
||||
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
ctx= EVP_CIPHER_CTX_new();
|
||||
}
|
||||
virtual ~MyCTX()
|
||||
{
|
||||
EVP_CIPHER_CTX_reset(ctx);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
ERR_remove_state(0);
|
||||
}
|
||||
|
||||
@@ -58,6 +48,8 @@ public:
|
||||
uint klen, const uchar *iv, uint ivlen)
|
||||
{
|
||||
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX));
|
||||
if (unlikely(!ctx))
|
||||
return MY_AES_OPENSSL_ERROR;
|
||||
if (unlikely(!cipher))
|
||||
return MY_AES_BAD_KEYSIZE;
|
||||
|
||||
@@ -116,9 +108,11 @@ public:
|
||||
DBUG_ASSERT(ivlen == 0 || ivlen == sizeof(oiv));
|
||||
|
||||
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||
return res;
|
||||
return MY_AES_OK;
|
||||
}
|
||||
|
||||
/** Update last partial source block, stored in source_tail array. */
|
||||
@@ -214,10 +208,12 @@ public:
|
||||
{
|
||||
compile_time_assert(MY_AES_CTX_SIZE >= sizeof(MyCTX_gcm));
|
||||
int res= MyCTX::init(cipher, encrypt, key, klen, iv, ivlen);
|
||||
if (res)
|
||||
return res;
|
||||
int real_ivlen= EVP_CIPHER_CTX_iv_length(ctx);
|
||||
aad= iv + real_ivlen;
|
||||
aadlen= ivlen - real_ivlen;
|
||||
return res;
|
||||
return MY_AES_OK;
|
||||
}
|
||||
|
||||
int update(const uchar *src, uint slen, uchar *dst, uint *dlen) override
|
||||
@@ -299,20 +295,30 @@ int my_aes_crypt_init(void *ctx, enum my_aes_mode mode, int flags,
|
||||
new (ctx) MyCTX_nopad();
|
||||
else
|
||||
new (ctx) MyCTX();
|
||||
return ((MyCTX*)ctx)->init(ciphers[mode](klen), flags & 1,
|
||||
key, klen, iv, ivlen);
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
int res= c->init(ciphers[mode](klen), flags & 1, key, klen, iv, ivlen);
|
||||
if (res)
|
||||
{
|
||||
/* release ctx but keep the object: a later update()/finish() from a
|
||||
caller that ignored the error is then a safe no-op, not a double free */
|
||||
EVP_CIPHER_CTX_free(c->ctx);
|
||||
c->ctx= NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int my_aes_crypt_update(void *ctx, const uchar *src, uint slen,
|
||||
uchar *dst, uint *dlen)
|
||||
{
|
||||
return ((MyCTX*)ctx)->update(src, slen, dst, dlen);
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
return c->ctx ? c->update(src, slen, dst, dlen) : MY_AES_OPENSSL_ERROR;
|
||||
}
|
||||
|
||||
int my_aes_crypt_finish(void *ctx, uchar *dst, uint *dlen)
|
||||
{
|
||||
int res= ((MyCTX*)ctx)->finish(dst, dlen);
|
||||
((MyCTX*)ctx)->~MyCTX();
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
int res= c->ctx ? c->finish(dst, dlen) : MY_AES_OPENSSL_ERROR;
|
||||
c->~MyCTX();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,70 +19,60 @@
|
||||
@file
|
||||
|
||||
@brief
|
||||
Wrapper functions for OpenSSL and YaSSL. Also provides a Compatibility layer
|
||||
to make available YaSSL's MD5 implementation.
|
||||
Wrapper functions for the OpenSSL/WolfSSL MD5 implementation.
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_md5.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined(HAVE_WOLFSSL)
|
||||
#include <wolfssl/wolfcrypt/md5.h>
|
||||
#include <ssl_compat.h>
|
||||
typedef wc_Md5 EVP_MD_CTX;
|
||||
static void md5_init(EVP_MD_CTX *context)
|
||||
{
|
||||
wc_InitMd5(context);;
|
||||
}
|
||||
|
||||
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
wc_Md5Update(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
wc_Md5Final(context,digest);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL) && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER)
|
||||
/*
|
||||
OpenSSL 1.0.x in FIPS mode aborts on the low-level MD5 API (md5 is not used
|
||||
for crypto here), so go through EVP with the non-FIPS flag. MDEV-7788.
|
||||
*/
|
||||
#include <openssl/evp.h>
|
||||
#include <ssl_compat.h>
|
||||
|
||||
static void md5_init(EVP_MD_CTX *context)
|
||||
typedef EVP_MD_CTX MY_MD5_CTX;
|
||||
static void md5_init(MY_MD5_CTX *context)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
EVP_MD *md5;
|
||||
EVP_MD_CTX_init(context);
|
||||
/* Ok to ignore FIPS: MD5 is not used for crypto here */
|
||||
/* In OpenSSL 3.0.0+ it is a different EVP_MD provider */
|
||||
md5 = EVP_MD_fetch(NULL, "MD5", "fips=no");
|
||||
EVP_DigestInit_ex(context, md5, NULL);
|
||||
EVP_MD_free(md5);
|
||||
#else
|
||||
EVP_MD_CTX_init(context);
|
||||
#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
|
||||
/* Ok to ignore FIPS: MD5 is not used for crypto here */
|
||||
/* In OpenSSL 1.1.1 the non FIPS allowed flag is context specific */
|
||||
EVP_MD_CTX_set_flags(context, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
|
||||
#endif
|
||||
EVP_DigestInit_ex(context, EVP_md5(), NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
|
||||
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
EVP_DigestUpdate(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
EVP_DigestFinal_ex(context, digest, NULL);
|
||||
EVP_MD_CTX_reset(context);
|
||||
EVP_MD_CTX_cleanup(context);
|
||||
}
|
||||
|
||||
#endif /* HAVE_WOLFSSL */
|
||||
#else
|
||||
#include <openssl/md5.h>
|
||||
typedef MD5_CTX MY_MD5_CTX;
|
||||
static void md5_init(MY_MD5_CTX *context)
|
||||
{
|
||||
MD5_Init(context);
|
||||
}
|
||||
|
||||
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
MD5_Update(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
MD5_Final(digest, context);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Wrapper function to compute MD5 message digest.
|
||||
@@ -95,11 +85,10 @@ static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
*/
|
||||
void my_md5(uchar *digest, const char *buf, size_t len)
|
||||
{
|
||||
char ctx_buf[EVP_MD_CTX_SIZE];
|
||||
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
|
||||
md5_init(ctx);
|
||||
md5_input(ctx, (const uchar *)buf, (uint) len);
|
||||
md5_result(ctx, digest);
|
||||
MY_MD5_CTX context;
|
||||
md5_init(&context);
|
||||
md5_input(&context, (const uchar *)buf, (uint) len);
|
||||
md5_result(&context, digest);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,34 +108,33 @@ void my_md5_multi(uchar *digest, ...)
|
||||
{
|
||||
va_list args;
|
||||
const uchar *str;
|
||||
char ctx_buf[EVP_MD_CTX_SIZE];
|
||||
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
|
||||
MY_MD5_CTX context;
|
||||
va_start(args, digest);
|
||||
|
||||
md5_init(ctx);
|
||||
md5_init(&context);
|
||||
for (str= va_arg(args, const uchar*); str; str= va_arg(args, const uchar*))
|
||||
md5_input(ctx, str, (uint) va_arg(args, size_t));
|
||||
md5_input(&context, str, (uint) va_arg(args, size_t));
|
||||
|
||||
md5_result(ctx, digest);
|
||||
md5_result(&context, digest);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
size_t my_md5_context_size()
|
||||
{
|
||||
return EVP_MD_CTX_SIZE;
|
||||
return sizeof(MY_MD5_CTX);
|
||||
}
|
||||
|
||||
void my_md5_init(void *context)
|
||||
{
|
||||
md5_init((EVP_MD_CTX *)context);
|
||||
md5_init((MY_MD5_CTX *)context);
|
||||
}
|
||||
|
||||
void my_md5_input(void *context, const uchar *buf, size_t len)
|
||||
{
|
||||
md5_input((EVP_MD_CTX *)context, buf, (uint) len);
|
||||
md5_input((MY_MD5_CTX *)context, buf, (uint) len);
|
||||
}
|
||||
|
||||
void my_md5_result(void *context, uchar *digest)
|
||||
{
|
||||
md5_result((EVP_MD_CTX *)context, digest);
|
||||
md5_result((MY_MD5_CTX *)context, digest);
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2017, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include <my_global.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <ssl_compat.h>
|
||||
|
||||
/*
|
||||
The check is only done for OpenSSL 1.1.x.
|
||||
It could run for OpenSSL 1.0.x but it doesn't make much sense
|
||||
and it hits this bug:
|
||||
https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1594748
|
||||
*/
|
||||
|
||||
#ifndef HAVE_OPENSSL11
|
||||
int check_openssl_compatibility()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#include <openssl/evp.h>
|
||||
|
||||
static uint testing;
|
||||
static size_t alloc_size, alloc_count;
|
||||
|
||||
static void *coc_malloc(size_t size
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *f __attribute__((unused)),
|
||||
int l __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (unlikely(testing))
|
||||
{
|
||||
alloc_size+= size;
|
||||
alloc_count++;
|
||||
}
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void *coc_realloc(void *addr, size_t num
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return realloc(addr, num);
|
||||
}
|
||||
|
||||
static void coc_free(void *addr
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
free(addr);
|
||||
}
|
||||
|
||||
int check_openssl_compatibility()
|
||||
{
|
||||
EVP_CIPHER_CTX *evp_ctx;
|
||||
EVP_MD_CTX *md5_ctx;
|
||||
|
||||
if (!CRYPTO_set_mem_functions(coc_malloc, coc_realloc, coc_free))
|
||||
return 0;
|
||||
|
||||
testing= 1;
|
||||
alloc_size= alloc_count= 0;
|
||||
evp_ctx= EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_free(evp_ctx);
|
||||
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_CIPHER_CTX_SIZE)
|
||||
return 1;
|
||||
|
||||
alloc_size= alloc_count= 0;
|
||||
md5_ctx= EVP_MD_CTX_new();
|
||||
EVP_MD_CTX_free(md5_ctx);
|
||||
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_MD_CTX_SIZE)
|
||||
return 1;
|
||||
|
||||
testing= 0;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1358,7 +1358,7 @@ Event_job_data::execute(THD *thd, bool drop)
|
||||
{
|
||||
String sp_sql;
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
Security_context event_sctx, *save_sctx= NULL;
|
||||
Security_context *save_sctx= NULL;
|
||||
#endif
|
||||
List<Item> empty_item_list;
|
||||
bool ret= TRUE;
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
|
||||
#include "event_parse_data.h"
|
||||
#include "thr_lock.h" /* thr_lock_type */
|
||||
#include "sql_class.h" /* Security_context */
|
||||
|
||||
class Field;
|
||||
class THD;
|
||||
class Time_zone;
|
||||
struct TABLE;
|
||||
|
||||
@@ -173,6 +173,17 @@ public:
|
||||
|
||||
class Stored_program_creation_ctx *creation_ctx;
|
||||
|
||||
/*
|
||||
DEFINER security context, active in thd->security_ctx during execute().
|
||||
Kept here, not in execute(), to outlive it: a concurrent PROCESSLIST
|
||||
scan may still read it. See Event_worker_thread::run().
|
||||
|
||||
acl_getroot() aliases user/host/ip to definer_user/definer_host
|
||||
instead of copying them, so they must live in this object.
|
||||
Never call event_sctx.destroy(): it would free strings not owned here.
|
||||
*/
|
||||
Security_context event_sctx;
|
||||
|
||||
Event_job_data();
|
||||
|
||||
virtual bool
|
||||
|
||||
@@ -319,6 +319,9 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
|
||||
|
||||
res= job_data.execute(thd, event->dropped);
|
||||
|
||||
/* Test hook: execute() has returned, job_data is still alive. */
|
||||
DEBUG_SYNC(thd, "event_worker_thread_after_execute_returned");
|
||||
|
||||
print_warnings(thd, &job_data);
|
||||
|
||||
if (res && global_system_variables.log_warnings > 2)
|
||||
|
||||
@@ -1632,9 +1632,9 @@ public:
|
||||
ha_rows part_records(partition_element *part_elem)
|
||||
{
|
||||
DBUG_ASSERT(m_part_info);
|
||||
uint32 sub_factor= m_part_info->num_subparts ? m_part_info->num_subparts : 1;
|
||||
uint32 part_id= part_elem->id * sub_factor;
|
||||
uint32 part_id_end= part_id + sub_factor;
|
||||
uint32 part_id_end;
|
||||
uint32 part_id= part_elem->bitmap_range(m_part_info->num_subparts,
|
||||
part_id_end);
|
||||
DBUG_ASSERT(part_id_end <= m_tot_parts);
|
||||
ha_rows part_recs= 0;
|
||||
for (; part_id < part_id_end; ++part_id)
|
||||
|
||||
@@ -4111,14 +4111,6 @@ static int init_common_variables()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (check_openssl_compatibility())
|
||||
{
|
||||
sql_print_error("Incompatible OpenSSL version. Cannot continue...");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (init_thread_environment() || mysql_init_variables())
|
||||
exit(1);
|
||||
|
||||
|
||||
126
sql/opt_range.cc
126
sql/opt_range.cc
@@ -4012,7 +4012,7 @@ void store_key_image_to_rec(Field *field, uchar *ptr, uint len)
|
||||
struct st_part_prune_param;
|
||||
struct st_part_opt_info;
|
||||
|
||||
typedef void (*mark_full_part_func)(partition_info*, uint32);
|
||||
typedef void (*mark_full_part_func)(struct st_part_prune_param*, uint32);
|
||||
|
||||
/*
|
||||
Partition pruning operation context
|
||||
@@ -4073,6 +4073,12 @@ typedef struct st_part_prune_param
|
||||
/* Iterator to be used to obtain the "current" set of used partitions */
|
||||
PARTITION_ITERATOR part_iter;
|
||||
|
||||
/*
|
||||
Initialized bitmap of read_partitions size.
|
||||
find_used_partitions() will save the set of used partitions here.
|
||||
*/
|
||||
MY_BITMAP parts_bitmap;
|
||||
|
||||
/* Initialized bitmap of num_subparts size */
|
||||
MY_BITMAP subparts_bitmap;
|
||||
|
||||
@@ -4088,7 +4094,6 @@ static int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar,
|
||||
SEL_IMERGE *imerge);
|
||||
static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
|
||||
List<SEL_IMERGE> &merges);
|
||||
static void mark_all_partitions_as_used(partition_info *part_info);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end);
|
||||
@@ -4105,13 +4110,30 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num);
|
||||
@param table Table to perform partition pruning for
|
||||
@param pprune_cond Condition to use for partition pruning
|
||||
|
||||
@param[in] part_info->read_partitions
|
||||
Candidate partitions to scan; normally all partitions (or the
|
||||
PARTITION() set)
|
||||
|
||||
@param[out] part_info->read_partitions
|
||||
Narrowed to the partitions that may hold records matching the
|
||||
condition. For a versioned table with history partitions
|
||||
accessed without FOR SYSTEM_TIME, PARTITION() and
|
||||
@@system_versioning_asof, also narrowed to the now-partition,
|
||||
with or without a condition. Never widened.
|
||||
|
||||
@note This function assumes that lock_partitions are setup when it
|
||||
is invoked. The function analyzes the condition, finds partitions that
|
||||
need to be used to retrieve the records that match the condition, and
|
||||
marks them as used by setting appropriate bit in part_info->read_partitions
|
||||
In the worst case all partitions are marked as used. If the table is not
|
||||
yet locked, it will also unset bits in part_info->lock_partitions that is
|
||||
not set in read_partitions.
|
||||
narrows part_info->read_partitions to them.
|
||||
If the table is not yet locked, it will also unset bits in
|
||||
part_info->lock_partitions that is not set in read_partitions.
|
||||
|
||||
The matching set is computed independently of the incoming
|
||||
read_partitions/lock_partitions: the analysis walks the whole partition
|
||||
list (or only the value interval derived from the condition for
|
||||
range-capable partitioning), and the result is then intersected into
|
||||
read_partitions. A restriction already present in read_partitions
|
||||
therefore narrows the outcome but does not reduce the pruning work.
|
||||
|
||||
This function returns promptly if called for non-partitioned table.
|
||||
|
||||
@@ -4129,11 +4151,40 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
|
||||
if (!part_info)
|
||||
DBUG_RETURN(FALSE); /* not a partitioned table */
|
||||
|
||||
if (!pprune_cond)
|
||||
/*
|
||||
Read only now_part, as vers_setup_conds() did not add the row_end condition.
|
||||
Done here as prune_partitions() runs on each execution, also for merged
|
||||
derived tables and views, for which vers_setup_conds() is not called
|
||||
again.
|
||||
*/
|
||||
if (TABLE_LIST *tl= table->pos_in_table_list)
|
||||
{
|
||||
mark_all_partitions_as_used(part_info);
|
||||
DBUG_RETURN(FALSE);
|
||||
if (part_info->vers_info && !tl->partition_names &&
|
||||
!tl->vers_conditions.was_set() && !tl->vers_conditions.from_sysvar)
|
||||
{
|
||||
DBUG_ASSERT(part_info->vers_info->initialized());
|
||||
uint32 end;
|
||||
const uint32 start=
|
||||
part_info->vers_info->now_part->bitmap_range(part_info->num_subparts,
|
||||
end);
|
||||
for (uint32 i= 0; i < part_info->read_partitions.n_bits; i++)
|
||||
if (i < start || i >= end)
|
||||
bitmap_clear_bit(&part_info->read_partitions, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Must be a subset of the locked partitions.
|
||||
lock_partitions contains the partitions marked by explicit partition
|
||||
selection (... t PARTITION (pX) ...) and we must only use partitions
|
||||
within that set. read_partitions may be wider on entry: e.g.
|
||||
check_insert_or_replace_autoincrement() sets all bits for
|
||||
INSERT/REPLACE ... SELECT. Done before the early returns below.
|
||||
*/
|
||||
bitmap_intersect(&part_info->read_partitions, &part_info->lock_partitions);
|
||||
|
||||
if (!pprune_cond)
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
PART_PRUNE_PARAM prune_param;
|
||||
MEM_ROOT alloc;
|
||||
@@ -4149,7 +4200,6 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
|
||||
|
||||
if (create_partition_index_description(&prune_param))
|
||||
{
|
||||
mark_all_partitions_as_used(part_info);
|
||||
free_root(&alloc,MYF(0)); // Return memory & allocator
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
@@ -4174,7 +4224,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
|
||||
thd->no_errors=1; // Don't warn about NULL
|
||||
thd->mem_root=&alloc;
|
||||
|
||||
bitmap_clear_all(&part_info->read_partitions);
|
||||
bitmap_clear_all(&prune_param.parts_bitmap);
|
||||
|
||||
prune_param.key= prune_param.range_param.key_parts;
|
||||
SEL_TREE *tree;
|
||||
@@ -4251,20 +4301,15 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
|
||||
|
||||
all_used:
|
||||
retval= FALSE; // some partitions are used
|
||||
mark_all_partitions_as_used(prune_param.part_info);
|
||||
goto all_used_end;
|
||||
end:
|
||||
bitmap_intersect(&prune_param.part_info->read_partitions,
|
||||
&prune_param.parts_bitmap);
|
||||
all_used_end:
|
||||
dbug_tmp_restore_column_maps(&table->read_set, &table->write_set, old_sets);
|
||||
thd->no_errors=0;
|
||||
thd->mem_root= range_par->old_root;
|
||||
free_root(&alloc,MYF(0)); // Return memory & allocator
|
||||
/*
|
||||
Must be a subset of the locked partitions.
|
||||
lock_partitions contains the partitions marked by explicit partition
|
||||
selection (... t PARTITION (pX) ...) and we must only use partitions
|
||||
within that set.
|
||||
*/
|
||||
bitmap_intersect(&prune_param.part_info->read_partitions,
|
||||
&prune_param.part_info->lock_partitions);
|
||||
/*
|
||||
If not yet locked, also prune partitions to lock if not UPDATEing
|
||||
partition key fields. This will also prune lock_partitions if we are under
|
||||
@@ -4330,20 +4375,21 @@ static void store_selargs_to_rec(PART_PRUNE_PARAM *ppar, SEL_ARG **start,
|
||||
|
||||
|
||||
/* Mark a partition as used in the case when there are no subpartitions */
|
||||
static void mark_full_partition_used_no_parts(partition_info* part_info,
|
||||
static void mark_full_partition_used_no_parts(PART_PRUNE_PARAM *ppar,
|
||||
uint32 part_id)
|
||||
{
|
||||
DBUG_ENTER("mark_full_partition_used_no_parts");
|
||||
DBUG_PRINT("enter", ("Mark partition %u as used", part_id));
|
||||
bitmap_set_bit(&part_info->read_partitions, part_id);
|
||||
bitmap_set_bit(&ppar->parts_bitmap, part_id);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/* Mark a partition as used in the case when there are subpartitions */
|
||||
static void mark_full_partition_used_with_parts(partition_info *part_info,
|
||||
static void mark_full_partition_used_with_parts(PART_PRUNE_PARAM *ppar,
|
||||
uint32 part_id)
|
||||
{
|
||||
partition_info *part_info= ppar->part_info;
|
||||
uint32 start= part_id * part_info->num_subparts;
|
||||
uint32 end= start + part_info->num_subparts;
|
||||
DBUG_ENTER("mark_full_partition_used_with_parts");
|
||||
@@ -4351,7 +4397,7 @@ static void mark_full_partition_used_with_parts(partition_info *part_info,
|
||||
for (; start != end; start++)
|
||||
{
|
||||
DBUG_PRINT("info", ("1:Mark subpartition %u as used", start));
|
||||
bitmap_set_bit(&part_info->read_partitions, start);
|
||||
bitmap_set_bit(&ppar->parts_bitmap, start);
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
@@ -4379,7 +4425,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
|
||||
MY_BITMAP all_merges;
|
||||
uint bitmap_bytes;
|
||||
my_bitmap_map *bitmap_buf;
|
||||
uint n_bits= ppar->part_info->read_partitions.n_bits;
|
||||
uint n_bits= ppar->parts_bitmap.n_bits;
|
||||
bitmap_bytes= bitmap_buffer_size(n_bits);
|
||||
if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root,
|
||||
bitmap_bytes)))
|
||||
@@ -4405,15 +4451,15 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
|
||||
}
|
||||
|
||||
if (res != -1)
|
||||
bitmap_intersect(&all_merges, &ppar->part_info->read_partitions);
|
||||
bitmap_intersect(&all_merges, &ppar->parts_bitmap);
|
||||
|
||||
|
||||
if (bitmap_is_clear_all(&all_merges))
|
||||
return 0;
|
||||
|
||||
bitmap_clear_all(&ppar->part_info->read_partitions);
|
||||
bitmap_clear_all(&ppar->parts_bitmap);
|
||||
}
|
||||
memcpy(ppar->part_info->read_partitions.bitmap, all_merges.bitmap,
|
||||
memcpy(ppar->parts_bitmap.bitmap, all_merges.bitmap,
|
||||
bitmap_bytes);
|
||||
return 1;
|
||||
}
|
||||
@@ -4566,6 +4612,8 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
|
||||
-1 Couldn't infer any partition pruning "intervals" from the passed
|
||||
SEL_ARG* tree (which means that all partitions should be marked as
|
||||
used) Marking partitions as used is the responsibility of the caller.
|
||||
|
||||
The set of used partitions is saved in PART_PRUNE_PARAM::parts_bitmap.
|
||||
*/
|
||||
|
||||
static
|
||||
@@ -4784,7 +4832,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
|
||||
{
|
||||
for (uint i= 0; i < ppar->part_info->num_subparts; i++)
|
||||
if (bitmap_is_set(&ppar->subparts_bitmap, i))
|
||||
bitmap_set_bit(&ppar->part_info->read_partitions,
|
||||
bitmap_set_bit(&ppar->parts_bitmap,
|
||||
part_id * ppar->part_info->num_subparts + i);
|
||||
}
|
||||
goto pop_and_go_right;
|
||||
@@ -4846,7 +4894,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
|
||||
while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
|
||||
NOT_A_PARTITION_ID)
|
||||
{
|
||||
bitmap_set_bit(&part_info->read_partitions,
|
||||
bitmap_set_bit(&ppar->parts_bitmap,
|
||||
part_id * part_info->num_subparts + subpart_id);
|
||||
}
|
||||
res= 1; /* Some partitions were marked as used */
|
||||
@@ -4901,7 +4949,7 @@ process_next_key_part:
|
||||
while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
|
||||
NOT_A_PARTITION_ID)
|
||||
{
|
||||
ppar->mark_full_partition_used(ppar->part_info, part_id);
|
||||
ppar->mark_full_partition_used(ppar, part_id);
|
||||
found= TRUE;
|
||||
}
|
||||
res= MY_TEST(found);
|
||||
@@ -4930,13 +4978,6 @@ pop_and_go_right:
|
||||
}
|
||||
|
||||
|
||||
static void mark_all_partitions_as_used(partition_info *part_info)
|
||||
{
|
||||
bitmap_copy(&(part_info->read_partitions),
|
||||
&(part_info->lock_partitions));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if field types allow to construct partitioning index description
|
||||
|
||||
@@ -5041,6 +5082,15 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
|
||||
!(ppar->is_subpart_keypart= (my_bool*)alloc_root(alloc, sizeof(my_bool)*
|
||||
total_parts)))
|
||||
return TRUE;
|
||||
|
||||
{
|
||||
my_bitmap_map *buf;
|
||||
uint32 bufsize= bitmap_buffer_size(part_info->read_partitions.n_bits);
|
||||
if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize)))
|
||||
return TRUE;
|
||||
my_bitmap_init(&ppar->parts_bitmap, buf,
|
||||
part_info->read_partitions.n_bits);
|
||||
}
|
||||
|
||||
if (ppar->subpart_fields)
|
||||
{
|
||||
|
||||
@@ -172,6 +172,19 @@ public:
|
||||
DBUG_ASSERT(ev->col_val_array);
|
||||
return ev->col_val_array[idx];
|
||||
}
|
||||
|
||||
/*
|
||||
Leaf-bitmap (read/lock_partitions) range covered by this top-level
|
||||
partition: a single bit if not subpartitioned, num_subparts bits
|
||||
otherwise. Returns the first bit; bit_no_end receives one past the last.
|
||||
*/
|
||||
uint32 bitmap_range(uint num_subparts, uint32 &bit_no_end) const
|
||||
{
|
||||
uint32 sub_factor= num_subparts ? num_subparts : 1;
|
||||
uint32 first= id * sub_factor;
|
||||
bit_no_end= first + sub_factor;
|
||||
return first;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* PARTITION_ELEMENT_INCLUDED */
|
||||
|
||||
@@ -1024,10 +1024,9 @@ void partition_info::vers_check_limit(THD *thd)
|
||||
of partitions.
|
||||
*/
|
||||
#ifndef DBUG_OFF
|
||||
const uint32 sub_factor= num_subparts ? num_subparts : 1;
|
||||
uint32 part_id= vers_info->hist_part->id * sub_factor;
|
||||
const uint32 part_id_end __attribute__((unused)) = part_id + sub_factor;
|
||||
DBUG_ASSERT(part_id_end <= num_parts * sub_factor);
|
||||
uint32 part_id_end;
|
||||
vers_info->hist_part->bitmap_range(num_subparts, part_id_end);
|
||||
DBUG_ASSERT(part_id_end <= get_tot_partitions());
|
||||
#endif
|
||||
|
||||
ha_partition *hp= (ha_partition*)(table->file);
|
||||
|
||||
@@ -92,6 +92,15 @@ static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
PROXY v2 header is 16 bytes, followed by the address block with source and
|
||||
destination address and port. Whole header length for TCP over IPv4, IPv6.
|
||||
*/
|
||||
#define PROXY_V2_HEADER_LEN 16
|
||||
#define PROXY_V2_TCP4_LEN (PROXY_V2_HEADER_LEN + 2 * 4 + 2 * 2)
|
||||
#define PROXY_V2_TCP6_LEN (PROXY_V2_HEADER_LEN + 2 * 16 + 2 * 2)
|
||||
|
||||
|
||||
/*
|
||||
Parse proxy protocol V2 (binary) header
|
||||
*/
|
||||
@@ -129,14 +138,14 @@ static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info)
|
||||
switch (fam)
|
||||
{
|
||||
case 0x11: /* TCPv4 */
|
||||
if (len < 25)
|
||||
if (len < PROXY_V2_TCP4_LEN)
|
||||
return -1;
|
||||
sin->sin_family= AF_INET;
|
||||
memcpy(&(sin->sin_addr), hdr + 16, 4);
|
||||
peer_info->port= (hdr[24] << 8) + hdr[25];
|
||||
break;
|
||||
case 0x21: /* TCPv6 */
|
||||
if (len < 49)
|
||||
if (len < PROXY_V2_TCP6_LEN)
|
||||
return -1;
|
||||
sin6->sin6_family= AF_INET6;
|
||||
memcpy(&(sin6->sin6_addr), hdr + 16, 16);
|
||||
@@ -201,8 +210,8 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
|
||||
/* Read until end of header (newline character)*/
|
||||
while(pos < sizeof(hdr) - 1)
|
||||
{
|
||||
long len= (long)vio_read(vio, hdr + pos, 1);
|
||||
if (len < 0)
|
||||
ssize_t len= vio_read(vio, hdr + pos, 1);
|
||||
if (len != 1)
|
||||
return -1;
|
||||
pos++;
|
||||
if (hdr[pos-1] == '\n')
|
||||
@@ -216,10 +225,9 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
|
||||
}
|
||||
else // if (have_v2_header)
|
||||
{
|
||||
#define PROXY_V2_HEADER_LEN 16
|
||||
/* read off 16 bytes of the header.*/
|
||||
ssize_t len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos);
|
||||
if (len < 0)
|
||||
if (len != PROXY_V2_HEADER_LEN - ssize_t(pos))
|
||||
return -1;
|
||||
// 2 last bytes are the length in network byte order of the part following header
|
||||
ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] << 8) + hdr[PROXY_V2_HEADER_LEN-1];
|
||||
@@ -228,7 +236,7 @@ int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
|
||||
if (trail_len > 0)
|
||||
{
|
||||
len= vio_read(vio, hdr + PROXY_V2_HEADER_LEN, trail_len);
|
||||
if (len < 0)
|
||||
if (len != trail_len)
|
||||
return -1;
|
||||
}
|
||||
pos= PROXY_V2_HEADER_LEN + trail_len;
|
||||
|
||||
@@ -378,6 +378,7 @@ class Master_info : public Slave_reporting_capability
|
||||
it must be ignored similarly to the replicate-same-server-id rule.
|
||||
*/
|
||||
bool do_accept_own_server_id= false;
|
||||
bool semi_sync_enabled= false;
|
||||
/*
|
||||
Set to 1 when semi_sync is enabled. Set to 0 if there is any transmit
|
||||
problems to the slave, in which case any furter semi-sync reply is
|
||||
|
||||
@@ -46,7 +46,8 @@ int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff,
|
||||
system_status_var *status_var,
|
||||
enum_var_type scope)
|
||||
{
|
||||
local_semi_sync_enabled= repl_semisync_slave.get_slave_enabled();
|
||||
//TODO MDEV-40941
|
||||
local_semi_sync_enabled= repl_semisync_slave.m_slave_enabled;
|
||||
var->type= SHOW_BOOL;
|
||||
var->value= (char*) &local_semi_sync_enabled;
|
||||
return 0;
|
||||
@@ -55,14 +56,14 @@ int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff,
|
||||
|
||||
int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
|
||||
unsigned long total_len,
|
||||
int *semi_flags,
|
||||
Master_info *mi,
|
||||
const uchar **payload,
|
||||
unsigned long *payload_len)
|
||||
{
|
||||
int read_res = 0;
|
||||
DBUG_ENTER("Repl_semi_sync_slave::slave_read_sync_header");
|
||||
|
||||
if (get_slave_enabled())
|
||||
if (get_slave_enabled(mi))
|
||||
{
|
||||
if (!DBUG_IF("semislave_corrupt_log")
|
||||
&& header[0] == k_packet_magic_num)
|
||||
@@ -76,9 +77,9 @@ int Repl_semi_sync_slave::slave_read_sync_header(const uchar *header,
|
||||
semi_sync_need_reply));
|
||||
|
||||
if (semi_sync_need_reply)
|
||||
*semi_flags |= SEMI_SYNC_NEED_ACK;
|
||||
mi->semi_ack |= SEMI_SYNC_NEED_ACK;
|
||||
if (is_delay_master())
|
||||
*semi_flags |= SEMI_SYNC_SLAVE_DELAY_SYNC;
|
||||
mi->semi_ack |= SEMI_SYNC_SLAVE_DELAY_SYNC;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,7 +112,7 @@ void Repl_semi_sync_slave::slave_start(Master_info *mi)
|
||||
*/
|
||||
bool semi_sync= global_rpl_semi_sync_slave_enabled;
|
||||
|
||||
set_slave_enabled(semi_sync);
|
||||
set_slave_enabled(mi, semi_sync);
|
||||
mi->semi_sync_reply_enabled= 0;
|
||||
|
||||
sql_print_information("Slave I/O thread: Start %s replication to\
|
||||
@@ -127,7 +128,7 @@ void Repl_semi_sync_slave::slave_start(Master_info *mi)
|
||||
|
||||
void Repl_semi_sync_slave::slave_stop(Master_info *mi)
|
||||
{
|
||||
if (get_slave_enabled())
|
||||
if (get_slave_enabled(mi))
|
||||
{
|
||||
#ifdef ENABLED_DEBUG_SYNC
|
||||
/*
|
||||
@@ -144,7 +145,7 @@ void Repl_semi_sync_slave::slave_stop(Master_info *mi)
|
||||
kill_connection(mi);
|
||||
}
|
||||
|
||||
set_slave_enabled(0);
|
||||
set_slave_enabled(mi, 0);
|
||||
}
|
||||
|
||||
void Repl_semi_sync_slave::slave_reconnect(Master_info *mi)
|
||||
@@ -153,7 +154,7 @@ void Repl_semi_sync_slave::slave_reconnect(Master_info *mi)
|
||||
Start semi-sync either if it globally enabled or if was enabled
|
||||
before the reconnect.
|
||||
*/
|
||||
if (global_rpl_semi_sync_slave_enabled || get_slave_enabled())
|
||||
if (global_rpl_semi_sync_slave_enabled || get_slave_enabled(mi))
|
||||
slave_start(mi);
|
||||
}
|
||||
|
||||
@@ -215,7 +216,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi)
|
||||
MYSQL_ROW row;
|
||||
const char *query;
|
||||
|
||||
if (!get_slave_enabled())
|
||||
if (!get_slave_enabled(mi))
|
||||
return 0;
|
||||
|
||||
query= "SHOW VARIABLES LIKE 'rpl_semi_sync_master_enabled'";
|
||||
@@ -223,7 +224,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi)
|
||||
!(res= mysql_store_result(mysql)))
|
||||
{
|
||||
sql_print_error("Execution failed on master: %s, error :%s", query, mysql_error(mysql));
|
||||
set_slave_enabled(0);
|
||||
set_slave_enabled(mi, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -234,7 +235,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi)
|
||||
if (!row)
|
||||
sql_print_warning("Master server does not support semi-sync, "
|
||||
"fallback to asynchronous replication");
|
||||
set_slave_enabled(0);
|
||||
set_slave_enabled(mi, 0);
|
||||
mysql_free_result(res);
|
||||
return 0;
|
||||
}
|
||||
@@ -253,7 +254,7 @@ int Repl_semi_sync_slave::request_transmit(Master_info *mi)
|
||||
if (mysql_real_query(mysql, query, (ulong)strlen(query)))
|
||||
{
|
||||
sql_print_error("%s on master failed", query);
|
||||
set_slave_enabled(0);
|
||||
set_slave_enabled(mi, 0);
|
||||
return 1;
|
||||
}
|
||||
mi->semi_sync_reply_enabled= 1;
|
||||
@@ -276,7 +277,7 @@ int Repl_semi_sync_slave::slave_reply(Master_info *mi)
|
||||
int reply_res = 0;
|
||||
size_t name_len = strlen(binlog_filename);
|
||||
DBUG_ENTER("Repl_semi_sync_slave::slave_reply");
|
||||
DBUG_ASSERT(get_slave_enabled() && mi->semi_sync_reply_enabled);
|
||||
DBUG_ASSERT(get_slave_enabled(mi) && mi->semi_sync_reply_enabled);
|
||||
|
||||
/* Prepare the buffer of the reply. */
|
||||
reply_buffer[REPLY_MAGIC_NUM_OFFSET] = k_packet_magic_num;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#ifndef SEMISYNC_SLAVE_H
|
||||
#define SEMISYNC_SLAVE_H
|
||||
#ifdef HAVE_REPLICATION
|
||||
|
||||
#include "semisync.h"
|
||||
#include "my_global.h"
|
||||
@@ -45,12 +46,12 @@ public:
|
||||
*/
|
||||
int init_object();
|
||||
|
||||
inline bool get_slave_enabled() {
|
||||
return m_slave_enabled;
|
||||
inline bool get_slave_enabled(Master_info *mi) {
|
||||
return mi->semi_sync_enabled;
|
||||
}
|
||||
|
||||
void set_slave_enabled(bool enabled) {
|
||||
m_slave_enabled = enabled;
|
||||
void set_slave_enabled(Master_info *mi, bool enabled) {
|
||||
m_slave_enabled= mi->semi_sync_enabled= enabled;
|
||||
}
|
||||
|
||||
inline bool is_delay_master(){
|
||||
@@ -71,8 +72,8 @@ public:
|
||||
* Input:
|
||||
* header - (IN) packet header pointer
|
||||
* total_len - (IN) total packet length: metadata + payload
|
||||
* semi_flags - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and
|
||||
SEMI_SYNC_NEED_ACK
|
||||
* mi - (IN) store flags: SEMI_SYNC_SLAVE_DELAY_SYNC and
|
||||
SEMI_SYNC_NEED_ACK, to Master_info::semi_ack
|
||||
* payload - (IN) payload: the replication event
|
||||
* payload_len - (IN) payload length
|
||||
*
|
||||
@@ -80,7 +81,7 @@ public:
|
||||
* 0: success; non-zero: error
|
||||
*/
|
||||
int slave_read_sync_header(const uchar *header, unsigned long total_len,
|
||||
int *semi_flags,
|
||||
Master_info *mi,
|
||||
const uchar **payload, unsigned long *payload_len);
|
||||
|
||||
/* A slave replies to the master indicating its replication process. It
|
||||
@@ -94,10 +95,11 @@ public:
|
||||
int request_transmit(Master_info *mi);
|
||||
void kill_connection(Master_info *mi);
|
||||
|
||||
/// @deprecated For `Rpl_semi_sync_slave_status` compatibility only
|
||||
bool m_slave_enabled;
|
||||
private:
|
||||
/* True when init_object has been called */
|
||||
bool m_init_done;
|
||||
bool m_slave_enabled; /* semi-sync is enabled on the slave */
|
||||
bool m_delay_master;
|
||||
unsigned int m_kill_conn_timeout;
|
||||
};
|
||||
@@ -115,4 +117,5 @@ extern unsigned long long rpl_semi_sync_slave_send_ack;
|
||||
extern int rpl_semi_sync_enabled(THD *thd, SHOW_VAR *var, void *buff,
|
||||
system_status_var *status_var,
|
||||
enum_var_type scope);
|
||||
#endif /* HAVE_REPLICATION */
|
||||
#endif /* SEMISYNC_SLAVE_H */
|
||||
|
||||
@@ -4977,7 +4977,7 @@ Stopping slave I/O thread due to out-of-memory error from master");
|
||||
if (repl_semisync_slave.
|
||||
slave_read_sync_header((const uchar*) mysql->net.read_pos + 1,
|
||||
event_len,
|
||||
&(mi->semi_ack), &event_buf, &event_len))
|
||||
mi, &event_buf, &event_len))
|
||||
{
|
||||
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
|
||||
ER_THD(thd, ER_SLAVE_FATAL_ERROR),
|
||||
@@ -5034,7 +5034,7 @@ Stopping slave I/O thread due to out-of-memory error from master");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (repl_semisync_slave.get_slave_enabled() &&
|
||||
if (repl_semisync_slave.get_slave_enabled(mi) &&
|
||||
mi->semi_sync_reply_enabled &&
|
||||
(mi->semi_ack & SEMI_SYNC_NEED_ACK))
|
||||
{
|
||||
@@ -5078,7 +5078,7 @@ Stopping slave I/O thread due to out-of-memory error from master");
|
||||
master info only when ack is needed. This may lead to at least one
|
||||
group transaction delay but affords better performance improvement.
|
||||
*/
|
||||
(!repl_semisync_slave.get_slave_enabled() ||
|
||||
(!repl_semisync_slave.get_slave_enabled(mi) ||
|
||||
(!(mi->semi_ack & SEMI_SYNC_SLAVE_DELAY_SYNC) ||
|
||||
(mi->semi_ack & (SEMI_SYNC_NEED_ACK)))) &&
|
||||
(DBUG_IF("failed_flush_master_info") ||
|
||||
@@ -6754,7 +6754,7 @@ dbug_gtid_accept:
|
||||
*/
|
||||
mi->do_accept_own_server_id=
|
||||
(s_id == global_system_variables.server_id &&
|
||||
repl_semisync_slave.get_slave_enabled() && opt_gtid_strict_mode &&
|
||||
repl_semisync_slave.get_slave_enabled(mi) && opt_gtid_strict_mode &&
|
||||
mi->using_gtid != Master_info::USE_GTID_NO &&
|
||||
!mysql_bin_log.check_strict_gtid_sequence(event_gtid.domain_id,
|
||||
event_gtid.server_id,
|
||||
|
||||
@@ -244,6 +244,16 @@ void mysql_client_binlog_statement(THD* thd)
|
||||
thd->lex->comment.length : 2048),
|
||||
thd->lex->comment.str));
|
||||
|
||||
/*
|
||||
The parser refuses BINLOG in a routine body; this catches dynamic SQL
|
||||
(EXECUTE IMMEDIATE, EXECUTE) run from a routine.
|
||||
*/
|
||||
if (thd->spcont)
|
||||
{
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "BINLOG");
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (check_global_access(thd, PRIV_STMT_BINLOG))
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
@@ -421,8 +431,12 @@ void mysql_client_binlog_statement(THD* thd)
|
||||
applied in PS mode. So, reset the current lex temporary to point to
|
||||
thd->main_lex before running ev->apply_event() and restore its
|
||||
original value on return.
|
||||
|
||||
Likewise, point thd->stmt_arena to thd: otherwise a Query event's
|
||||
statement leaks its allocations onto the PS's persistent arena.
|
||||
*/
|
||||
LEX *backup_lex;
|
||||
SCOPE_VALUE(thd->stmt_arena, static_cast<Query_arena *>(thd));
|
||||
|
||||
thd->backup_and_reset_current_lex(&backup_lex);
|
||||
err= save_restore_context_apply_event(ev, rgi);
|
||||
|
||||
@@ -1195,11 +1195,6 @@ void Explain_select::print_explain_json(Explain_query *query,
|
||||
writer->add_member("outer_ref_condition");
|
||||
write_item(writer, outer_ref_cond);
|
||||
}
|
||||
if (pseudo_bits_cond)
|
||||
{
|
||||
writer->add_member("pseudo_bits_condition");
|
||||
write_item(writer, pseudo_bits_cond);
|
||||
}
|
||||
|
||||
/* we do not print HAVING which always evaluates to TRUE */
|
||||
if (having || (having_value == Item::COND_FALSE))
|
||||
|
||||
@@ -248,7 +248,6 @@ public:
|
||||
/* Expensive constant condition */
|
||||
Item *exec_const_cond;
|
||||
Item *outer_ref_cond;
|
||||
Item *pseudo_bits_cond;
|
||||
|
||||
/* HAVING condition */
|
||||
Item *having;
|
||||
|
||||
@@ -560,7 +560,7 @@ void JOIN::init(THD *thd_arg, List<Item> &fields_arg,
|
||||
first_select= sub_select;
|
||||
group_sent= 0;
|
||||
|
||||
outer_ref_cond= pseudo_bits_cond= NULL;
|
||||
outer_ref_cond= NULL;
|
||||
in_to_exists_where= NULL;
|
||||
in_to_exists_having= NULL;
|
||||
emb_sjm_nest= NULL;
|
||||
@@ -980,6 +980,7 @@ bool vers_select_conds_t::init_from_sysvar(THD *thd)
|
||||
{
|
||||
vers_asof_timestamp_t &in= thd->variables.vers_asof_timestamp;
|
||||
type= (vers_system_time_t) in.type;
|
||||
from_sysvar= (type != SYSTEM_TIME_UNSPECIFIED);
|
||||
delete_history= false;
|
||||
start.unit= VERS_TIMESTAMP;
|
||||
if (type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL)
|
||||
@@ -1302,6 +1303,21 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
partition_info *part_info= table->table->part_info;
|
||||
if (part_info && part_info->vers_info &&
|
||||
!table->partition_names &&
|
||||
!vers_conditions.was_set() &&
|
||||
!vers_conditions.from_sysvar)
|
||||
{
|
||||
/*
|
||||
No row_end condition: prune_partitions() restricts read_partitions
|
||||
to now_part on each execution, with the same conditions.
|
||||
*/
|
||||
vers_conditions.set_all();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vers_conditions.is_set())
|
||||
{
|
||||
if (vers_conditions.was_set() &&
|
||||
@@ -14529,6 +14545,26 @@ bool build_tmp_join_prefix_cond(JOIN *join, JOIN_TAB *last_tab, Item **ret)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Find the last top level table in the join order that is neither
|
||||
const nor a materialized semijoin nest. Return its index in
|
||||
join->join_tab, or a value below join->const_tables when no such
|
||||
table exists.
|
||||
*/
|
||||
|
||||
static uint
|
||||
get_last_top_base_tab_idx(const JOIN *join)
|
||||
{
|
||||
uint i;
|
||||
for (i= join->top_join_tab_count - 1; i >= join->const_tables; i--)
|
||||
{
|
||||
if (!join->join_tab[i].bush_children)
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
{
|
||||
@@ -14542,6 +14578,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
"attached_conditions_computation");
|
||||
add_not_null_conds(join);
|
||||
table_map used_tables;
|
||||
|
||||
/* Step #1 and Step #2 both decide from this index. */
|
||||
const uint last_top_base_tab_idx= get_last_top_base_tab_idx(join);
|
||||
|
||||
/*
|
||||
Step #1: Extract constant condition
|
||||
- Extract and check the constant part of the WHERE
|
||||
@@ -14607,40 +14647,36 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
join->exec_const_cond= const_cond;
|
||||
}
|
||||
|
||||
if (join->table_count != join->const_tables)
|
||||
{
|
||||
COND *outer_ref_cond= make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
OUTER_REF_TABLE_BIT,
|
||||
OUTER_REF_TABLE_BIT,
|
||||
-1, FALSE, FALSE);
|
||||
if (outer_ref_cond)
|
||||
/*
|
||||
Collect the conjuncts that are checked once for each execution
|
||||
of the query, before the join reads a table that is not const.
|
||||
Each refers to at least one pseudo table in pseudo_bits and to
|
||||
no table that is not const.
|
||||
|
||||
A conjunct that refers to a column of an outer query is always
|
||||
collected, because that column has one value for each
|
||||
execution. rand() takes a new value on each call, and a
|
||||
conjunct that calls it is checked for each row of the last top
|
||||
level table that is neither const nor a materialized semijoin
|
||||
nest. When no such table exists, the join returns at most one
|
||||
row, and its rand() conjuncts are collected here.
|
||||
*/
|
||||
table_map pseudo_bits= OUTER_REF_TABLE_BIT;
|
||||
if (last_top_base_tab_idx < join->const_tables)
|
||||
pseudo_bits= PSEUDO_TABLE_BITS;
|
||||
COND *pseudo_cond= make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
pseudo_bits,
|
||||
pseudo_bits,
|
||||
-1, FALSE, FALSE);
|
||||
if (pseudo_cond)
|
||||
{
|
||||
add_cond_and_fix(thd, &outer_ref_cond, join->outer_ref_cond);
|
||||
join->outer_ref_cond= outer_ref_cond;
|
||||
add_cond_and_fix(thd, &pseudo_cond, join->outer_ref_cond);
|
||||
join->outer_ref_cond= pseudo_cond;
|
||||
|
||||
Json_writer_object trace(thd);
|
||||
trace.add("outer_ref_cond", outer_ref_cond);
|
||||
}
|
||||
else if (thd->is_error())
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
COND *pseudo_bits_cond=
|
||||
make_cond_for_table(thd, cond,
|
||||
join->const_table_map |
|
||||
PSEUDO_TABLE_BITS,
|
||||
PSEUDO_TABLE_BITS,
|
||||
-1, FALSE, FALSE);
|
||||
if (pseudo_bits_cond)
|
||||
{
|
||||
add_cond_and_fix(thd, &pseudo_bits_cond,
|
||||
join->pseudo_bits_cond);
|
||||
join->pseudo_bits_cond= pseudo_bits_cond;
|
||||
|
||||
Json_writer_object trace(thd);
|
||||
trace.add("pseudo_bits_cond", pseudo_bits_cond);
|
||||
trace.add("outer_ref_cond", pseudo_cond);
|
||||
}
|
||||
else if (thd->is_error())
|
||||
DBUG_RETURN(1);
|
||||
@@ -14653,13 +14689,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
*/
|
||||
|
||||
uint i;
|
||||
for (i= join->top_join_tab_count - 1; i >= join->const_tables; i--)
|
||||
{
|
||||
if (!join->join_tab[i].bush_children)
|
||||
break;
|
||||
}
|
||||
uint last_top_base_tab_idx= i;
|
||||
|
||||
table_map save_used_tables= 0;
|
||||
used_tables=((select->const_tables=join->const_table_map) |
|
||||
OUTER_REF_TABLE_BIT | RAND_TABLE_BIT);
|
||||
@@ -24080,12 +24109,11 @@ do_select(JOIN *join, Procedure *procedure)
|
||||
HAVING will be checked after processing aggregate functions,
|
||||
But WHERE should checked here (we alredy have read tables).
|
||||
Notice that make_join_select() splits all conditions in this case
|
||||
into two groups exec_const_cond and outer_ref_cond.
|
||||
If join->table_count == join->const_tables then it is
|
||||
sufficient to check only the condition pseudo_bits_cond.
|
||||
into two groups exec_const_cond and outer_ref_cond. exec_const_cond
|
||||
is checked before the join starts, and it is sufficient to check
|
||||
only the condition outer_ref_cond.
|
||||
*/
|
||||
DBUG_ASSERT(join->outer_ref_cond == NULL);
|
||||
if (!join->pseudo_bits_cond || join->pseudo_bits_cond->val_bool())
|
||||
if (!join->outer_ref_cond || join->outer_ref_cond->val_bool())
|
||||
{
|
||||
// HAVING will be checked by end_select
|
||||
error= (*end_select)(join, 0, 0);
|
||||
@@ -31713,7 +31741,6 @@ int JOIN::save_explain_data_intern(Explain_query *output,
|
||||
|
||||
xpl_sel->exec_const_cond= exec_const_cond;
|
||||
xpl_sel->outer_ref_cond= outer_ref_cond;
|
||||
xpl_sel->pseudo_bits_cond= pseudo_bits_cond;
|
||||
if (tmp_having)
|
||||
xpl_sel->having= tmp_having;
|
||||
else
|
||||
|
||||
@@ -1653,8 +1653,15 @@ public:
|
||||
ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select
|
||||
COND *conds; // ---"---
|
||||
Item *conds_history; // store WHERE for explain
|
||||
COND *outer_ref_cond; ///<part of conds containing only outer references
|
||||
COND *pseudo_bits_cond; // part of conds containing special bita
|
||||
/*
|
||||
Condition to be checked once for every join execution, before reading any
|
||||
non-const tables. (Do not confuse this with exec_const_cond which is
|
||||
constant across all executions). Normally, this is condition that refers
|
||||
to outer tables only.
|
||||
For degenerate joins that only have constant tables and semi-join nests,
|
||||
this includes conditions with RAND_TABLE_BIT.
|
||||
*/
|
||||
Item *outer_ref_cond;
|
||||
TABLE_LIST *tables_list; ///<hold 'tables' parameter of mysql_select
|
||||
List<TABLE_LIST> *join_list; ///< list of joined tables in reverse order
|
||||
COND_EQUAL *cond_equal;
|
||||
|
||||
@@ -3453,6 +3453,13 @@ static my_bool processlist_callback(THD *tmp, processlist_callback_arg *arg)
|
||||
ulonglong max_counter;
|
||||
bool got_thd_data;
|
||||
|
||||
/* Test hook: pause after tmp_sctx is captured, before it is read. */
|
||||
DBUG_EXECUTE_IF("processlist_callback_pause_after_sctx_capture",
|
||||
if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER &&
|
||||
tmp_sctx != &tmp->main_security_ctx)
|
||||
DEBUG_SYNC(arg->thd,
|
||||
"processlist_callback_captured_sctx"););
|
||||
|
||||
if (!thd_visible_in_processlist(arg->thd->security_ctx, tmp))
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -8542,6 +8542,8 @@ table_index_name:
|
||||
binlog_base64_event:
|
||||
BINLOG_SYM TEXT_STRING_sys
|
||||
{
|
||||
if (Lex->sphead)
|
||||
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BINLOG"));
|
||||
Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
|
||||
Lex->comment= $2;
|
||||
Lex->ident.str= NULL;
|
||||
@@ -8550,6 +8552,8 @@ binlog_base64_event:
|
||||
|
|
||||
BINLOG_SYM '@' ident_or_text ',' '@' ident_or_text
|
||||
{
|
||||
if (Lex->sphead)
|
||||
my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BINLOG"));
|
||||
Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
|
||||
Lex->comment= $3;
|
||||
Lex->ident= $6;
|
||||
|
||||
@@ -2503,7 +2503,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var)
|
||||
Master_info *mi;
|
||||
bool res= false;
|
||||
|
||||
if (!base_name->length)
|
||||
if (!base_name->str)
|
||||
base_name= &thd->variables.default_master_connection;
|
||||
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
@@ -2549,7 +2549,7 @@ Sys_var_slave_parallel_mode::global_value_ptr(THD *thd,
|
||||
enum_slave_parallel_mode val=
|
||||
(enum_slave_parallel_mode)opt_slave_parallel_mode;
|
||||
|
||||
if (!base_name->length)
|
||||
if (!base_name->str)
|
||||
base_name= &thd->variables.default_master_connection;
|
||||
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
@@ -5742,7 +5742,7 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
|
||||
bool result= true; // Assume error
|
||||
LEX_CSTRING *base_name= &var->base;
|
||||
|
||||
if (!base_name->length)
|
||||
if (!base_name->str)
|
||||
base_name= &thd->variables.default_master_connection;
|
||||
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
|
||||
38
sql/table.h
38
sql/table.h
@@ -2345,11 +2345,47 @@ public:
|
||||
|
||||
struct vers_select_conds_t
|
||||
{
|
||||
/*
|
||||
Effective system-time selection used to build the actual row condition
|
||||
(row_start/row_end predicate). is_set() tests it against SYSTEM_TIME_UNSPECIFIED.
|
||||
set_all() forces it to SYSTEM_TIME_ALL in three situations:
|
||||
|
||||
1) once the row_start/row_end predicate for the requested time has been
|
||||
generated and attached to the query - marking it applied so a later pass
|
||||
or the next prepared-statement execution neither rebuilds nor re-applies
|
||||
it (guarded by "if (type == SYSTEM_TIME_ALL) continue");
|
||||
|
||||
2) a versioned table whose history is stored in partitions is queried with
|
||||
an explicit PARTITION(...) clause - the named partitions are read as-is,
|
||||
without a versioning predicate;
|
||||
|
||||
3) the same kind of table accessed without FOR SYSTEM_TIME, PARTITION(...)
|
||||
and @@system_versioning_asof -
|
||||
read_partitions is narrowed to the now-partition, which takes the place
|
||||
of the row_end predicate.
|
||||
*/
|
||||
vers_system_time_t type;
|
||||
/*
|
||||
Originally requested system-time selection, set by init() from an explicit
|
||||
FOR SYSTEM_TIME or DELETE HISTORY clause, or copied with the conditions of
|
||||
an outer derived table or view. Unlike 'type' it is NOT overwritten by
|
||||
set_all(), so it still reflects the request after the predicate has been
|
||||
applied.
|
||||
Tested by was_set(); used by print() to reconstruct the FOR SYSTEM_TIME
|
||||
clause.
|
||||
*/
|
||||
vers_system_time_t orig_type;
|
||||
bool used:1;
|
||||
bool delete_history:1;
|
||||
bool has_param:1;
|
||||
/*
|
||||
Set by init_from_sysvar() when @@system_versioning_asof supplied the
|
||||
selection. Tested to keep the now-partition narrowing off, so the query
|
||||
still reads history. Unlike orig_type it affects neither print() nor
|
||||
the write lock check, and it persists to prepared-statement
|
||||
re-execution, where init_from_sysvar() is skipped.
|
||||
*/
|
||||
bool from_sysvar:1;
|
||||
Vers_history_point start;
|
||||
Vers_history_point end;
|
||||
Lex_ident_column name;
|
||||
@@ -2366,6 +2402,7 @@ struct vers_select_conds_t
|
||||
used= false;
|
||||
delete_history= false;
|
||||
has_param= false;
|
||||
from_sysvar= false;
|
||||
start.empty();
|
||||
end.empty();
|
||||
}
|
||||
@@ -2381,6 +2418,7 @@ struct vers_select_conds_t
|
||||
delete_history= (type == SYSTEM_TIME_HISTORY ||
|
||||
type == SYSTEM_TIME_BEFORE);
|
||||
has_param= false;
|
||||
from_sysvar= false;
|
||||
start= _start;
|
||||
end= _end;
|
||||
name= _name;
|
||||
|
||||
@@ -396,3 +396,56 @@ CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?
|
||||
</line>
|
||||
</t1>
|
||||
|
||||
#
|
||||
# MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
|
||||
# after insert into CONNECT table
|
||||
#
|
||||
CREATE TABLE t1 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t2 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
INSERT INTO t2 VALUES (2);
|
||||
ERROR HY000: Got error 174 'Missing XMLDB' from CONNECT
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t3 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
CREATE TABLE t4 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
CREATE TABLE t5 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=/XMLDB,xmlsup=libxml2';
|
||||
INSERT INTO t3 VALUES (3);
|
||||
INSERT INTO t4 VALUES (4);
|
||||
INSERT INTO t5 VALUES (5);
|
||||
DROP TABLE t3, t4, t5;
|
||||
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/y.xml');
|
||||
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
|
||||
y <?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created by the MariaDB CONNECT Storage Engine-->
|
||||
<XMLDB>
|
||||
<t3>
|
||||
<t3>
|
||||
<id>3</id>
|
||||
</t3>
|
||||
</t3>
|
||||
<t4>
|
||||
<t4>
|
||||
<id>4</id>
|
||||
</t4>
|
||||
</t4>
|
||||
<t5>
|
||||
<t5>
|
||||
<id>5</id>
|
||||
</t5>
|
||||
</t5>
|
||||
</XMLDB>
|
||||
|
||||
#
|
||||
# End of 10.11 tests
|
||||
#
|
||||
|
||||
@@ -318,3 +318,52 @@ SELECT CAST(@a AS CHAR CHARACTER SET latin1);
|
||||
--remove_file $MYSQLD_DATADIR/test/xsample.xml
|
||||
--remove_file $MYSQLD_DATADIR/test/latin1.xml
|
||||
--remove_file $MYSQLD_DATADIR/test/cp1251.xml
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
|
||||
--echo # after insert into CONNECT table
|
||||
--echo #
|
||||
# Create x.xml so that the inserts below go into an existing file
|
||||
CREATE TABLE t1 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t1;
|
||||
|
||||
let $xmldb= `SELECT REPEAT('A', 240)`;
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t2 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_regex /A{60,}/XMLDB/
|
||||
--error ER_GET_ERRMSG
|
||||
INSERT INTO t2 VALUES (2);
|
||||
DROP TABLE t2;
|
||||
--remove_file $MYSQLD_DATADIR/test/x.xml
|
||||
|
||||
# Long XmlDB node created by t3 must be found by t4 and t5
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t3 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t4 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t5 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=/$xmldb,xmlsup=libxml2';
|
||||
INSERT INTO t3 VALUES (3);
|
||||
INSERT INTO t4 VALUES (4);
|
||||
INSERT INTO t5 VALUES (5);
|
||||
DROP TABLE t3, t4, t5;
|
||||
--chmod 0777 $MYSQLD_DATADIR/test/y.xml
|
||||
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
|
||||
--eval SET @a=LOAD_FILE('$MYSQLD_DATADIR/test/y.xml')
|
||||
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
|
||||
--remove_file $MYSQLD_DATADIR/test/y.xml
|
||||
|
||||
--echo #
|
||||
--echo # End of 10.11 tests
|
||||
--echo #
|
||||
|
||||
@@ -819,6 +819,23 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
|
||||
return rc;
|
||||
} // end of LoadTableFile
|
||||
|
||||
/***********************************************************************/
|
||||
/* Return name as an Xpath. If name is not an Xpath, construct one */
|
||||
/* that will find it anywhere. */
|
||||
/***********************************************************************/
|
||||
static char *MakeXpath(PGLOBAL g, PCSZ name)
|
||||
{
|
||||
size_t len = strlen(name) + 3;
|
||||
char *xpath = (char*)PlugSubAlloc(g, NULL, len);
|
||||
|
||||
if (!strchr(name, '/'))
|
||||
snprintf(xpath, len, "//%s", name);
|
||||
else
|
||||
snprintf(xpath, len, "%s", name);
|
||||
|
||||
return xpath;
|
||||
} // end of MakeXpath
|
||||
|
||||
/***********************************************************************/
|
||||
/* Initialize the processing of the XML file. */
|
||||
/* Note: this function can be called several times, eventally before */
|
||||
@@ -850,7 +867,7 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
#else
|
||||
if (!Root) {
|
||||
#endif
|
||||
char tabpath[64], filename[_MAX_PATH];
|
||||
char filename[_MAX_PATH];
|
||||
|
||||
// We used the file name relative to recorded datapath
|
||||
PlugSetPath(filename, Xfile, GetPath());
|
||||
@@ -866,15 +883,8 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
goto error;
|
||||
} // endif Root
|
||||
|
||||
// If tabname is not an Xpath,
|
||||
// construct one that will find it anywhere
|
||||
if (!strchr(Tabname, '/'))
|
||||
snprintf(tabpath, sizeof(tabpath), "//%s", Tabname);
|
||||
else
|
||||
snprintf(tabpath, sizeof(tabpath), "%s", Tabname);
|
||||
|
||||
// Evaluate table xpath
|
||||
if ((TabNode = Root->SelectSingleNode(g, tabpath))) {
|
||||
if ((TabNode = Root->SelectSingleNode(g, MakeXpath(g, Tabname)))) {
|
||||
if (TabNode->GetType() != XML_ELEMENT_NODE) {
|
||||
snprintf(g->Message, sizeof(g->Message), MSG(BAD_NODE_TYPE), TabNode->GetType());
|
||||
goto error;
|
||||
@@ -882,15 +892,7 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
|
||||
} else if (Mode == MODE_INSERT && XmlDB) {
|
||||
// We are adding a new table to a multi-table file
|
||||
|
||||
// If XmlDB is not an Xpath,
|
||||
// construct one that will find it anywhere
|
||||
if (!strchr(XmlDB, '/'))
|
||||
strcat(strcpy(tabpath, "//"), XmlDB);
|
||||
else
|
||||
strcpy(tabpath, XmlDB);
|
||||
|
||||
if (!(DBnode = Root->SelectSingleNode(g, tabpath))) {
|
||||
if (!(DBnode = Root->SelectSingleNode(g, MakeXpath(g, XmlDB)))) {
|
||||
// DB node does not exist yet; we cannot create it
|
||||
// because we don't know where it should be placed
|
||||
snprintf(g->Message, sizeof(g->Message), MSG(MISSING_NODE), XmlDB, Xfile);
|
||||
|
||||
@@ -104,7 +104,10 @@ ExternalProject_Add(duckdb_build
|
||||
-DBUILD_TPCE=OFF
|
||||
-DEXTENSION_STATIC_BUILD=1
|
||||
"-DDUCKDB_EXTENSION_CONFIGS=${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb_extensions.cmake"
|
||||
"-DCMAKE_CXX_FLAGS=${_DUCKDB_EXTRA_CXX_FLAGS}"
|
||||
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} ${_DUCKDB_EXTRA_CXX_FLAGS}"
|
||||
"-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}"
|
||||
"-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}"
|
||||
"-DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS}"
|
||||
-DENABLE_SANITIZER=${ADDRESS_SANITIZER}
|
||||
-DENABLE_UBSAN=${UB_SANITIZER}
|
||||
-DOVERRIDE_GIT_DESCRIBE=${_DUCKDB_GIT_DESCRIBE}
|
||||
|
||||
@@ -1165,6 +1165,15 @@ int ha_duckdb::external_lock(THD *thd, int lock_type)
|
||||
DBUG_ENTER("ha_duckdb::external_lock");
|
||||
if (lock_type != F_UNLCK)
|
||||
{
|
||||
/* DuckDB has no row locks: reject SELECT ... FOR UPDATE / LOCK IN SHARE MODE. */
|
||||
if (thd_sql_command(thd) == SQLCOM_SELECT &&
|
||||
(table->reginfo.lock_type == TL_READ_WITH_SHARED_LOCKS ||
|
||||
table->reginfo.lock_type >= TL_FIRST_WRITE))
|
||||
{
|
||||
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "locking SELECT");
|
||||
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
||||
}
|
||||
|
||||
/* DuckDB does not support XA transactions. Reject DML early. */
|
||||
if (myduck::reject_xa_if_active(thd))
|
||||
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT);
|
||||
SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP;
|
||||
id col1 col2
|
||||
NULL NULL NULL
|
||||
ALTER TABLE t1 ENGINE=DuckDB;
|
||||
SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP;
|
||||
id col1 col2
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE t (id INT PRIMARY KEY, v INT) ENGINE=DuckDB;
|
||||
INSERT INTO t VALUES (1, 1),(2, 2);
|
||||
SELECT v FROM t WHERE id=1 FOR UPDATE;
|
||||
ERROR 42000: This version of MariaDB doesn't yet support 'locking SELECT'
|
||||
SELECT v FROM t WHERE id=1 LOCK IN SHARE MODE;
|
||||
ERROR 42000: This version of MariaDB doesn't yet support 'locking SELECT'
|
||||
DROP TABLE t;
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB;
|
||||
INSERT INTO t VALUES(1, 1, 1, 1);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo ####################
|
||||
--echo # TEST FOR INSTANT #
|
||||
--echo ####################
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #################
|
||||
--echo # TEST FOR COPY #
|
||||
--echo #################
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo ####################
|
||||
--echo # TEST FOR INSTANT #
|
||||
--echo ####################
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
SET GLOBAL duckdb_copy_ddl_in_batch=ON;
|
||||
--source ../include/alter_engine_duckdb.inc
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
--disable_query_log
|
||||
SET @saved_duckdb_dml_in_batch = @@GLOBAL.duckdb_dml_in_batch;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
--disable_query_log
|
||||
SET @saved_duckdb_dml_in_batch = @@GLOBAL.duckdb_dml_in_batch;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
--skip TODO
|
||||
create table t1(id int primary key) engine = duckdb;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
# DuckDB has default databases named 'temp' and 'system', if we try to create
|
||||
# schema named 'temp' or 'system', we get an error.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # check collation config when execute
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
# file: duckdb_create_table.test
|
||||
#
|
||||
# Test case for creating tables with various MySQL field types in DuckDB.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--source include/have_debug.inc
|
||||
|
||||
--disable_query_log
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
--source include/have_debug.inc
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # Cross-engine join: DuckDB + InnoDB via _mdb_scan replacement scan
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--source include/have_sequence.inc
|
||||
|
||||
--echo #
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
--echo #
|
||||
--echo # Cross-engine RYOW: the same external table referenced more than once.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
--source include/have_sequence.inc
|
||||
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # Cross-engine UNION: DuckDB + InnoDB via select_handler unit pushdown
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--source include/have_sequence.inc
|
||||
|
||||
--echo #
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
set global duckdb_dml_in_batch = OFF;
|
||||
--source ../include/decimal_precision_all_possibilities.inc
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # DuckDB DELETE operations test
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # DuckDB UPDATE operations test
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
--echo #
|
||||
--echo # DuckDB DROP DATABASE operations test
|
||||
--echo #
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--source include/not_msan.inc
|
||||
SET GLOBAL duckdb_require_primary_key=OFF;
|
||||
|
||||
--echo #
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
--source ../include/have_duckdb.inc
|
||||
--source include/not_msan.inc
|
||||
|
||||
CREATE TABLE t_innodb (
|
||||
id INT PRIMARY KEY,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user