1
0
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:
Marko Mäkelä
2026-10-20 19:09:40 +03:00
147 changed files with 3124 additions and 566 deletions

View File

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

View 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

View 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

View File

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

View File

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

View File

@@ -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": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": {

View File

@@ -0,0 +1 @@
--memory.optimizer_row_copy_cost=0

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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

View File

@@ -0,0 +1,3 @@
--innodb-buffer-pool-size=8M
--innodb-buffer-pool-size-max=16M
--innodb-read-ahead-threshold=0

View 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

View File

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

View 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

View 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

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -28,7 +28,6 @@ SET(MYSYS_SSL_HIDDEN_SOURCES
my_sha384.cc
my_sha512.cc
my_md5.cc
openssl.c
)
SET(MYSYS_SSL_SOURCES

View File

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

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -248,7 +248,6 @@ public:
/* Expensive constant condition */
Item *exec_const_cond;
Item *outer_ref_cond;
Item *pseudo_bits_cond;
/* HAVING condition */
Item *having;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo ####################
--echo # TEST FOR INSTANT #
--echo ####################

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #################
--echo # TEST FOR COPY #
--echo #################

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo ####################
--echo # TEST FOR INSTANT #
--echo ####################

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
SET GLOBAL duckdb_copy_ddl_in_batch=ON;
--source ../include/alter_engine_duckdb.inc

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
--source ../include/have_duckdb.inc
--source include/not_msan.inc
--echo #
--echo # check collation config when execute
--echo #

View File

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

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--source include/have_debug.inc
--disable_query_log

View File

@@ -1,5 +1,4 @@
--source ../include/have_duckdb.inc
--source include/not_msan.inc
--source include/have_debug.inc

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #
--echo # Cross-engine join: DuckDB + InnoDB via _mdb_scan replacement scan
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--source include/have_sequence.inc
--echo #

View File

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

View File

@@ -1,5 +1,4 @@
--source ../include/have_duckdb.inc
--source include/not_msan.inc
--source include/have_sequence.inc
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #
--echo # Cross-engine UNION: DuckDB + InnoDB via select_handler unit pushdown
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--source include/have_sequence.inc
--echo #

View File

@@ -1,5 +1,4 @@
--source ../include/have_duckdb.inc
--source include/not_msan.inc
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
set global duckdb_dml_in_batch = OFF;
--source ../include/decimal_precision_all_possibilities.inc

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #
--echo # DuckDB DELETE operations test
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #
--echo # DuckDB UPDATE operations test
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
--echo #
--echo # DuckDB DROP DATABASE operations test
--echo #

View File

@@ -1,4 +1,3 @@
--source include/not_msan.inc
SET GLOBAL duckdb_require_primary_key=OFF;
--echo #

View File

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