mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
Merge 10.11 into 11.4
This commit is contained in:
@@ -24,12 +24,6 @@
|
||||
#define HAVE_OPENSSL11 1
|
||||
#define SSL_LIBRARY OpenSSL_version(OPENSSL_VERSION)
|
||||
#define ERR_remove_state(X) ERR_clear_error()
|
||||
#define EVP_CIPHER_CTX_SIZE 200
|
||||
#define EVP_MD_CTX_SIZE 80
|
||||
#undef EVP_MD_CTX_init
|
||||
#define EVP_MD_CTX_init(X) do { memset((X), 0, EVP_MD_CTX_SIZE); EVP_MD_CTX_reset(X); } while(0)
|
||||
#undef EVP_CIPHER_CTX_init
|
||||
#define EVP_CIPHER_CTX_init(X) do { memset((X), 0, EVP_CIPHER_CTX_SIZE); EVP_CIPHER_CTX_reset(X); } while(0)
|
||||
|
||||
/*
|
||||
Macros below are deprecated. OpenSSL 1.1 may define them or not,
|
||||
@@ -66,24 +60,16 @@
|
||||
#endif /* HAVE_OPENSSL11 */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WOLFSSL
|
||||
#define EVP_MD_CTX_SIZE sizeof(wc_Md5)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENSSL11
|
||||
#ifndef ASN1_STRING_get0_data
|
||||
#define ASN1_STRING_get0_data(X) ASN1_STRING_data(X)
|
||||
#endif
|
||||
#ifndef EVP_MD_CTX_SIZE
|
||||
#define EVP_MD_CTX_SIZE sizeof(EVP_MD_CTX)
|
||||
#endif
|
||||
|
||||
#ifndef DH_set0_pqg
|
||||
#define DH_set0_pqg(D,P,Q,G) ((D)->p= (P), (D)->g= (G))
|
||||
#endif
|
||||
|
||||
#define EVP_CIPHER_CTX_encrypting(ctx) ((ctx)->encrypt)
|
||||
#define EVP_CIPHER_CTX_SIZE sizeof(EVP_CIPHER_CTX)
|
||||
|
||||
#ifndef HAVE_WOLFSSL
|
||||
#define OPENSSL_init_ssl(X,Y) SSL_library_init()
|
||||
@@ -97,13 +83,3 @@
|
||||
#ifndef TLS1_3_VERSION
|
||||
#define SSL_CTX_set_ciphersuites(X,Y) 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int check_openssl_compatibility();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
47
mysql-test/main/events_debug.result
Normal file
47
mysql-test/main/events_debug.result
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
|
||||
#
|
||||
CREATE USER definer@localhost;
|
||||
GRANT EVENT ON *.* TO definer@localhost;
|
||||
GRANT SUPER ON *.* TO definer@localhost;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
# Per-session DEBUG_SYNC: must arm from inside the event body (its own
|
||||
# thd), not from another connection.
|
||||
CREATE DEFINER=definer@localhost EVENT ev
|
||||
ON SCHEDULE AT CURRENT_TIMESTAMP
|
||||
DO
|
||||
BEGIN
|
||||
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
|
||||
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
|
||||
END|
|
||||
Warnings:
|
||||
Warning 1105 Event scheduler is switched off, use SET GLOBAL event_scheduler=ON to enable it.
|
||||
SET GLOBAL event_scheduler= ON;
|
||||
# Connect 'waiter' now: iterate() holds server_threads' rwlock for the
|
||||
# whole scan; connecting after would deadlock (needs that lock's
|
||||
# write side).
|
||||
connect waiter, localhost, root,,;
|
||||
connection default;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
|
||||
# Pause right after processlist_callback() captures the
|
||||
# DEFINER-switched security_ctx, before USER/HOST are read.
|
||||
SET @saved_dbug= @@SESSION.debug_dbug;
|
||||
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
|
||||
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
|
||||
# Filter by name: checks the value read through the captured pointer.
|
||||
SELECT user, host FROM information_schema.processlist WHERE user = 'definer';
|
||||
connection waiter;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
|
||||
# Let the event finish and execute() return.
|
||||
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
|
||||
# Release the scanner to dereference the pointer it captured.
|
||||
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
|
||||
disconnect waiter;
|
||||
connection default;
|
||||
user host
|
||||
definer localhost
|
||||
SET SESSION debug_dbug= @saved_dbug;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
SET GLOBAL event_scheduler= OFF;
|
||||
DROP USER definer@localhost;
|
||||
# End of 10.11 tests
|
||||
68
mysql-test/main/events_debug.test
Normal file
68
mysql-test/main/events_debug.test
Normal file
@@ -0,0 +1,68 @@
|
||||
--source include/have_debug.inc
|
||||
--source include/have_debug_sync.inc
|
||||
--source include/not_embedded.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39146 Fix stack-use-after-return in PROCESSLIST for event threads
|
||||
--echo #
|
||||
|
||||
CREATE USER definer@localhost;
|
||||
GRANT EVENT ON *.* TO definer@localhost;
|
||||
GRANT SUPER ON *.* TO definer@localhost;
|
||||
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
|
||||
--echo # Per-session DEBUG_SYNC: must arm from inside the event body (its own
|
||||
--echo # thd), not from another connection.
|
||||
--delimiter |
|
||||
CREATE DEFINER=definer@localhost EVENT ev
|
||||
ON SCHEDULE AT CURRENT_TIMESTAMP
|
||||
DO
|
||||
BEGIN
|
||||
SET DEBUG_SYNC= 'event_worker_thread_after_execute_returned SIGNAL event_execute_returned';
|
||||
SET DEBUG_SYNC= 'now SIGNAL definer_ctx_active WAIT_FOR event_may_finish';
|
||||
END|
|
||||
--delimiter ;
|
||||
|
||||
SET GLOBAL event_scheduler= ON;
|
||||
|
||||
--echo # Connect 'waiter' now: iterate() holds server_threads' rwlock for the
|
||||
--echo # whole scan; connecting after would deadlock (needs that lock's
|
||||
--echo # write side).
|
||||
connect (waiter, localhost, root,,);
|
||||
connection default;
|
||||
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR definer_ctx_active';
|
||||
|
||||
--echo # Pause right after processlist_callback() captures the
|
||||
--echo # DEFINER-switched security_ctx, before USER/HOST are read.
|
||||
SET @saved_dbug= @@SESSION.debug_dbug;
|
||||
SET SESSION debug_dbug= '+d,processlist_callback_pause_after_sctx_capture';
|
||||
SET DEBUG_SYNC= 'processlist_callback_captured_sctx SIGNAL scanner_captured WAIT_FOR let_scanner_continue';
|
||||
|
||||
--echo # Filter by name: checks the value read through the captured pointer.
|
||||
--send SELECT user, host FROM information_schema.processlist WHERE user = 'definer'
|
||||
|
||||
connection waiter;
|
||||
SET DEBUG_SYNC= 'now WAIT_FOR scanner_captured';
|
||||
|
||||
--echo # Let the event finish and execute() return.
|
||||
SET DEBUG_SYNC= 'now SIGNAL event_may_finish WAIT_FOR event_execute_returned TIMEOUT 60';
|
||||
|
||||
--echo # Release the scanner to dereference the pointer it captured.
|
||||
SET DEBUG_SYNC= 'now SIGNAL let_scanner_continue';
|
||||
|
||||
--disconnect waiter
|
||||
--connection default
|
||||
--reap
|
||||
|
||||
SET SESSION debug_dbug= @saved_dbug;
|
||||
SET DEBUG_SYNC= 'RESET';
|
||||
|
||||
SET GLOBAL event_scheduler= OFF;
|
||||
--source include/check_events_off.inc
|
||||
let $count_sessions= 1;
|
||||
--source include/wait_until_count_sessions.inc
|
||||
DROP USER definer@localhost;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
@@ -1143,11 +1143,11 @@ connection con1;
|
||||
set time_zone= '+00:00';
|
||||
select row_start, row_end from t;
|
||||
row_start row_end
|
||||
2001-01-01 00:00:00.000000 2038-01-19 03:14:07.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 2038-01-19 03:14:07.999999 #
|
||||
1 10 x 2001-01-01 00:00:00.000000 # #
|
||||
handler t close;
|
||||
disconnect con1;
|
||||
connection default;
|
||||
|
||||
@@ -1179,9 +1179,10 @@ connection default;
|
||||
grant select (a) on db.t to u@localhost;
|
||||
connection con1;
|
||||
set time_zone= '+00:00';
|
||||
--replace_column 2 #
|
||||
select row_start, row_end from t;
|
||||
handler t open;
|
||||
--replace_column 6 #
|
||||
--replace_column 5 # 6 #
|
||||
handler t read first;
|
||||
handler t close;
|
||||
disconnect con1;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -104,3 +104,74 @@ BINLOG '-2079193929';
|
||||
ERROR HY000: Decoding of base64 string failed
|
||||
BINLOG 'xç↓%~∙D╒ƒ╡';
|
||||
ERROR HY000: Decoding of base64 string failed
|
||||
#
|
||||
# MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
|
||||
#
|
||||
create table t0 (a int);
|
||||
create table t1 (a int);
|
||||
## Case 1: Trigger (disabled)
|
||||
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
create trigger trig_t0_insert after insert on t0 for each row
|
||||
binlog @a, @b;
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 2: SP (disabled)
|
||||
create procedure proc_t1_insert() binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 3: SF (disabled)
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
return 0;
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog @a, @b;
|
||||
return 0;
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 4: Event (disabled)
|
||||
create event ev_t1_insert on schedule every 1 day do
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 5: Anonymous block (disabled)
|
||||
begin not atomic
|
||||
binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA==';
|
||||
end|
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
## Case 6: Dynamic SQL in SP (disabled)
|
||||
create procedure proc_t1_insert()
|
||||
execute immediate "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
call proc_t1_insert();
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
drop procedure proc_t1_insert;
|
||||
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
create procedure proc_t1_insert() execute stmt;
|
||||
call proc_t1_insert();
|
||||
ERROR 0A000: BINLOG is not allowed in stored procedures
|
||||
drop procedure proc_t1_insert;
|
||||
deallocate prepare stmt;
|
||||
## Case 7: Row event in PS (works)
|
||||
binlog 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC';
|
||||
prepare stmt from "binlog 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
a
|
||||
3
|
||||
3
|
||||
## Case 8: Statement event in PS (works)
|
||||
prepare stmt from "binlog 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
a
|
||||
3
|
||||
3
|
||||
99
|
||||
99
|
||||
drop table t0, t1;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -164,3 +164,101 @@ BINLOG '123';
|
||||
BINLOG '-2079193929';
|
||||
--error ER_BASE64_DECODE_ERROR
|
||||
BINLOG 'xç↓%~∙D╒ƒ╡';
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-39074 trans_rollback_stmt(THD *): Assertion `! thd->in_sub_stmt' failed.
|
||||
--echo #
|
||||
|
||||
create table t0 (a int);
|
||||
create table t1 (a int);
|
||||
|
||||
# Format_description_log_event, same as in the FD test above
|
||||
--let $FORMAT_DESCR_LOG_EVENT= 'ODdYRw8BAAAAZgAAAGoAAAABAAQANS4xLjIzLXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4N1hHEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC'
|
||||
|
||||
# table_map+write_rows_log_event corresponding to: insert into t1 values (3)
|
||||
--let $ROW_INSERT_T1= 'TFtYRxMBAAAAKQAAAH8BAAAAABAAAAAAAAAABHRlc3QAAnQxAAEDAAE=TFtYRxcBAAAAIgAAAKEBAAAQABAAAAAAAAEAAf/+AwAAAA=='
|
||||
|
||||
# Query_log_event (no checksum, matches the checksum-less FD event above)
|
||||
# corresponding to: insert into t1 values (99)
|
||||
--let $STMT_INSERT_T1= 'AAAAAAIBAAAAPwAAAAAAAAAAAAEAAAAAAAAABAAAAAB0ZXN0AGluc2VydCBpbnRvIHQxIHZhbHVlcyAoOTkp'
|
||||
|
||||
--echo ## Case 1: Trigger (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create trigger trig_t0_insert after insert on t0 for each row binlog 'enabled';
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create trigger trig_t0_insert after insert on t0 for each row
|
||||
binlog @a, @b;
|
||||
|
||||
--echo ## Case 2: SP (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create procedure proc_t1_insert() binlog $STMT_INSERT_T1;
|
||||
|
||||
--echo ## Case 3: SF (disabled)
|
||||
|
||||
delimiter |;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog $ROW_INSERT_T1;
|
||||
return 0;
|
||||
end|
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create function func_t1_insert() returns int
|
||||
begin
|
||||
binlog @a, @b;
|
||||
return 0;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
--echo ## Case 4: Event (disabled)
|
||||
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval create event ev_t1_insert on schedule every 1 day do
|
||||
binlog $ROW_INSERT_T1;
|
||||
|
||||
--echo ## Case 5: Anonymous block (disabled)
|
||||
|
||||
delimiter |;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
eval begin not atomic
|
||||
binlog $ROW_INSERT_T1;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
--echo ## Case 6: Dynamic SQL in SP (disabled)
|
||||
|
||||
eval create procedure proc_t1_insert()
|
||||
execute immediate "binlog $ROW_INSERT_T1";
|
||||
--error ER_SP_BADSTATEMENT
|
||||
call proc_t1_insert();
|
||||
drop procedure proc_t1_insert;
|
||||
|
||||
eval prepare stmt from "binlog $ROW_INSERT_T1";
|
||||
create procedure proc_t1_insert() execute stmt;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
call proc_t1_insert();
|
||||
drop procedure proc_t1_insert;
|
||||
deallocate prepare stmt;
|
||||
|
||||
--echo ## Case 7: Row event in PS (works)
|
||||
|
||||
eval binlog $FORMAT_DESCR_LOG_EVENT;
|
||||
eval prepare stmt from "binlog $ROW_INSERT_T1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
|
||||
--echo ## Case 8: Statement event in PS (works)
|
||||
|
||||
eval prepare stmt from "binlog $STMT_INSERT_T1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
select * from t1;
|
||||
|
||||
drop table t0, t1;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
connection node_2;
|
||||
connection node_1;
|
||||
# Correct Galera library found
|
||||
CALL mtr.add_suppression("Aborting");
|
||||
CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
|
||||
connection node_1;
|
||||
@@ -22,7 +23,10 @@ f1 f2
|
||||
2 b
|
||||
3 c
|
||||
connection node_2;
|
||||
Starting server ...
|
||||
Starting server with an unknown option ...
|
||||
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
|
||||
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
|
||||
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
|
||||
Starting server ...
|
||||
SELECT * FROM t1;
|
||||
f1 f2
|
||||
@@ -39,7 +43,10 @@ f1 f2
|
||||
2 d
|
||||
3 d
|
||||
connection node_2;
|
||||
Starting server ...
|
||||
Starting server with an unknown option ...
|
||||
FOUND 1 /unknown option '--galera-unknown-option'/ in galera_restart_on_unknown_option.err
|
||||
FOUND 1 /State transfer interrupted, shutting down gracefully/ in galera_restart_on_unknown_option.err
|
||||
NOT FOUND /Assertion .* failed/ in galera_restart_on_unknown_option.err
|
||||
Starting server ...
|
||||
call mtr.add_suppression("WSREP: Failed to scan the last segment to the end\\. Last events may be missing\\. Last recovered event: ");
|
||||
SELECT * FROM t1;
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
--source include/galera_cluster.inc
|
||||
--source include/have_innodb.inc
|
||||
|
||||
#
|
||||
# Galera library version with the graceful shutdown on an interrupted state
|
||||
# transfer (MDEV-31517), which this test expects
|
||||
#
|
||||
--let $galera_version=26.4.25
|
||||
source ../wsrep/include/check_galera_version.inc;
|
||||
|
||||
# Suppress expected warnings:
|
||||
|
||||
CALL mtr.add_suppression("Aborting");
|
||||
@@ -11,22 +18,6 @@ CALL mtr.add_suppression("unknown option '--galera-unknown-option'");
|
||||
--let $node_2=node_2
|
||||
--source include/auto_increment_offset_save.inc
|
||||
|
||||
#
|
||||
# We should count the number of "Assertion failed" warnings
|
||||
# in the log file before and after testing. To do this we need
|
||||
# to save original log file before testing:
|
||||
#
|
||||
--let TEST_LOG=$MYSQLTEST_VARDIR/log/mysqld.2.err
|
||||
--perl
|
||||
use strict;
|
||||
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
|
||||
my $test_log_copy=$test_log . '.copy';
|
||||
if (-e $test_log_copy) {
|
||||
unlink $test_log_copy;
|
||||
}
|
||||
EOF
|
||||
--copy_file $TEST_LOG $TEST_LOG.copy
|
||||
|
||||
--connection node_2
|
||||
|
||||
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 CHAR(1));
|
||||
@@ -59,14 +50,26 @@ SELECT * FROM t1;
|
||||
|
||||
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
|
||||
|
||||
--let $start_mysqld_params=--galera-unknown-option
|
||||
# The server must refuse to start, and exit without an assertion.
|
||||
# Start it directly rather than through the expect file: mtr would treat
|
||||
# a server that fails to start as a test failure.
|
||||
# The unknown option is found only after the state transfer has started,
|
||||
# which is then interrupted, and Galera aborts the process on purpose
|
||||
# (SIGABRT, exit code 134).
|
||||
|
||||
--echo Starting server ...
|
||||
--write_line "try:$start_mysqld_params" $_expect_file_name
|
||||
|
||||
# Sleep to ensure that server exited...
|
||||
|
||||
--sleep 30
|
||||
--echo Starting server with an unknown option ...
|
||||
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
|
||||
--error 134
|
||||
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
|
||||
--let SEARCH_FILE= $unknown_option_log
|
||||
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= Assertion .* failed
|
||||
--source include/search_pattern_in_file.inc
|
||||
--remove_file $unknown_option_log
|
||||
--remove_file $unknown_option_log.out
|
||||
|
||||
# Restart node 2 without unknown option:
|
||||
|
||||
@@ -104,14 +107,26 @@ SELECT * FROM t1;
|
||||
|
||||
--connection node_2
|
||||
|
||||
--let $start_mysqld_params=--galera-unknown-option
|
||||
# The server must refuse to start, and exit without an assertion.
|
||||
# Start it directly rather than through the expect file: mtr would treat
|
||||
# a server that fails to start as a test failure.
|
||||
# The unknown option is found only after the state transfer has started,
|
||||
# which is then interrupted, and Galera aborts the process on purpose
|
||||
# (SIGABRT, exit code 134).
|
||||
|
||||
--echo Starting server ...
|
||||
--write_line "try:$start_mysqld_params" $_expect_file_name
|
||||
|
||||
# Sleep to ensure that server exited...
|
||||
|
||||
--sleep 30
|
||||
--echo Starting server with an unknown option ...
|
||||
--let $unknown_option_log= $MYSQLTEST_VARDIR/tmp/galera_restart_on_unknown_option.err
|
||||
--error 134
|
||||
--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$unknown_option_log --galera-unknown-option > $unknown_option_log.out 2>&1
|
||||
--let SEARCH_FILE= $unknown_option_log
|
||||
--let SEARCH_PATTERN= unknown option '--galera-unknown-option'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= State transfer interrupted, shutting down gracefully
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= Assertion .* failed
|
||||
--source include/search_pattern_in_file.inc
|
||||
--remove_file $unknown_option_log
|
||||
--remove_file $unknown_option_log.out
|
||||
|
||||
# Restart node 2 without unknown option:
|
||||
|
||||
@@ -133,27 +148,4 @@ SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
--source include/auto_increment_offset_restore.inc
|
||||
|
||||
#
|
||||
# We should count the number of "Assertion failed" warnings
|
||||
# in the log file during test phase - to print the error message
|
||||
# if quantity of such warnings in log file increased at the end
|
||||
# of the test:
|
||||
#
|
||||
--perl
|
||||
use strict;
|
||||
my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set";
|
||||
my $test_log_copy=$test_log . '.copy';
|
||||
open(FILE, $test_log_copy) or die("Unable to open $test_log_copy: $!\n");
|
||||
my $initial=grep(/Assertion * failed/gi,<FILE>);
|
||||
close(FILE);
|
||||
open(FILE, $test_log) or die("Unable to open $test_log: $!\n");
|
||||
my $count_warnings=grep(/Assertion * failed/gi,<FILE>);
|
||||
close(FILE);
|
||||
if ($count_warnings != $initial) {
|
||||
my $diff=$count_warnings-$initial;
|
||||
print "Assertion failed $diff times.\n";
|
||||
}
|
||||
EOF
|
||||
--remove_file $TEST_LOG.copy
|
||||
|
||||
--source include/auto_increment_offset_restore.inc
|
||||
|
||||
122
mysql-test/suite/innodb/r/buf_pool_resize_pcur_restore.result
Normal file
122
mysql-test/suite/innodb/r/buf_pool_resize_pcur_restore.result
Normal file
@@ -0,0 +1,122 @@
|
||||
create table t1 (pk int primary key, val varchar(255) not null)
|
||||
engine=innodb stats_persistent=0 charset=latin1;
|
||||
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t2 like t1;
|
||||
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t3 (pk int primary key) engine=innodb stats_persistent=0
|
||||
partition by hash(pk) partitions 256;
|
||||
insert into t3 select seq from seq_0_to_255;
|
||||
create table t4 (a int primary key, b varchar(7000)) engine=innodb
|
||||
stats_persistent=0;
|
||||
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
|
||||
create function hold(x int) returns int
|
||||
begin
|
||||
if x = 1 then
|
||||
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
|
||||
end if;
|
||||
return 1;
|
||||
end|
|
||||
create procedure read_into_first_new_block(tbl varchar(64))
|
||||
begin
|
||||
declare attempts int default 5;
|
||||
declare blk bigint default -1;
|
||||
declare p int;
|
||||
set @read_tbl= concat('select count(*) into @n from ', tbl);
|
||||
prepare read_tbl from @read_tbl;
|
||||
while blk != @first_new do
|
||||
if attempts = 0 then
|
||||
signal sqlstate '45000' set message_text= 'Wrong placement';
|
||||
end if;
|
||||
set attempts= attempts - 1;
|
||||
if blk != -1 then
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
end if;
|
||||
# Evict all pages of t1, t2 and t3.
|
||||
select sum(length(b)) into @n from t4;
|
||||
if @first_new = 0 then
|
||||
set @first_new= (select pool_size
|
||||
from information_schema.innodb_buffer_pool_stats);
|
||||
end if;
|
||||
set global innodb_buffer_pool_size= @new_size;
|
||||
# A grow appends the new blocks to the end of the free list. Read pages
|
||||
# of t3 until only the new blocks are free.
|
||||
set p= 0;
|
||||
while (select free_buffers > pool_size - @first_new
|
||||
from information_schema.innodb_buffer_pool_stats) do
|
||||
if p = 256 then
|
||||
signal sqlstate '45000' set message_text= 'Out of partitions';
|
||||
end if;
|
||||
select pk into @n from t3 where pk= p;
|
||||
set p= p + 1;
|
||||
end while;
|
||||
execute read_tbl;
|
||||
select block_id into blk from information_schema.innodb_buffer_page
|
||||
where table_name = concat('`test`.`', tbl, '`');
|
||||
end while;
|
||||
deallocate prepare read_tbl;
|
||||
end|
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
set @old_size= @@global.innodb_buffer_pool_size;
|
||||
set @new_size= 12582912;
|
||||
set @first_new= 0;
|
||||
set @save_scan_depth= @@global.innodb_lru_scan_depth;
|
||||
set global innodb_lru_scan_depth= 100;
|
||||
#
|
||||
# A read that continues from a stale position
|
||||
#
|
||||
# Read the page of t1 into the first new block.
|
||||
call read_into_first_new_block('t1');
|
||||
connect con1,localhost,root;
|
||||
select group_concat(pk) from t1 where hold(pk);
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
# Reorganize the page while the read holds a stored position on pk=1.
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t1 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
# Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Read the page of t1 into the same block again.
|
||||
call read_into_first_new_block('t1');
|
||||
# Resume the read, which must return pk=2 to pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
group_concat(pk)
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
|
||||
connection default;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
#
|
||||
# An UPDATE that continues from a stale position
|
||||
#
|
||||
# Read the page of t2 into the first new block.
|
||||
call read_into_first_new_block('t2');
|
||||
connection con1;
|
||||
update t2 set val= 'b' where hold(pk);
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
# Reorganize the page while the UPDATE holds a stored position on pk=1.
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t2 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
# Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Read the page of t2 into the same block again.
|
||||
call read_into_first_new_block('t2');
|
||||
# Resume the UPDATE, which must change pk=1, not pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
affected rows: 21
|
||||
info: Rows matched: 21 Changed: 21 Warnings: 0
|
||||
disconnect con1;
|
||||
connection default;
|
||||
select group_concat(pk) from t2 where val = 'b';
|
||||
group_concat(pk)
|
||||
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
|
||||
set debug_sync= 'reset';
|
||||
drop procedure read_into_first_new_block;
|
||||
drop function hold;
|
||||
drop table t1, t2, t3, t4;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_lru_scan_depth= @save_scan_depth;
|
||||
@@ -0,0 +1,3 @@
|
||||
--innodb-buffer-pool-size=8M
|
||||
--innodb-buffer-pool-size-max=16M
|
||||
--innodb-read-ahead-threshold=0
|
||||
173
mysql-test/suite/innodb/t/buf_pool_resize_pcur_restore.test
Normal file
173
mysql-test/suite/innodb/t/buf_pool_resize_pcur_restore.test
Normal file
@@ -0,0 +1,173 @@
|
||||
#
|
||||
# A persistent cursor that is restored after the buffer pool shrank and
|
||||
# grew again
|
||||
#
|
||||
# btr_pcur_t::restore_position() accepts the stored position without a
|
||||
# search when the block descriptor that it remembers holds the same page
|
||||
# id and the same modify_clock. The descriptors are at fixed addresses,
|
||||
# and a grow initializes the descriptors that an earlier shrink withdrew.
|
||||
# If that reset modify_clock, a page that is read into such a descriptor
|
||||
# after the grow could have the modify_clock that a cursor stored before
|
||||
# the shrink, when the page had been read into the same descriptor after
|
||||
# an earlier grow. The page can have changed in between, and the cursor
|
||||
# would continue from a stale record pointer.
|
||||
#
|
||||
--source include/have_innodb.inc
|
||||
--source include/have_innodb_16k.inc
|
||||
--source include/have_partition.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/have_debug_sync.inc
|
||||
--source include/not_embedded.inc
|
||||
--source include/count_sessions.inc
|
||||
|
||||
# One leaf page each, with the records in descending key order in the page.
|
||||
# A reorganization writes them in key order, so pk=20 moves to the offset
|
||||
# of pk=1.
|
||||
create table t1 (pk int primary key, val varchar(255) not null)
|
||||
engine=innodb stats_persistent=0 charset=latin1;
|
||||
insert into t1 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
create table t2 like t1;
|
||||
insert into t2 select seq, repeat('a', 100) from seq_1_to_20 order by seq desc;
|
||||
|
||||
# One page per partition. A lookup of an evicted partition reads one page.
|
||||
# The page cleaner keeps about innodb_lru_scan_depth blocks free, and the
|
||||
# partitions must outnumber them.
|
||||
create table t3 (pk int primary key) engine=innodb stats_persistent=0
|
||||
partition by hash(pk) partitions 256;
|
||||
insert into t3 select seq from seq_0_to_255;
|
||||
|
||||
# Three times innodb_buffer_pool_size, to evict everything else.
|
||||
create table t4 (a int primary key, b varchar(7000)) engine=innodb
|
||||
stats_persistent=0;
|
||||
insert into t4 select seq, repeat('x', 7000) from seq_1_to_3000;
|
||||
|
||||
delimiter |;
|
||||
# Stop the statement after it read pk=1 and stored its position.
|
||||
create function hold(x int) returns int
|
||||
begin
|
||||
if x = 1 then
|
||||
set debug_sync= 'now SIGNAL paused WAIT_FOR go';
|
||||
end if;
|
||||
return 1;
|
||||
end|
|
||||
|
||||
# Grow the buffer pool from @old_size to @new_size and read the only page
|
||||
# of the table tbl into the first block that the grow adds. A concurrent
|
||||
# page read can take that block; then shrink and try again. The first call
|
||||
# sets @first_new, the block id of the first new block. Later calls require
|
||||
# the same @first_new.
|
||||
create procedure read_into_first_new_block(tbl varchar(64))
|
||||
begin
|
||||
declare attempts int default 5;
|
||||
declare blk bigint default -1;
|
||||
declare p int;
|
||||
set @read_tbl= concat('select count(*) into @n from ', tbl);
|
||||
prepare read_tbl from @read_tbl;
|
||||
while blk != @first_new do
|
||||
if attempts = 0 then
|
||||
signal sqlstate '45000' set message_text= 'Wrong placement';
|
||||
end if;
|
||||
set attempts= attempts - 1;
|
||||
if blk != -1 then
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
end if;
|
||||
# Evict all pages of t1, t2 and t3.
|
||||
select sum(length(b)) into @n from t4;
|
||||
if @first_new = 0 then
|
||||
set @first_new= (select pool_size
|
||||
from information_schema.innodb_buffer_pool_stats);
|
||||
end if;
|
||||
set global innodb_buffer_pool_size= @new_size;
|
||||
# A grow appends the new blocks to the end of the free list. Read pages
|
||||
# of t3 until only the new blocks are free.
|
||||
set p= 0;
|
||||
while (select free_buffers > pool_size - @first_new
|
||||
from information_schema.innodb_buffer_pool_stats) do
|
||||
if p = 256 then
|
||||
signal sqlstate '45000' set message_text= 'Out of partitions';
|
||||
end if;
|
||||
select pk into @n from t3 where pk= p;
|
||||
set p= p + 1;
|
||||
end while;
|
||||
execute read_tbl;
|
||||
select block_id into blk from information_schema.innodb_buffer_page
|
||||
where table_name = concat('`test`.`', tbl, '`');
|
||||
end while;
|
||||
deallocate prepare read_tbl;
|
||||
end|
|
||||
delimiter ;|
|
||||
|
||||
# Purge resets DB_TRX_ID in the inserted records. Let it finish, so that it
|
||||
# reads no pages into the free blocks that the procedure counts.
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
|
||||
set @old_size= @@global.innodb_buffer_pool_size;
|
||||
set @new_size= 12582912;
|
||||
set @first_new= 0;
|
||||
# The minimum, which keeps fewer blocks free than t3 has partitions.
|
||||
set @save_scan_depth= @@global.innodb_lru_scan_depth;
|
||||
set global innodb_lru_scan_depth= 100;
|
||||
|
||||
--echo #
|
||||
--echo # A read that continues from a stale position
|
||||
--echo #
|
||||
--echo # Read the page of t1 into the first new block.
|
||||
call read_into_first_new_block('t1');
|
||||
connect (con1,localhost,root);
|
||||
--send select group_concat(pk) from t1 where hold(pk)
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
--echo # Reorganize the page while the read holds a stored position on pk=1.
|
||||
set @save_dbug= @@debug_dbug;
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t1 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
--echo # Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
--echo # Read the page of t1 into the same block again.
|
||||
call read_into_first_new_block('t1');
|
||||
--echo # Resume the read, which must return pk=2 to pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
--reap
|
||||
connection default;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
# Purge was blocked by the read view of the paused read. Let it finish, so
|
||||
# that it reads no pages into the new blocks of the next case.
|
||||
set global innodb_max_purge_lag_wait= 0;
|
||||
|
||||
--echo #
|
||||
--echo # An UPDATE that continues from a stale position
|
||||
--echo #
|
||||
--echo # Read the page of t2 into the first new block.
|
||||
call read_into_first_new_block('t2');
|
||||
connection con1;
|
||||
--send update t2 set val= 'b' where hold(pk)
|
||||
connection default;
|
||||
set debug_sync= 'now WAIT_FOR paused';
|
||||
--echo # Reorganize the page while the UPDATE holds a stored position on pk=1.
|
||||
set debug_dbug= '+d,do_page_reorganize';
|
||||
insert into t2 values (21, repeat('a', 100));
|
||||
set debug_dbug= @save_dbug;
|
||||
--echo # Shrink, which moves the page out of the block.
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
--echo # Read the page of t2 into the same block again.
|
||||
call read_into_first_new_block('t2');
|
||||
--echo # Resume the UPDATE, which must change pk=1, not pk=20.
|
||||
set debug_sync= 'now SIGNAL go';
|
||||
connection con1;
|
||||
--enable_info
|
||||
--reap
|
||||
--disable_info
|
||||
disconnect con1;
|
||||
connection default;
|
||||
select group_concat(pk) from t2 where val = 'b';
|
||||
|
||||
set debug_sync= 'reset';
|
||||
drop procedure read_into_first_new_block;
|
||||
drop function hold;
|
||||
drop table t1, t2, t3, t4;
|
||||
set global innodb_buffer_pool_size= @old_size;
|
||||
set global innodb_lru_scan_depth= @save_scan_depth;
|
||||
--source include/wait_until_count_sessions.inc
|
||||
@@ -36,7 +36,9 @@ send SET GLOBAL innodb_buffer_pool_size=8388608;
|
||||
connection default;
|
||||
SET DEBUG_SYNC='now WAIT_FOR blocked';
|
||||
# IA-32: 504 blocks; IA-32 glibc SUX_LOCK_GENERIC: 500; macOS SUX_LOCK_GENERIC: 498
|
||||
--replace_regex /([45]..)\/\1/505\/505/
|
||||
# Other threads may already have withdrawn some blocks before
|
||||
# buf_pool_shrink_before_wakeup.
|
||||
--replace_regex /\([0-9]+\/[45][0-9][0-9]\)/(505\/505)/
|
||||
SHOW STATUS LIKE 'innodb_buffer_pool_resize_status';
|
||||
SET DEBUG_SYNC='now SIGNAL go';
|
||||
connection con1;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -3749,20 +3749,22 @@ 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 2038-01-19 03:14:07.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 2038-01-19 03:14:07.999999
|
||||
2 CURRENT
|
||||
set timestamp= default;
|
||||
set time_zone= default;
|
||||
drop table t1;
|
||||
|
||||
@@ -2989,12 +2989,14 @@ 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;
|
||||
|
||||
@@ -28,7 +28,6 @@ SET(MYSYS_SSL_HIDDEN_SOURCES
|
||||
my_sha384.cc
|
||||
my_sha512.cc
|
||||
my_md5.cc
|
||||
openssl.c
|
||||
)
|
||||
|
||||
SET(MYSYS_SSL_SOURCES
|
||||
|
||||
@@ -27,29 +27,18 @@
|
||||
|
||||
#include <my_crypt.h>
|
||||
#include <ssl_compat.h>
|
||||
#include <cstdint>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -57,6 +46,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;
|
||||
|
||||
@@ -115,9 +106,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. */
|
||||
@@ -213,10 +206,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
|
||||
@@ -298,20 +293,30 @@ int my_aes_crypt_init(void *ctx, enum my_aes_mode mode, int flags,
|
||||
new (ctx) MyCTX_nopad();
|
||||
else
|
||||
new (ctx) MyCTX();
|
||||
return ((MyCTX*)ctx)->init(ciphers[mode](klen), flags & 1,
|
||||
key, klen, iv, ivlen);
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
int res= c->init(ciphers[mode](klen), flags & 1, key, klen, iv, ivlen);
|
||||
if (res)
|
||||
{
|
||||
/* release ctx but keep the object: a later update()/finish() from a
|
||||
caller that ignored the error is then a safe no-op, not a double free */
|
||||
EVP_CIPHER_CTX_free(c->ctx);
|
||||
c->ctx= NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int my_aes_crypt_update(void *ctx, const uchar *src, uint slen,
|
||||
uchar *dst, uint *dlen)
|
||||
{
|
||||
return ((MyCTX*)ctx)->update(src, slen, dst, dlen);
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
return c->ctx ? c->update(src, slen, dst, dlen) : MY_AES_OPENSSL_ERROR;
|
||||
}
|
||||
|
||||
int my_aes_crypt_finish(void *ctx, uchar *dst, uint *dlen)
|
||||
{
|
||||
int res= ((MyCTX*)ctx)->finish(dst, dlen);
|
||||
((MyCTX*)ctx)->~MyCTX();
|
||||
MyCTX *c= (MyCTX*)ctx;
|
||||
int res= c->ctx ? c->finish(dst, dlen) : MY_AES_OPENSSL_ERROR;
|
||||
c->~MyCTX();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,70 +19,60 @@
|
||||
@file
|
||||
|
||||
@brief
|
||||
Wrapper functions for OpenSSL and YaSSL. Also provides a Compatibility layer
|
||||
to make available YaSSL's MD5 implementation.
|
||||
Wrapper functions for the OpenSSL/WolfSSL MD5 implementation.
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_md5.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined(HAVE_WOLFSSL)
|
||||
#include <wolfssl/wolfcrypt/md5.h>
|
||||
#include <ssl_compat.h>
|
||||
typedef wc_Md5 EVP_MD_CTX;
|
||||
static void md5_init(EVP_MD_CTX *context)
|
||||
{
|
||||
wc_InitMd5(context);;
|
||||
}
|
||||
|
||||
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
wc_Md5Update(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
wc_Md5Final(context,digest);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
#if defined(HAVE_OPENSSL10) && !defined(HAVE_WOLFSSL) && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER)
|
||||
/*
|
||||
OpenSSL 1.0.x in FIPS mode aborts on the low-level MD5 API (md5 is not used
|
||||
for crypto here), so go through EVP with the non-FIPS flag. MDEV-7788.
|
||||
*/
|
||||
#include <openssl/evp.h>
|
||||
#include <ssl_compat.h>
|
||||
|
||||
static void md5_init(EVP_MD_CTX *context)
|
||||
typedef EVP_MD_CTX MY_MD5_CTX;
|
||||
static void md5_init(MY_MD5_CTX *context)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
EVP_MD *md5;
|
||||
EVP_MD_CTX_init(context);
|
||||
/* Ok to ignore FIPS: MD5 is not used for crypto here */
|
||||
/* In OpenSSL 3.0.0+ it is a different EVP_MD provider */
|
||||
md5 = EVP_MD_fetch(NULL, "MD5", "fips=no");
|
||||
EVP_DigestInit_ex(context, md5, NULL);
|
||||
EVP_MD_free(md5);
|
||||
#else
|
||||
EVP_MD_CTX_init(context);
|
||||
#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
|
||||
/* Ok to ignore FIPS: MD5 is not used for crypto here */
|
||||
/* In OpenSSL 1.1.1 the non FIPS allowed flag is context specific */
|
||||
EVP_MD_CTX_set_flags(context, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
|
||||
#endif
|
||||
EVP_DigestInit_ex(context, EVP_md5(), NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void md5_input(EVP_MD_CTX *context, const uchar *buf, unsigned len)
|
||||
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
EVP_DigestUpdate(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
EVP_DigestFinal_ex(context, digest, NULL);
|
||||
EVP_MD_CTX_reset(context);
|
||||
EVP_MD_CTX_cleanup(context);
|
||||
}
|
||||
|
||||
#endif /* HAVE_WOLFSSL */
|
||||
#else
|
||||
#include <openssl/md5.h>
|
||||
typedef MD5_CTX MY_MD5_CTX;
|
||||
static void md5_init(MY_MD5_CTX *context)
|
||||
{
|
||||
MD5_Init(context);
|
||||
}
|
||||
|
||||
static void md5_input(MY_MD5_CTX *context, const uchar *buf, unsigned len)
|
||||
{
|
||||
MD5_Update(context, buf, len);
|
||||
}
|
||||
|
||||
static void md5_result(MY_MD5_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
{
|
||||
MD5_Final(digest, context);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Wrapper function to compute MD5 message digest.
|
||||
@@ -95,11 +85,10 @@ static void md5_result(EVP_MD_CTX *context, uchar digest[MD5_HASH_SIZE])
|
||||
*/
|
||||
void my_md5(uchar *digest, const char *buf, size_t len)
|
||||
{
|
||||
char ctx_buf[EVP_MD_CTX_SIZE];
|
||||
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
|
||||
md5_init(ctx);
|
||||
md5_input(ctx, (const uchar *)buf, (uint) len);
|
||||
md5_result(ctx, digest);
|
||||
MY_MD5_CTX context;
|
||||
md5_init(&context);
|
||||
md5_input(&context, (const uchar *)buf, (uint) len);
|
||||
md5_result(&context, digest);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,34 +108,33 @@ void my_md5_multi(uchar *digest, ...)
|
||||
{
|
||||
va_list args;
|
||||
const uchar *str;
|
||||
char ctx_buf[EVP_MD_CTX_SIZE];
|
||||
EVP_MD_CTX * const ctx= (EVP_MD_CTX*)ctx_buf;
|
||||
MY_MD5_CTX context;
|
||||
va_start(args, digest);
|
||||
|
||||
md5_init(ctx);
|
||||
md5_init(&context);
|
||||
for (str= va_arg(args, const uchar*); str; str= va_arg(args, const uchar*))
|
||||
md5_input(ctx, str, (uint) va_arg(args, size_t));
|
||||
md5_input(&context, str, (uint) va_arg(args, size_t));
|
||||
|
||||
md5_result(ctx, digest);
|
||||
md5_result(&context, digest);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
size_t my_md5_context_size()
|
||||
{
|
||||
return EVP_MD_CTX_SIZE;
|
||||
return sizeof(MY_MD5_CTX);
|
||||
}
|
||||
|
||||
void my_md5_init(void *context)
|
||||
{
|
||||
md5_init((EVP_MD_CTX *)context);
|
||||
md5_init((MY_MD5_CTX *)context);
|
||||
}
|
||||
|
||||
void my_md5_input(void *context, const uchar *buf, size_t len)
|
||||
{
|
||||
md5_input((EVP_MD_CTX *)context, buf, (uint) len);
|
||||
md5_input((MY_MD5_CTX *)context, buf, (uint) len);
|
||||
}
|
||||
|
||||
void my_md5_result(void *context, uchar *digest)
|
||||
{
|
||||
md5_result((EVP_MD_CTX *)context, digest);
|
||||
md5_result((MY_MD5_CTX *)context, digest);
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2017, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include <my_global.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <ssl_compat.h>
|
||||
|
||||
/*
|
||||
The check is only done for OpenSSL 1.1.x.
|
||||
It could run for OpenSSL 1.0.x but it doesn't make much sense
|
||||
and it hits this bug:
|
||||
https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1594748
|
||||
*/
|
||||
|
||||
#ifndef HAVE_OPENSSL11
|
||||
int check_openssl_compatibility()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#include <openssl/evp.h>
|
||||
|
||||
static uint testing;
|
||||
static size_t alloc_size, alloc_count;
|
||||
|
||||
static void *coc_malloc(size_t size
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *f __attribute__((unused)),
|
||||
int l __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (unlikely(testing))
|
||||
{
|
||||
alloc_size+= size;
|
||||
alloc_count++;
|
||||
}
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void *coc_realloc(void *addr, size_t num
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return realloc(addr, num);
|
||||
}
|
||||
|
||||
static void coc_free(void *addr
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
, const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
free(addr);
|
||||
}
|
||||
|
||||
int check_openssl_compatibility()
|
||||
{
|
||||
EVP_CIPHER_CTX *evp_ctx;
|
||||
EVP_MD_CTX *md5_ctx;
|
||||
|
||||
if (!CRYPTO_set_mem_functions(coc_malloc, coc_realloc, coc_free))
|
||||
return 0;
|
||||
|
||||
testing= 1;
|
||||
alloc_size= alloc_count= 0;
|
||||
evp_ctx= EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_free(evp_ctx);
|
||||
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_CIPHER_CTX_SIZE)
|
||||
return 1;
|
||||
|
||||
alloc_size= alloc_count= 0;
|
||||
md5_ctx= EVP_MD_CTX_new();
|
||||
EVP_MD_CTX_free(md5_ctx);
|
||||
if (alloc_count != 1 || !alloc_size || alloc_size > EVP_MD_CTX_SIZE)
|
||||
return 1;
|
||||
|
||||
testing= 0;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1358,7 +1358,7 @@ Event_job_data::execute(THD *thd, bool drop)
|
||||
{
|
||||
String sp_sql;
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
Security_context event_sctx, *save_sctx= NULL;
|
||||
Security_context *save_sctx= NULL;
|
||||
#endif
|
||||
List<Item> empty_item_list;
|
||||
bool ret= TRUE;
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
|
||||
#include "event_parse_data.h"
|
||||
#include "thr_lock.h" /* thr_lock_type */
|
||||
#include "sql_class.h" /* Security_context */
|
||||
|
||||
class Field;
|
||||
class THD;
|
||||
class Time_zone;
|
||||
struct TABLE;
|
||||
|
||||
@@ -173,6 +173,17 @@ public:
|
||||
|
||||
class Stored_program_creation_ctx *creation_ctx;
|
||||
|
||||
/*
|
||||
DEFINER security context, active in thd->security_ctx during execute().
|
||||
Kept here, not in execute(), to outlive it: a concurrent PROCESSLIST
|
||||
scan may still read it. See Event_worker_thread::run().
|
||||
|
||||
acl_getroot() aliases user/host/ip to definer_user/definer_host
|
||||
instead of copying them, so they must live in this object.
|
||||
Never call event_sctx.destroy(): it would free strings not owned here.
|
||||
*/
|
||||
Security_context event_sctx;
|
||||
|
||||
Event_job_data();
|
||||
|
||||
virtual bool
|
||||
|
||||
@@ -317,6 +317,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)
|
||||
|
||||
@@ -4027,14 +4027,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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -3357,6 +3357,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;
|
||||
|
||||
|
||||
@@ -8378,6 +8378,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;
|
||||
@@ -8386,6 +8388,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;
|
||||
|
||||
@@ -396,3 +396,56 @@ CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?
|
||||
</line>
|
||||
</t1>
|
||||
|
||||
#
|
||||
# MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
|
||||
# after insert into CONNECT table
|
||||
#
|
||||
CREATE TABLE t1 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t2 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
INSERT INTO t2 VALUES (2);
|
||||
ERROR HY000: Got error 174 'Missing XMLDB' from CONNECT
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t3 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
CREATE TABLE t4 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=XMLDB,xmlsup=libxml2';
|
||||
CREATE TABLE t5 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=/XMLDB,xmlsup=libxml2';
|
||||
INSERT INTO t3 VALUES (3);
|
||||
INSERT INTO t4 VALUES (4);
|
||||
INSERT INTO t5 VALUES (5);
|
||||
DROP TABLE t3, t4, t5;
|
||||
SET @a=LOAD_FILE('MYSQLD_DATADIR/test/y.xml');
|
||||
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
|
||||
y <?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created by the MariaDB CONNECT Storage Engine-->
|
||||
<XMLDB>
|
||||
<t3>
|
||||
<t3>
|
||||
<id>3</id>
|
||||
</t3>
|
||||
</t3>
|
||||
<t4>
|
||||
<t4>
|
||||
<id>4</id>
|
||||
</t4>
|
||||
</t4>
|
||||
<t5>
|
||||
<t5>
|
||||
<id>5</id>
|
||||
</t5>
|
||||
</t5>
|
||||
</XMLDB>
|
||||
|
||||
#
|
||||
# End of 10.11 tests
|
||||
#
|
||||
|
||||
@@ -318,3 +318,52 @@ SELECT CAST(@a AS CHAR CHARACTER SET latin1);
|
||||
--remove_file $MYSQLD_DATADIR/test/xsample.xml
|
||||
--remove_file $MYSQLD_DATADIR/test/latin1.xml
|
||||
--remove_file $MYSQLD_DATADIR/test/cp1251.xml
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41405 ASAN stack-buffer-overflow TDBXML::Initialize/TDBXML::OpenDB
|
||||
--echo # after insert into CONNECT table
|
||||
--echo #
|
||||
# Create x.xml so that the inserts below go into an existing file
|
||||
CREATE TABLE t1 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=root89,xmlsup=libxml2';
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t1;
|
||||
|
||||
let $xmldb= `SELECT REPEAT('A', 240)`;
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t2 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='x.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_regex /A{60,}/XMLDB/
|
||||
--error ER_GET_ERRMSG
|
||||
INSERT INTO t2 VALUES (2);
|
||||
DROP TABLE t2;
|
||||
--remove_file $MYSQLD_DATADIR/test/x.xml
|
||||
|
||||
# Long XmlDB node created by t3 must be found by t4 and t5
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t3 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t4 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=$xmldb,xmlsup=libxml2';
|
||||
--replace_result $xmldb XMLDB
|
||||
eval CREATE TABLE t5 (id INT)
|
||||
ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='y.xml'
|
||||
OPTION_LIST='XmlDB=/$xmldb,xmlsup=libxml2';
|
||||
INSERT INTO t3 VALUES (3);
|
||||
INSERT INTO t4 VALUES (4);
|
||||
INSERT INTO t5 VALUES (5);
|
||||
DROP TABLE t3, t4, t5;
|
||||
--chmod 0777 $MYSQLD_DATADIR/test/y.xml
|
||||
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
|
||||
--eval SET @a=LOAD_FILE('$MYSQLD_DATADIR/test/y.xml')
|
||||
SELECT REPLACE(@a, REPEAT('A', 240), 'XMLDB') AS y;
|
||||
--remove_file $MYSQLD_DATADIR/test/y.xml
|
||||
|
||||
--echo #
|
||||
--echo # End of 10.11 tests
|
||||
--echo #
|
||||
|
||||
@@ -819,6 +819,23 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename)
|
||||
return rc;
|
||||
} // end of LoadTableFile
|
||||
|
||||
/***********************************************************************/
|
||||
/* Return name as an Xpath. If name is not an Xpath, construct one */
|
||||
/* that will find it anywhere. */
|
||||
/***********************************************************************/
|
||||
static char *MakeXpath(PGLOBAL g, PCSZ name)
|
||||
{
|
||||
size_t len = strlen(name) + 3;
|
||||
char *xpath = (char*)PlugSubAlloc(g, NULL, len);
|
||||
|
||||
if (!strchr(name, '/'))
|
||||
snprintf(xpath, len, "//%s", name);
|
||||
else
|
||||
snprintf(xpath, len, "%s", name);
|
||||
|
||||
return xpath;
|
||||
} // end of MakeXpath
|
||||
|
||||
/***********************************************************************/
|
||||
/* Initialize the processing of the XML file. */
|
||||
/* Note: this function can be called several times, eventally before */
|
||||
@@ -850,7 +867,7 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
#else
|
||||
if (!Root) {
|
||||
#endif
|
||||
char tabpath[64], filename[_MAX_PATH];
|
||||
char filename[_MAX_PATH];
|
||||
|
||||
// We used the file name relative to recorded datapath
|
||||
PlugSetPath(filename, Xfile, GetPath());
|
||||
@@ -866,15 +883,8 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
goto error;
|
||||
} // endif Root
|
||||
|
||||
// If tabname is not an Xpath,
|
||||
// construct one that will find it anywhere
|
||||
if (!strchr(Tabname, '/'))
|
||||
snprintf(tabpath, sizeof(tabpath), "//%s", Tabname);
|
||||
else
|
||||
snprintf(tabpath, sizeof(tabpath), "%s", Tabname);
|
||||
|
||||
// Evaluate table xpath
|
||||
if ((TabNode = Root->SelectSingleNode(g, tabpath))) {
|
||||
if ((TabNode = Root->SelectSingleNode(g, MakeXpath(g, Tabname)))) {
|
||||
if (TabNode->GetType() != XML_ELEMENT_NODE) {
|
||||
snprintf(g->Message, sizeof(g->Message), MSG(BAD_NODE_TYPE), TabNode->GetType());
|
||||
goto error;
|
||||
@@ -882,15 +892,7 @@ bool TDBXML::Initialize(PGLOBAL g)
|
||||
|
||||
} else if (Mode == MODE_INSERT && XmlDB) {
|
||||
// We are adding a new table to a multi-table file
|
||||
|
||||
// If XmlDB is not an Xpath,
|
||||
// construct one that will find it anywhere
|
||||
if (!strchr(XmlDB, '/'))
|
||||
strcat(strcpy(tabpath, "//"), XmlDB);
|
||||
else
|
||||
strcpy(tabpath, XmlDB);
|
||||
|
||||
if (!(DBnode = Root->SelectSingleNode(g, tabpath))) {
|
||||
if (!(DBnode = Root->SelectSingleNode(g, MakeXpath(g, XmlDB)))) {
|
||||
// DB node does not exist yet; we cannot create it
|
||||
// because we don't know where it should be placed
|
||||
snprintf(g->Message, sizeof(g->Message), MSG(MISSING_NODE), XmlDB, Xfile);
|
||||
|
||||
@@ -1673,12 +1673,15 @@ ATTRIBUTE_COLD buf_pool_t::shrink_status buf_pool_t::shrink(size_t size)
|
||||
{
|
||||
withdraw_done:
|
||||
first_to_withdraw= nullptr;
|
||||
uint64_t base{0};
|
||||
while (buf_page_t *b= UT_LIST_GET_FIRST(withdrawn))
|
||||
{
|
||||
UT_LIST_REMOVE(withdrawn, b);
|
||||
base= std::max(base, reinterpret_cast<buf_block_t*>(b)->modify_clock);
|
||||
/* satisfy the check in lazy_allocate() */
|
||||
ut_d(memset((void*) b, 0, sizeof(buf_block_t)));
|
||||
}
|
||||
withdrawn_modify_clock= std::max(withdrawn_modify_clock, base);
|
||||
return SHRINK_DONE;
|
||||
}
|
||||
|
||||
@@ -1988,6 +1991,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
|
||||
const size_t ssize= srv_page_size_shift - UNIV_PAGE_SIZE_SHIFT_MIN;
|
||||
const size_t pages= pages_in_extent[ssize];
|
||||
const size_t first_extent= n_blocks / pages;
|
||||
const uint64_t modify_clock= withdrawn_modify_clock + 1;
|
||||
|
||||
char *extent= memory + first_extent * innodb_buffer_pool_extent_size;
|
||||
|
||||
@@ -2009,6 +2013,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
|
||||
{
|
||||
block->page.frame= frame;
|
||||
block->page.lock.init();
|
||||
block->modify_clock= modify_clock;
|
||||
UT_LIST_ADD_LAST(free, &block->page);
|
||||
ut_d(block->page.in_free_list= true);
|
||||
}
|
||||
@@ -2032,6 +2037,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept
|
||||
{
|
||||
block->page.frame= frame;
|
||||
block->page.lock.init();
|
||||
block->modify_clock= modify_clock;
|
||||
UT_LIST_ADD_LAST(free, &block->page);
|
||||
ut_d(block->page.in_free_list= true);
|
||||
}
|
||||
|
||||
@@ -1569,6 +1569,13 @@ public:
|
||||
|
||||
/** number of index page splits */
|
||||
Atomic_counter<ulint> pages_split;
|
||||
/** the maximum modify_clock of any block that shrink() withdrew;
|
||||
resize() initializes the modify_clock of the blocks that it adds to
|
||||
one more than this, so that the modify_clock at a block descriptor
|
||||
address never repeats an earlier value and a stale
|
||||
btr_pcur_t::restore_position() cannot succeed optimistically;
|
||||
protected by mutex */
|
||||
uint64_t withdrawn_modify_clock;
|
||||
|
||||
/** @name Page flushing algorithm fields */
|
||||
/* @{ */
|
||||
|
||||
@@ -246,6 +246,8 @@ search:
|
||||
pfs->m_schema_name_length= schema_name_length;
|
||||
pfs->m_enabled= is_enabled;
|
||||
pfs->m_timed= is_timed;
|
||||
pfs->m_stmt_stat.reset();
|
||||
pfs->m_sp_stat.reset();
|
||||
|
||||
/* Insert this record. */
|
||||
pfs->m_lock.dirty_to_allocated(& dirty_state);
|
||||
|
||||
@@ -20576,6 +20576,22 @@ typedef struct {
|
||||
} v2_proxy_header;
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
/*
|
||||
Zero the header, and set the part that is common to all PROXY v2 headers:
|
||||
the signature, version 2 with the command (0x1 PROXY, 0x0 LOCAL), the
|
||||
address family and protocol, and the length of the address block.
|
||||
*/
|
||||
static void init_v2_header(v2_proxy_header *header, int command, int fam,
|
||||
size_t len)
|
||||
{
|
||||
memset(header, 0, sizeof(*header));
|
||||
memcpy(header->sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A",
|
||||
sizeof(header->sig));
|
||||
header->ver_cmd= (0x2 << 4) | command;
|
||||
header->fam= fam;
|
||||
header->len= htons(len);
|
||||
}
|
||||
|
||||
static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
{
|
||||
int rc;
|
||||
@@ -20584,26 +20600,22 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
char query[256];
|
||||
char text_header[256], bad_text_header[256];
|
||||
char addr_bin[16];
|
||||
v2_proxy_header v2_header, bad_v2_header;
|
||||
void *header_data[4];
|
||||
size_t header_lengths[4];
|
||||
v2_proxy_header v2_header, bad_v2_header, short_v2_header;
|
||||
void *header_data[5];
|
||||
size_t header_lengths[5];
|
||||
size_t i;
|
||||
|
||||
// normalize IPv4-mapped IPv6 addresses, e.g ::ffff:127.0.0.2 to 127.0.0.2
|
||||
const char *normalized_addr= strncmp(ipaddr, "::ffff:", 7)?ipaddr : ipaddr + 7;
|
||||
myheader("test_proxy_header_tcp");
|
||||
|
||||
memset(&v2_header, 0, sizeof(v2_header));
|
||||
snprintf(text_header, sizeof(text_header), "PROXY %s %s %s %d 3306\r\n",family == AF_INET?"TCP4":"TCP6", ipaddr, ipaddr, port);
|
||||
|
||||
|
||||
inet_pton(family,ipaddr,addr_bin);
|
||||
|
||||
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
|
||||
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
|
||||
if(family == AF_INET)
|
||||
{
|
||||
v2_header.fam= 0x11;
|
||||
v2_header.len= htons(12);
|
||||
init_v2_header(&v2_header, 0x1, 0x11, sizeof(v2_header.addr.ip4));
|
||||
v2_header.addr.ip4.src_port= htons(port);
|
||||
v2_header.addr.ip4.dst_port= htons(3306);
|
||||
memcpy(&v2_header.addr.ip4.src_addr,addr_bin, sizeof (v2_header.addr.ip4.src_addr));
|
||||
@@ -20611,8 +20623,7 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
}
|
||||
else
|
||||
{
|
||||
v2_header.fam= 0x21;
|
||||
v2_header.len= htons(36);
|
||||
init_v2_header(&v2_header, 0x1, 0x21, sizeof(v2_header.addr.ip6));
|
||||
v2_header.addr.ip6.src_port= htons(port);
|
||||
v2_header.addr.ip6.dst_port= htons(3306);
|
||||
memcpy(v2_header.addr.ip6.src_addr,addr_bin, sizeof (v2_header.addr.ip6.src_addr));
|
||||
@@ -20627,11 +20638,14 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
header_data[1]= &v2_header;
|
||||
header_data[2]= bad_text_header;
|
||||
header_data[3]= &bad_v2_header;
|
||||
header_data[4]= &short_v2_header;
|
||||
|
||||
header_lengths[0]= strlen(text_header);
|
||||
header_lengths[1]= family == AF_INET ? 28 : 52;
|
||||
header_lengths[2]= sizeof(text_header)-4;
|
||||
header_lengths[3]= header_lengths[1];
|
||||
/* MDEV-41508: address block ends before the source port's low byte */
|
||||
header_lengths[4]= family == AF_INET ? 25 : 49;
|
||||
|
||||
memset(bad_text_header, ' ', sizeof(bad_text_header));
|
||||
memcpy(bad_text_header, text_header, header_lengths[1]-6);
|
||||
@@ -20639,6 +20653,9 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
bad_v2_header= v2_header;
|
||||
bad_v2_header.len= 0;
|
||||
|
||||
short_v2_header= v2_header;
|
||||
short_v2_header.len= htons(header_lengths[4] - 16);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
MYSQL *m;
|
||||
@@ -20679,8 +20696,7 @@ static void test_proxy_header_tcp(const char *ipaddr, int port)
|
||||
DIE(0);
|
||||
printf("pass %zu error %i - %s\n", i, mysql_errno(m),
|
||||
mysql_error(m));
|
||||
DIE_IF(i == 2 && mysql_errno(m) != ER_UNKNOWN_ERROR);
|
||||
DIE_IF(i == 3 && mysql_errno(m) != ER_UNKNOWN_ERROR);
|
||||
DIE_IF(mysql_errno(m) != ER_UNKNOWN_ERROR);
|
||||
mysql_close(m);
|
||||
}
|
||||
snprintf(query, sizeof(query), "DROP USER 'u'@'%s'",normalized_addr);
|
||||
@@ -20701,11 +20717,7 @@ static void test_proxy_header_localhost()
|
||||
int rc;
|
||||
myheader("test_proxy_header_localhost");
|
||||
|
||||
memset(&v2_header, 0, sizeof(v2_header));
|
||||
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
|
||||
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* Version (0x2) , Command = PROXY (0x1) */
|
||||
v2_header.fam= 0x31;
|
||||
v2_header.len= htons(216);
|
||||
init_v2_header(&v2_header, 0x1, 0x31, sizeof(v2_header.addr.unx));
|
||||
strcpy(v2_header.addr.unx.src_addr,"/tmp/mysql.sock");
|
||||
strcpy(v2_header.addr.unx.dst_addr,"/tmp/mysql.sock");
|
||||
rc = mysql_query(mysql, "CREATE USER 'u'@'localhost' IDENTIFIED BY 'password'");
|
||||
@@ -20738,10 +20750,8 @@ static void test_proxy_header_ignore()
|
||||
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
|
||||
mysql_close(m);
|
||||
|
||||
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
|
||||
v2_header.ver_cmd = (0x2 << 4) | 0x0; /* Version (0x2) , Command = LOCAL (0x0) */
|
||||
v2_header.fam= 0x0; /* AF_UNSPEC*/
|
||||
v2_header.len= htons(0);
|
||||
/* Command LOCAL (0x0), AF_UNSPEC, no address block */
|
||||
init_v2_header(&v2_header, 0x0, 0x0, 0);
|
||||
m = mysql_client_init(NULL);
|
||||
mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header,16);
|
||||
DIE_UNLESS(mysql_real_connect(m, opt_host, "root", "", NULL, opt_port, opt_unix_socket, 0) == m);
|
||||
@@ -20760,6 +20770,113 @@ static void test_proxy_header_ignore()
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
/* Open a raw TCP connection to opt_host:opt_port */
|
||||
static my_socket connect_tcp(void)
|
||||
{
|
||||
struct addrinfo hints, *ai= NULL, *p;
|
||||
char portbuf[MY_INT32_NUM_DECIMAL_DIGITS];
|
||||
int connected= 0;
|
||||
my_socket s= INVALID_SOCKET;
|
||||
|
||||
snprintf(portbuf, sizeof(portbuf), "%u", opt_port);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family= AF_UNSPEC;
|
||||
hints.ai_socktype= SOCK_STREAM;
|
||||
DIE_UNLESS(getaddrinfo(opt_host ? opt_host : "localhost", portbuf,
|
||||
&hints, &ai) == 0);
|
||||
|
||||
for (p= ai; p; p= p->ai_next)
|
||||
{
|
||||
s= socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if (s == INVALID_SOCKET)
|
||||
continue;
|
||||
if (connect(s, p->ai_addr, (int) p->ai_addrlen) == 0)
|
||||
{
|
||||
connected= 1;
|
||||
break;
|
||||
}
|
||||
closesocket(s);
|
||||
s= INVALID_SOCKET;
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
DIE_UNLESS(connected);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
MDEV-41508 PROXY v2 header, shorter than it declares, must be rejected.
|
||||
Raw socket is used, as the client library would follow the header with
|
||||
its login packet, which would be taken for the missing header bytes.
|
||||
*/
|
||||
static void test_proxy_header_short()
|
||||
{
|
||||
v2_proxy_header v2_header;
|
||||
/* 1024 is enough for the greeting packet plus the error packet */
|
||||
char buf[1024];
|
||||
/* Error packet payload: 0xff, error code, '#' and SQLSTATE, then message */
|
||||
const size_t error_payload_header_len= 1 + 2 + 1 + SQLSTATE_LENGTH;
|
||||
size_t total= 0, error_packet_pos, error_packet_len, err_msg_len;
|
||||
const uchar *error_packet, *error_payload;
|
||||
const char *err_msg;
|
||||
static const LEX_CSTRING expected_msg=
|
||||
{ STRING_WITH_LEN("Failed to parse proxy header") };
|
||||
uint err_code;
|
||||
my_socket s;
|
||||
|
||||
myheader("test_proxy_header_short");
|
||||
|
||||
/* Command PROXY, TCP over IPv4, declares the whole IPv4 address block */
|
||||
init_v2_header(&v2_header, 0x1, 0x11, sizeof(v2_header.addr.ip4));
|
||||
|
||||
s= connect_tcp();
|
||||
/*
|
||||
Send an incomplete PROXY v2 header, 25 bytes: the signature, 2 IPv4
|
||||
addresses, and the first byte of the source port (16 + 4 + 4 + 1).
|
||||
The complete header is 28 bytes.
|
||||
*/
|
||||
DIE_UNLESS(send(s, (const char *) &v2_header, 25, 0) == 25);
|
||||
|
||||
/*
|
||||
The server sends its greeting first. Then it rejects the header with an
|
||||
error packet and closes the connection, so read until EOF.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
int n= recv(s, buf + total, (int) (sizeof(buf) - total), 0);
|
||||
if (n <= 0)
|
||||
break;
|
||||
total+= n;
|
||||
}
|
||||
closesocket(s);
|
||||
|
||||
/*
|
||||
Every packet is: 3 bytes of payload length, 1 byte of sequence number,
|
||||
and the payload. Skip the greeting packet.
|
||||
*/
|
||||
DIE_UNLESS(total >= NET_HEADER_SIZE);
|
||||
error_packet_pos= NET_HEADER_SIZE + uint3korr(buf);
|
||||
|
||||
/* The error packet, after its header: 0xff, error code, '#', SQLSTATE */
|
||||
DIE_UNLESS(total >= error_packet_pos + NET_HEADER_SIZE);
|
||||
error_packet= (const uchar *) buf + error_packet_pos;
|
||||
error_packet_len= uint3korr(error_packet);
|
||||
error_payload= error_packet + NET_HEADER_SIZE;
|
||||
DIE_UNLESS(error_packet_len >= error_payload_header_len);
|
||||
DIE_UNLESS(total >= error_packet_pos + NET_HEADER_SIZE + error_packet_len);
|
||||
DIE_UNLESS(error_payload[0] == 0xff);
|
||||
DIE_UNLESS(error_payload[3] == '#');
|
||||
err_code= uint2korr(error_payload + 1);
|
||||
err_msg= (const char *) error_payload + error_payload_header_len;
|
||||
err_msg_len= error_packet_len - error_payload_header_len;
|
||||
printf("error %u: %.*s\n", err_code, (int) err_msg_len, err_msg);
|
||||
|
||||
DIE_UNLESS(err_code == ER_UNKNOWN_ERROR);
|
||||
DIE_UNLESS(err_msg_len == expected_msg.length);
|
||||
DIE_UNLESS(!memcmp(err_msg, expected_msg.str, expected_msg.length));
|
||||
}
|
||||
|
||||
|
||||
static void test_proxy_header_limits()
|
||||
{
|
||||
MYSQL *m;
|
||||
@@ -20823,11 +20940,7 @@ static void test_proxy_header_dbug_remote_connection()
|
||||
myquery(rc);
|
||||
|
||||
/* Build a v2 proxy header announcing proxy_ipv6 as the client address. */
|
||||
memset(&v2_header, 0, sizeof(v2_header));
|
||||
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
|
||||
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* PROXY command */
|
||||
v2_header.fam = 0x21; /* TCPv6 */
|
||||
v2_header.len = htons(36);
|
||||
init_v2_header(&v2_header, 0x1, 0x21, sizeof(v2_header.addr.ip6));
|
||||
inet_pton(AF_INET6, proxy_ipv6, addr_bin);
|
||||
memcpy(v2_header.addr.ip6.src_addr, addr_bin, 16);
|
||||
memcpy(v2_header.addr.ip6.dst_addr, addr_bin, 16);
|
||||
@@ -20872,33 +20985,8 @@ static void test_proxy_header_dbug_remote_connection()
|
||||
*/
|
||||
static void proxy_send_handshake_error(const char *client_ip)
|
||||
{
|
||||
struct addrinfo hints, *ai= NULL, *p;
|
||||
char portbuf[20], header[128];
|
||||
int connected= 0;
|
||||
my_socket s= INVALID_SOCKET;
|
||||
|
||||
snprintf(portbuf, sizeof(portbuf), "%u", opt_port);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family= AF_UNSPEC;
|
||||
hints.ai_socktype= SOCK_STREAM;
|
||||
DIE_UNLESS(getaddrinfo(opt_host ? opt_host : "localhost", portbuf,
|
||||
&hints, &ai) == 0);
|
||||
|
||||
for (p= ai; p; p= p->ai_next)
|
||||
{
|
||||
s= socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if (s == INVALID_SOCKET)
|
||||
continue;
|
||||
if (connect(s, p->ai_addr, (int) p->ai_addrlen) == 0)
|
||||
{
|
||||
connected= 1;
|
||||
break;
|
||||
}
|
||||
closesocket(s);
|
||||
s= INVALID_SOCKET;
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
DIE_UNLESS(connected);
|
||||
char header[128];
|
||||
my_socket s= connect_tcp();
|
||||
|
||||
if (client_ip)
|
||||
{
|
||||
@@ -21198,6 +21286,7 @@ static void test_proxy_header()
|
||||
test_proxy_header_localhost();
|
||||
test_proxy_header_ignore();
|
||||
test_proxy_header_limits();
|
||||
test_proxy_header_short();
|
||||
test_proxy_header_dbug_remote_connection();
|
||||
test_proxy_header_connect_errors_reset();
|
||||
test_proxy_header_proxy_host_connect_errors_reset();
|
||||
|
||||
Reference in New Issue
Block a user