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-15 07:47:24 +03:00
136 changed files with 4389 additions and 378 deletions

View File

@@ -88,6 +88,9 @@ enforces the coding style — flag style violations as well as bugs.
## PR hygiene
- Subject line starts with `MDEV-NNNNN`; body wrapped at 72 cols.
- AI attribution must use the `Assisted-by: NAME:MODEL` line in the commit
comment, for example, `Assisted-by: Claude:claude-5.5-opus`. Flag if it's
`Co-Authored-By`, flag if an AI-assisted commit has no attribution.
- Bug fix targets the oldest maintained branch that reproduces (≤3y since GA);
new feature targets the main branch.
- Bug fix commit is minimal; cleanups (unrelated and related - prerequisite

View File

@@ -1,4 +1,4 @@
# Community Contributions Process #
# Community Contributions Process
MariaDB Server is an Open Source project that thrives thanks to its community.
MariaDB Foundation handles all community contributions in a timely manner.
@@ -25,7 +25,7 @@ But it's important to always have both prior to review.
There are constant efforts (including background processes) to make sure
the states of the Jira and the corresponding pull request(s) are kept in sync.
### What should go into the jira ###
### What should go into the jira
The [Jira](https://jira.mariadb.org) is used to describe the design of the
feature: why is it a good idea, what should work, what might not work etc.
@@ -45,9 +45,9 @@ See [here](https://mariadb.org/contribute/#contribute-to-testing)
> [!CAUTION]
> If the jira already exists and is assigned to somebody please reach out
> to them **before** starting to working on it to avoid effort duplication.
> to them **before** starting to working on it to avoid effort duplication.
### What should go into the pull request ###
### What should go into the pull request
The pull request is used to describe the implementation.
E.g. how the bug is fixed. Or how the feature is implemented in detail.
@@ -64,7 +64,7 @@ And that commit should have a commit message that's compliant with
> If there's no Jira or no reference to it in your pull request
> there might be delays in processing it
## States of a Community Contribution ##
## States of a Community Contribution
```mermaid
stateDiagram-v2
@@ -87,7 +87,7 @@ stateDiagram-v2
Closed --> [*]
```
### Draft ###
### Draft
* Pull request
* State: Draft
@@ -114,7 +114,7 @@ the GitHub pull request viewer UI.
> and "External Contribution" pull request labels and there's
> been no activity for 3 months.
### Open ##
### Open
* Pull request
* State: Open
@@ -169,7 +169,7 @@ Questions and Answers
> label to pull requests in this state, if absent.
> This marks the contribution entering the preliminary review queue.
### Preliminary Review ###
### Preliminary Review
* Pull request
* State: Open
@@ -199,7 +199,7 @@ The preliminary reviewer does the following:
Or that an internal developer has taken over the contribution to
re-do it and push it directly.
### Final review ###
### Final review
* Pull request
* State: Open
@@ -245,7 +245,7 @@ detailed information:
> There's a requirement that certain contributions need to undergo
> a separate test step. This is usually done for the new features or
> for more complex and risky bug fixes. If that's the desired state,
> a test engineer will be assigned as a final reviewer. See
> a test engineer will be assigned as a final reviewer. See
> https://mariadb.com/docs/server/reference/product-development/mariadb-quality-development-rules
> for more details.
@@ -256,7 +256,7 @@ detailed information:
> in the middle of the new feature development cycle or vice versa
> it might take up to a couple of months for a developer to do a review.
### Approved ###
### Approved
* Pull request
* State: Open
@@ -280,7 +280,7 @@ The person that does the merge might need some assistance by the contributor.
When the pull request is successfully merged the Jira should be moved to
"Closed" state and its target version should be specified.
### Merged ###
### Merged
* Pull request
* State: Merged
@@ -295,7 +295,7 @@ This is the "success" final state for a contribution.
Ideally all contributions will end up in this state.
No further actions are necessary.
### Closed ###
### Closed
* Pull request
* State: Closed

View File

@@ -1415,6 +1415,9 @@ int main(int argc,char *argv[])
mysql_thread_id(&mysql), server_version_string(&mysql));
put_info((char*) glob_buffer.ptr(),INFO_INFO);
put_info(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"), INFO_INFO);
put_info("Help others discover MariaDB."
" Star it on GitHub: https://github.com/MariaDB/server\n",
INFO_INFO);
}
#ifdef HAVE_READLINE
@@ -4505,6 +4508,8 @@ print_tab_data(MYSQL_RES *result)
}
while ((cur = fetch_row(result)))
{
if (interrupted_query)
break;
lengths=mysql_fetch_lengths(result);
field= mysql_fetch_fields(result);
if (opt_binhex && is_binary_field(&field[0]))

View File

@@ -5395,7 +5395,7 @@ void do_sync_with_master(struct st_command *command)
long offset= 0;
char *p= command->first_argument;
const char *offset_start= p;
char *start, *buff= 0;
char *start, *buff= 0, *str;
start= const_cast<char*>("");
if (*offset_start)
@@ -5413,15 +5413,15 @@ void do_sync_with_master(struct st_command *command)
p++;
while (*p && my_isspace(charset_info, *p))
p++;
start= buff= (char*)my_malloc(PSI_NOT_INSTRUMENTED, strlen(p)+1,
MYF(MY_WME|MY_FAE));
get_string(&buff, &p, command);
str= buff= (char*)my_malloc(PSI_NOT_INSTRUMENTED, strlen(p)+1,
MYF(MY_WME|MY_FAE));
start= get_string(&str, &p, command);
}
command->last_argument= p;
}
do_sync_with_master2(command, offset, start);
if (buff)
my_free(start);
my_free(buff);
return;
}
@@ -8464,7 +8464,7 @@ void do_get_errcodes(struct st_command *command)
/*
Get a string; Return ptr to end of string
Get a string; Return ptr to start of string
Strings may be surrounded by " or '
If string is a '$variable', return the value of the variable.

View File

@@ -28,6 +28,10 @@ IF(RPM)
STRING(REGEX REPLACE "^Have library " "" L ${H})
SET(V ${L}_LIBRARY)
FIND_LIBRARY(${V} ${L})
ELSEIF(H MATCHES "^Have includes? [^/]" AND ${V})
STRING(REGEX REPLACE "^Have includes? (.*;)?" "" I ${H})
SET(V ${I}_INCLUDE)
FIND_FILE(${V} ${I})
ENDIF()
GET_PROPERTY(T CACHE ${V} PROPERTY TYPE)
IF ((T STREQUAL FILEPATH OR V MATCHES "^CMAKE_COMMAND$") AND ${V} MATCHES "^/")

View File

@@ -59,6 +59,11 @@ MACRO(CHECK_DTRACE)
SET(DTRACE_FLAGS -64 CACHE INTERNAL "DTrace architecture flags")
ENDIF()
ENDIF()
include(CheckIncludeFile)
CHECK_INCLUDE_FILE("sys/sdt.h" HAVE_SYS_SDT_H)
IF(NOT HAVE_SYS_SDT_H)
SET(ENABLE_DTRACE OFF CACHE BOOL "Disable dtrace")
ENDIF()
ENDMACRO()
CHECK_DTRACE()

View File

@@ -933,6 +933,7 @@ extern void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root,
extern void *alloc_root(MEM_ROOT *mem_root, size_t Size);
extern void *multi_alloc_root(MEM_ROOT *mem_root, ...);
extern void free_root(MEM_ROOT *root, myf MyFLAGS);
/* There is also dbug_get_mem_root_alloc_size() in sql/sql_test.cc */
extern void move_root(MEM_ROOT *to, MEM_ROOT *from);
extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,

View File

@@ -2,11 +2,26 @@
#
# Waits for galera node to transition to READY state.
#
# Parameters
# ----------
# $galera_wait_ready_timeout (optional)
# Seconds to wait before giving up, 60 by default. Raise it around a
# restart which has to wait for a full SST, as that takes considerably
# longer than a plain restart and on a loaded machine can exceed the
# default. The value is reset here, so it only applies to the next
# sourcing of this file.
#
--enable_reconnect
--disable_query_log
--disable_result_log
let $wait_counter = 600;
if ($galera_wait_ready_timeout)
{
let $wait_counter = $($galera_wait_ready_timeout * 10);
}
while ($wait_counter)
{
--disable_abort_on_error
@@ -25,7 +40,7 @@ while ($wait_counter)
if (!$success)
{
die "Server did not transition to READY state";
die "Server did not transition to READY state in $_galera_wait_ready_timeout seconds";
}
--disable_reconnect
--enable_query_log

View File

@@ -6609,3 +6609,71 @@ a
0
DROP TABLE t1;
# End of 10.11 tests
#
# MDEV-32412 Pushdown from HAVING: Item_func is immutable while arguments are not
#
CREATE TABLE t1 (a int);
INSERT INTO t1 values (1),(2),(3);
SELECT a FROM ( SELECT a, 1 AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + 1;
a
2
EXPLAIN FORMAT=JSON SELECT a FROM ( SELECT a, 1 AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + 1;
EXPLAIN
{
"query_block": {
"select_id": 1,
"cost": 0.012403489,
"filesort": {
"sort_key": "t1.a",
"temporary_table": {
"nested_loop": [
{
"table": {
"table_name": "t1",
"access_type": "ALL",
"loops": 1,
"rows": 3,
"cost": 0.010504815,
"filtered": 100,
"attached_condition": "t1.a = <cache>(1 + 1)"
}
}
]
}
}
}
}
# Item_param as an immutable argument of the pushed equality
PREPARE stmt FROM
"SELECT a FROM ( SELECT a, 1 AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + ?";
SET @p= 1;
EXECUTE stmt USING @p;
a
2
SET @p= 2;
EXECUTE stmt USING @p;
a
3
DEALLOCATE PREPARE stmt;
# Item_param as the basic constant behind the view reference
PREPARE stmt FROM
"SELECT a FROM ( SELECT a, ? AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + 1";
SET @p= 1;
EXECUTE stmt USING @p;
a
2
SET @p= 2;
EXECUTE stmt USING @p;
a
3
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
# End of 11.4 tests

View File

@@ -1822,3 +1822,45 @@ HAVING (NOT a) AND TRUE;
DROP TABLE t1;
--echo # End of 10.11 tests
--echo #
--echo # MDEV-32412 Pushdown from HAVING: Item_func is immutable while arguments are not
--echo #
CREATE TABLE t1 (a int);
INSERT INTO t1 values (1),(2),(3);
let $q= SELECT a FROM ( SELECT a, 1 AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + 1;
eval $q;
eval EXPLAIN FORMAT=JSON $q;
--echo # Item_param as an immutable argument of the pushed equality
PREPARE stmt FROM
"SELECT a FROM ( SELECT a, 1 AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + ?";
SET @p= 1;
EXECUTE stmt USING @p;
SET @p= 2;
EXECUTE stmt USING @p;
DEALLOCATE PREPARE stmt;
--echo # Item_param as the basic constant behind the view reference
PREPARE stmt FROM
"SELECT a FROM ( SELECT a, ? AS b FROM t1 ) AS dt
GROUP BY a, b
HAVING a = b + 1";
SET @p= 1;
EXECUTE stmt USING @p;
SET @p= 2;
EXECUTE stmt USING @p;
DEALLOCATE PREPARE stmt;
DROP TABLE t1;
--echo # End of 11.4 tests

View File

@@ -45,6 +45,21 @@ json_normalize('{ "invalid": "no_close"')
NULL
Warnings:
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_normalize'
select json_normalize('');
json_normalize('')
NULL
Warnings:
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_normalize'
select json_normalize(' ');
json_normalize(' ')
NULL
Warnings:
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_normalize'
select json_normalize('123 garbage');
json_normalize('123 garbage')
NULL
Warnings:
Warning 4038 Syntax error in JSON text in argument 1 to function 'json_normalize' at position 5
drop table t1;
drop view v1;
create table t1 (text varchar(200) character set 'latin1');

View File

@@ -25,6 +25,9 @@ order by json;
select json_normalize(NULL);
select json_normalize('{ "invalid": "no_close"');
select json_normalize('');
select json_normalize(' ');
select json_normalize('123 garbage');
drop table t1;
drop view v1;

View File

@@ -0,0 +1,54 @@
#
# MDEV-40698: ROLLUP query returns incorrect result with empty result set
#
CREATE TABLE t0(a INT, b INT, c INT);
SELECT *, COUNT(0) FROM t0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT *, COUNT(0) FROM t0 WHERE 1=0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT *, COUNT(0), ROW_NUMBER() OVER () FROM t0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0) ROW_NUMBER() OVER ()
NULL NULL NULL 0 1
SELECT 1 IN (SELECT a FROM t0 WHERE a>0 GROUP BY a WITH ROLLUP);
1 IN (SELECT a FROM t0 WHERE a>0 GROUP BY a WITH ROLLUP)
NULL
INSERT INTO t0 VALUES (0, 0, 0),(0, 0, 0);
SELECT *, COUNT(0) FROM t0 WHERE a > 0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT EXISTS (SELECT t0.a FROM t0 x WHERE 1=0 GROUP BY x.a WITH ROLLUP) AS e
FROM t0;
e
1
1
SELECT DISTINCT a FROM t0 GROUP BY a, b WITH ROLLUP;
a
0
NULL
CREATE TABLE t1 (a INT, b INT, c INT) ENGINE=InnoDB;
SELECT *, COUNT(0) FROM t1 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT *, COUNT(0) FROM t1 WHERE 1=0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT *, COUNT(0), ROW_NUMBER() OVER () FROM t1 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0) ROW_NUMBER() OVER ()
NULL NULL NULL 0 1
INSERT INTO t1 VALUES (0, 0, 0),(0, 0, 0);
SELECT *, COUNT(0) FROM t1 WHERE a > 0 GROUP BY a, b, c WITH ROLLUP;
a b c COUNT(0)
NULL NULL NULL 0
SELECT EXISTS (SELECT t1.a FROM t1 x WHERE 1=0 GROUP BY x.a WITH ROLLUP) AS e
FROM t1;
e
1
1
SELECT DISTINCT a FROM t1 GROUP BY a, b WITH ROLLUP;
a
0
NULL
DROP TABLE t0, t1;
# End of 10.11 tests

View File

@@ -0,0 +1,30 @@
--source include/have_innodb.inc
--echo #
--echo # MDEV-40698: ROLLUP query returns incorrect result with empty result set
--echo #
CREATE TABLE t0(a INT, b INT, c INT);
SELECT *, COUNT(0) FROM t0 GROUP BY a, b, c WITH ROLLUP;
SELECT *, COUNT(0) FROM t0 WHERE 1=0 GROUP BY a, b, c WITH ROLLUP;
SELECT *, COUNT(0), ROW_NUMBER() OVER () FROM t0 GROUP BY a, b, c WITH ROLLUP;
SELECT 1 IN (SELECT a FROM t0 WHERE a>0 GROUP BY a WITH ROLLUP);
INSERT INTO t0 VALUES (0, 0, 0),(0, 0, 0);
SELECT *, COUNT(0) FROM t0 WHERE a > 0 GROUP BY a, b, c WITH ROLLUP;
SELECT EXISTS (SELECT t0.a FROM t0 x WHERE 1=0 GROUP BY x.a WITH ROLLUP) AS e
FROM t0;
SELECT DISTINCT a FROM t0 GROUP BY a, b WITH ROLLUP;
CREATE TABLE t1 (a INT, b INT, c INT) ENGINE=InnoDB;
SELECT *, COUNT(0) FROM t1 GROUP BY a, b, c WITH ROLLUP;
SELECT *, COUNT(0) FROM t1 WHERE 1=0 GROUP BY a, b, c WITH ROLLUP;
SELECT *, COUNT(0), ROW_NUMBER() OVER () FROM t1 GROUP BY a, b, c WITH ROLLUP;
INSERT INTO t1 VALUES (0, 0, 0),(0, 0, 0);
SELECT *, COUNT(0) FROM t1 WHERE a > 0 GROUP BY a, b, c WITH ROLLUP;
SELECT EXISTS (SELECT t1.a FROM t1 x WHERE 1=0 GROUP BY x.a WITH ROLLUP) AS e
FROM t1;
SELECT DISTINCT a FROM t1 GROUP BY a, b WITH ROLLUP;
DROP TABLE t0, t1;
--echo # End of 10.11 tests

View File

@@ -10,6 +10,8 @@ Your MariaDB connection id is X
Server version: Y
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Help others discover MariaDB. Star it on GitHub: https://github.com/MariaDB/server
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> delimiter $
@@ -40,6 +42,8 @@ Your MariaDB connection id is X
Server version: Y
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Help others discover MariaDB. Star it on GitHub: https://github.com/MariaDB/server
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> create database db1;
@@ -74,6 +78,8 @@ Your MariaDB connection id is X
Server version: Y
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Help others discover MariaDB. Star it on GitHub: https://github.com/MariaDB/server
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [test]> CREATE TABLE t (c INT) ENGINE=InnoDB;
@@ -105,6 +111,8 @@ Your MariaDB connection id is X
Your MariaDB connection id is X
Server version: Y
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Help others discover MariaDB. Star it on GitHub: https://github.com/MariaDB/server
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

View File

@@ -791,4 +791,24 @@ select * from t1 where a in (1, 2) and b = 2 order by a, b;
a b c
1 2 NULL
drop table t1;
#
# MDEV-31225: Signal 11 after select ..WHERE ROW(a,(a,a)) IN
# ((1,(1,1)),(2,(2,null))) with in_predicate_conversion_threshold=1
#
create table t1 (a int);
insert into t1 values (1), (2), (3);
set in_predicate_conversion_threshold=1;
explain extended select a from t1 where row(a,(a,a)) in ((1,(1,1)),(2,(2,null)));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a`,(`test`.`t1`.`a`,`test`.`t1`.`a`)) in (<cache>((1,(1,1))),<cache>((2,(2,NULL))))
select a from t1 where row(a,(a,a)) in ((1,(1,1)),(2,(2,null)));
a
1
select a from t1 where row(a,(a,a)) not in ((1,(1,1)),(2,(2,2)));
a
3
drop table t1;
set in_predicate_conversion_threshold=default;
# End of 11.4 tests

View File

@@ -488,4 +488,25 @@ select * from t1 where a in (1, 2) and b = 2 order by a, b;
drop table t1;
--echo #
--echo # MDEV-31225: Signal 11 after select ..WHERE ROW(a,(a,a)) IN
--echo # ((1,(1,1)),(2,(2,null))) with in_predicate_conversion_threshold=1
--echo #
create table t1 (a int);
insert into t1 values (1), (2), (3);
set in_predicate_conversion_threshold=1;
let $query=
select a from t1 where row(a,(a,a)) in ((1,(1,1)),(2,(2,null)));
eval explain extended $query;
eval $query;
select a from t1 where row(a,(a,a)) not in ((1,(1,1)),(2,(2,2)));
drop table t1;
set in_predicate_conversion_threshold=default;
--echo # End of 11.4 tests

View File

@@ -684,4 +684,39 @@ SELECT * FROM t1 ORDER BY c1 LIMIT 1;
c1
1
DROP TABLE t1;
#
# MDEV-41252: Assertion partial_join_cost >= -2.2204460492503131e-16
# when optimizer_scan_setup_cost=100000000, and
# optimizer_join_limit_pref_ratio=1
#
CREATE TABLE t (c INT NOT NULL UNIQUE) ENGINE=InnoDB;
INSERT INTO t VALUES (0),(1),(2),(3),(4),(5);
ANALYZE TABLE t;
Table Op Msg_type Msg_text
test.t analyze status Engine-independent statistics collected
test.t analyze status OK
SET optimizer_scan_setup_cost=100000000;
SET optimizer_join_limit_pref_ratio=1;
EXPLAIN SELECT c FROM t ORDER BY c LIMIT 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t index NULL c 4 NULL 1
SET optimizer_join_limit_pref_ratio= DEFAULT;
SET optimizer_scan_setup_cost= DEFAULT;
DROP TABLE t;
# another variant, with only optimizer_join_limit_pref_ratio=1
CREATE TABLE t (c INT NOT NULL UNIQUE) ENGINE=InnoDB;
INSERT INTO t SELECT seq FROM seq_1_to_30000;
ANALYZE TABLE t;
Table Op Msg_type Msg_text
test.t analyze status Engine-independent statistics collected
test.t analyze status OK
SET optimizer_join_limit_pref_ratio=1;
EXPLAIN SELECT c FROM t ORDER BY c LIMIT 28000;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t index NULL c 4 NULL 28000
SET optimizer_join_limit_pref_ratio= DEFAULT;
DROP TABLE t;
#
# End of 11.4 tests
#
set optimizer_join_limit_pref_ratio=default;

View File

@@ -344,4 +344,41 @@ INSERT INTO t1 VALUES (1),(2);
SELECT * FROM t1 ORDER BY c1 LIMIT 1;
DROP TABLE t1;
--echo #
--echo # MDEV-41252: Assertion partial_join_cost >= -2.2204460492503131e-16
--echo # when optimizer_scan_setup_cost=100000000, and
--echo # optimizer_join_limit_pref_ratio=1
--echo #
CREATE TABLE t (c INT NOT NULL UNIQUE) ENGINE=InnoDB;
INSERT INTO t VALUES (0),(1),(2),(3),(4),(5);
ANALYZE TABLE t;
SET optimizer_scan_setup_cost=100000000;
SET optimizer_join_limit_pref_ratio=1;
EXPLAIN SELECT c FROM t ORDER BY c LIMIT 1;
SET optimizer_join_limit_pref_ratio= DEFAULT;
SET optimizer_scan_setup_cost= DEFAULT;
DROP TABLE t;
--echo # another variant, with only optimizer_join_limit_pref_ratio=1
CREATE TABLE t (c INT NOT NULL UNIQUE) ENGINE=InnoDB;
INSERT INTO t SELECT seq FROM seq_1_to_30000;
ANALYZE TABLE t;
SET optimizer_join_limit_pref_ratio=1;
EXPLAIN SELECT c FROM t ORDER BY c LIMIT 28000;
SET optimizer_join_limit_pref_ratio= DEFAULT;
DROP TABLE t;
--echo #
--echo # End of 11.4 tests
--echo #
set optimizer_join_limit_pref_ratio=default;

View File

@@ -2272,6 +2272,53 @@ select 1b from test.1a;
select select.1c from test.select;
1c
drop table 1a, `select`;
#
# MDEV-37167 Nested BEGINs (4600+) cause a segmentation fault
#
#
# Unclosed BEGINs
#
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1
#
# Well-formed deep routine
#
DROP PROCEDURE p2;
#
# Same, with variables, so the contexts are not empty
#
DROP PROCEDURE p3;
#
# A deep FUNCTION tears down the same way
#
DROP FUNCTION f1;
#
# Invoking a deep routine must not crash. It is rejected with
# ER_STACK_OVERRUN_NEED_MORE when the frame does not fit the thread
# stack, which depends on the build, so both outcomes are accepted.
#
CALL p4();
DROP PROCEDURE p4;
#
# Nested scopes must still resolve correctly
#
CREATE PROCEDURE p5()
BEGIN
DECLARE a INT DEFAULT 1;
BEGIN
DECLARE b INT DEFAULT 2;
BEGIN
DECLARE c INT DEFAULT 3;
SELECT a + b + c AS total;
END;
END;
END$$
CALL p5();
total
6
DROP PROCEDURE p5;
SELECT 'Server is alive' AS state;
state
Server is alive
# End of 10.11 tests
#
# Start of 11.7 tests

View File

@@ -2078,6 +2078,80 @@ select select.1c from test.select;
drop table 1a, `select`;
--echo #
--echo # MDEV-37167 Nested BEGINs (4600+) cause a segmentation fault
--echo #
# Each nested BEGIN adds one sp_pcontext. Freeing them used to recurse once
# per nesting level and overran the thread stack. Building the run-time frame
# still recurses, but checks the stack now, and the depth where it gives up
# follows thread_stack, so the invoking statements accept either outcome.
--echo #
--echo # Unclosed BEGINs
--echo #
--disable_query_log
--error ER_PARSE_ERROR
--eval CREATE PROCEDURE p1() $(repeat("BEGIN ", 6000))
--enable_query_log
--echo #
--echo # Well-formed deep routine
--echo #
--disable_query_log
--eval CREATE PROCEDURE p2() $(repeat("BEGIN ", 6000)) $(repeat("END; ", 5999)) END
--enable_query_log
DROP PROCEDURE p2;
--echo #
--echo # Same, with variables, so the contexts are not empty
--echo #
--disable_query_log
--eval CREATE PROCEDURE p3() $(repeat("BEGIN DECLARE v INT DEFAULT 1; ", 2000)) $(repeat("END; ", 1999)) END
--enable_query_log
DROP PROCEDURE p3;
--echo #
--echo # A deep FUNCTION tears down the same way
--echo #
--disable_query_log
--eval CREATE FUNCTION f1() RETURNS INT $(repeat("BEGIN ", 6000)) RETURN 1; $(repeat("END; ", 5999)) END
--enable_query_log
DROP FUNCTION f1;
--echo #
--echo # Invoking a deep routine must not crash. It is rejected with
--echo # ER_STACK_OVERRUN_NEED_MORE when the frame does not fit the thread
--echo # stack, which depends on the build, so both outcomes are accepted.
--echo #
--disable_query_log
--eval CREATE PROCEDURE p4() $(repeat("BEGIN ", 6000)) $(repeat("END; ", 5999)) END
--enable_query_log
--error 0,ER_STACK_OVERRUN_NEED_MORE
CALL p4();
DROP PROCEDURE p4;
--echo #
--echo # Nested scopes must still resolve correctly
--echo #
DELIMITER $$;
CREATE PROCEDURE p5()
BEGIN
DECLARE a INT DEFAULT 1;
BEGIN
DECLARE b INT DEFAULT 2;
BEGIN
DECLARE c INT DEFAULT 3;
SELECT a + b + c AS total;
END;
END;
END$$
DELIMITER ;$$
CALL p5();
DROP PROCEDURE p5;
SELECT 'Server is alive' AS state;
--echo # End of 10.11 tests
--echo #

View File

@@ -10,6 +10,7 @@ EXECUTE stmt;
ERROR 22007: Truncated incorrect DOUBLE value: 'foo'
SELECT a FROM t1 GROUP BY NULL WITH ROLLUP;
a
NULL
DROP TABLE t1;
SET sql_mode=DEFAULT;
SET SQL_MODE= 'STRICT_ALL_TABLES';

View File

@@ -3930,9 +3930,80 @@ pk b
29 3
drop view v1;
drop table t1,t2,t3,t4;
#
# MDEV-32401 expression cache lead to crash if table of wrong type
# created and the cache switched off
#
CREATE TABLE t0 (c TEXT);
INSERT INTO t0 VALUES (71), (-79);
SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`t0`.`c` AS `d` from `test`.`t0` join `test`.`t0` where <expr_cache><`test`.`t0`.`c`>((/* select#3 */ select ord(<expr_cache><`test`.`t0`.`c`,bit_or(`test`.`t0`.`c`),bit_or(`test`.`t0`.`c`)>((/* select#4 */ select bit_or(`test`.`t0`.`c`)))) from `test`.`t0` group by `test`.`t0`.`c`))
SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t0 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t0 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select cast(`test`.`t0`.`c` as signed) AS `d` from `test`.`t0` join `test`.`t0` where <expr_cache><cast(`test`.`t0`.`c` as signed)>((/* select#3 */ select ord(<expr_cache><cast(`test`.`t0`.`c` as signed),bit_or(cast(`test`.`t0`.`c` as signed)),bit_or(cast(`test`.`t0`.`c` as signed))>((/* select#4 */ select bit_or(cast(`test`.`t0`.`c` as signed))))) from `test`.`t0` group by cast(`test`.`t0`.`c` as signed)))
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (71), (-79);
SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
d
71
-79
71
-79
EXPLAIN EXTENDED SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 Using where
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 Using join buffer (flat, BNL join)
3 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00 Using temporary; Using filesort
4 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1276 Field or reference 'd' of SELECT #4 was resolved in SELECT #1
Note 1981 Aggregate function 'bit_or()' of SELECT #4 belongs to SELECT #3
Note 1276 Field or reference 'd' of SELECT #3 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`t1`.`c` AS `d` from `test`.`t1` join `test`.`t1` where <expr_cache><`test`.`t1`.`c`>((/* select#3 */ select ord(<expr_cache><`test`.`t1`.`c`,bit_or(`test`.`t1`.`c`),bit_or(`test`.`t1`.`c`)>((/* select#4 */ select bit_or(`test`.`t1`.`c`)))) from `test`.`t1` group by `test`.`t1`.`c`))
DROP TABLE t0, t1;
# End of 11.4 tests
SET optimizer_switch=@save_optimizer_switch;
# restore default
set @@optimizer_switch= default;
#
# MDEV-38801 implement Item_cache_year shallow_copy()
#
@@ -3957,4 +4028,4 @@ NATURAL JOIN
2000 1
2001 1
DROP TABLE t1;
# end of 10.11 tests
# End of 11.8 tests

View File

@@ -1739,12 +1739,40 @@ SELECT * FROM t4 WHERE b NOT IN ( SELECT * FROM v1 );
drop view v1;
drop table t1,t2,t3,t4;
--echo #
--echo # MDEV-32401 expression cache lead to crash if table of wrong type
--echo # created and the cache switched off
--echo #
CREATE TABLE t0 (c TEXT);
INSERT INTO t0 VALUES (71), (-79);
SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT c AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
EXPLAIN EXTENDED SELECT t1.d FROM (SELECT CAST(c as int) AS d FROM t0) AS t1 JOIN t0 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t0 GROUP BY d
);
CREATE TABLE t1 (c INT);
INSERT INTO t1 VALUES (71), (-79);
SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
EXPLAIN EXTENDED SELECT t2.d FROM (SELECT c AS d FROM t1) AS t2 JOIN t1 ON (
SELECT ORD((SELECT BIT_OR(d))) FROM t1 GROUP BY d
);
DROP TABLE t0, t1;
--echo # End of 11.4 tests
SET optimizer_switch=@save_optimizer_switch;
--echo # restore default
set @@optimizer_switch= default;
--echo #
--echo # MDEV-38801 implement Item_cache_year shallow_copy()
--echo #
@@ -1762,4 +1790,4 @@ SELECT
) FROM t1;
DROP TABLE t1;
--echo # end of 10.11 tests
--echo # End of 11.8 tests

View File

@@ -3539,6 +3539,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
select distinct 1, row_number() over (order by 1) from t1 where a=0 group by a with rollup;
1 row_number() over (order by 1)
1 1
drop table t1;
explain
SELECT DISTINCT BIT_OR(100) OVER () FROM dual

View File

@@ -3,4 +3,11 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
# Set gtid_slave_pos should not hang
SET GLOBAL gtid_slave_pos=@@gtid_binlog_pos;
COMMIT;
### MDEV-22848: SET GLOBAL gtid_slave_pos leaves dangling partial transaction
CREATE TABLE t1 (c INT) ENGINE=MyISAM;
SET GLOBAL gtid_slave_pos="0-1-100";
INSERT INTO t1 VALUES (0);
DROP TABLE not_there;
ERROR 42S02: Unknown table 'test.not_there'
DROP TABLE t1;
RESET MASTER;

View File

@@ -1,4 +1,4 @@
call mtr.add_suppression("BINLOG_BASE64_EVENT: According to the master's version");
call mtr.add_suppression("BINLOG_BASE64_EVENT: Master's version is earlier than");
call mtr.add_suppression("BINLOG_BASE64_EVENT: Column 1 of table 'test.char128_utf8' cannot be converted");
CREATE TABLE t1 (a int);
INSERT INTO t1 VALUES (1), (1);

View File

@@ -0,0 +1,72 @@
SET @@global.binlog_expire_logs_seconds= 0;
SET @@global.max_binlog_total_size= 0;
SET @@global.slave_connections_needed_for_purge= 1;
SELECT @@global.slave_connections_needed_for_purge;
@@global.slave_connections_needed_for_purge
1
SELECT @@global.binlog_expire_logs_seconds, @@global.max_binlog_total_size;
@@global.binlog_expire_logs_seconds @@global.max_binlog_total_size
0 0
# No replica has ever connected:
SELECT COUNT(*) AS dump_threads FROM information_schema.PROCESSLIST
WHERE COMMAND = 'Binlog Dump';
dump_threads
0
RESET MASTER;
#
# Part 1 (control). Nothing has attempted a purge yet, so no refusal is
# cached, and the interactive purge correctly ignores the slave's guard.
#
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
show binary logs;
Log_name File_size
master-bin.000001 #
master-bin.000002 #
master-bin.000003 #
master-bin.000004 #
PURGE BINARY LOGS TO 'master-bin.000003';
SHOW WARNINGS;
Level Code Message
show binary logs;
Log_name File_size
master-bin.000003 #
master-bin.000004 #
#
# Part 2. Let one automatic purge be refused, then try PURGE that must
# be effective on the fixed server.
#
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
show binary logs;
Log_name File_size
master-bin.000003 #
master-bin.000004 #
master-bin.000005 #
master-bin.000006 #
master-bin.000007 #
# Let the files age past binlog_expire_logs_seconds: purge_logs_before_date
# skips can_purge_log() for any file whose mtime is not older than that.
SET @@global.binlog_expire_logs_seconds= 1;
# Rotating attempts an automatic purge, which the guard refuses. That
# refusal is what populates the cache.
FLUSH BINARY LOGS;
# The automatic refusal states the correct reason in the error log:
FOUND 1 /Binary log .master-bin.000003. is not purged because less than .slave_connections_needed_for_purge. slaves have processed it/ in mysqld.1.err
# Now the interactive purge. It should behave exactly as in part 1.
PURGE BINARY LOGS TO 'master-bin.000006';
# On affected versions this reports master-bin.000003 as the current active
# binlog, while master-bin.000008 is active:
SHOW WARNINGS;
Level Code Message
show binary logs;
Log_name File_size
master-bin.000006 #
master-bin.000007 #
master-bin.000008 #
SET @@global.binlog_expire_logs_seconds = $save_binlog_expire_logs_seconds;
SET @@global.max_binlog_total_size= $save_max_binlog_total_size;
SET @@global.slave_connections_needed_for_purge= $save_slave_connections_needed_for_purge;
RESET MASTER;

View File

@@ -37,6 +37,15 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
SET GLOBAL gtid_slave_pos=@@gtid_binlog_pos;
COMMIT;
--echo ### MDEV-22848: SET GLOBAL gtid_slave_pos leaves dangling partial transaction
CREATE TABLE t1 (c INT) ENGINE=MyISAM;
SET GLOBAL gtid_slave_pos="0-1-100";
INSERT INTO t1 VALUES (0);
--error ER_BAD_TABLE_ERROR
DROP TABLE not_there;
DROP TABLE t1;
# Revert table type
--disable_query_log
--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine

View File

@@ -6,7 +6,7 @@
# BINLOG statement does not work in embedded mode.
source include/not_embedded.inc;
call mtr.add_suppression("BINLOG_BASE64_EVENT: According to the master's version");
call mtr.add_suppression("BINLOG_BASE64_EVENT: Master's version is earlier than");
call mtr.add_suppression("BINLOG_BASE64_EVENT: Column 1 of table 'test.char128_utf8' cannot be converted");
CREATE TABLE t1 (a int);

View File

@@ -0,0 +1,90 @@
# MDEV-41214 PURGE BINARY LOGS does not delete non-active binlog files
#
# PURGE BINARY LOGS could end with a warning instead of deleting files, as
# this test shows when run on the base of the fix. The warning names a wrong
# reason. It follows an automatic purge that was refused by the slave guard,
# slave_connections_needed_for_purge: the automatic purge does not delete a
# binlog while it might still be needed by a slave, not necessarily a
# connected one, that is while fewer than the variable's number of slaves
# are connected.
#
# Part 1 is a control: the same statement shape, with nothing cached, succeeds.
#
# The test sets slave_connections_needed_for_purge=1,
# binlog_expire_logs_seconds=0 and max_binlog_total_size=0, and restores the
# old values at the end. With slave_connections_needed_for_purge=0 the guard
# is off and the bug does not show.
#
--source include/have_binlog_format_mixed.inc
--let $save_slave_connections_needed_for_purge= `SELECT @@global.slave_connections_needed_for_purge`
--let $save_binlog_expire_logs_seconds= `SELECT @@global.binlog_expire_logs_seconds`
--let $save_max_binlog_total_size= `SELECT @@global.max_binlog_total_size`
SET @@global.binlog_expire_logs_seconds= 0;
SET @@global.max_binlog_total_size= 0;
SET @@global.slave_connections_needed_for_purge= 1;
SELECT @@global.slave_connections_needed_for_purge;
SELECT @@global.binlog_expire_logs_seconds, @@global.max_binlog_total_size;
--echo # No replica has ever connected:
SELECT COUNT(*) AS dump_threads FROM information_schema.PROCESSLIST
WHERE COMMAND = 'Binlog Dump';
RESET MASTER;
--echo #
--echo # Part 1 (control). Nothing has attempted a purge yet, so no refusal is
--echo # cached, and the interactive purge correctly ignores the slave's guard.
--echo #
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
--source include/show_binary_logs.inc
PURGE BINARY LOGS TO 'master-bin.000003';
SHOW WARNINGS;
--source include/show_binary_logs.inc
--let $first= query_get_value(SHOW BINARY LOGS, Log_name, 1)
if ($first != master-bin.000003)
{
--echo # Control failed: expected master-bin.000003 first, got $first
--die Interactive purge did not work even with no cached refusal
}
--echo #
--echo # Part 2. Let one automatic purge be refused, then try PURGE that must
--echo # be effective on the fixed server.
--echo #
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
FLUSH BINARY LOGS;
--source include/show_binary_logs.inc
--echo # Let the files age past binlog_expire_logs_seconds: purge_logs_before_date
--echo # skips can_purge_log() for any file whose mtime is not older than that.
--real_sleep 2
SET @@global.binlog_expire_logs_seconds= 1;
--echo # Rotating attempts an automatic purge, which the guard refuses. That
--echo # refusal is what populates the cache.
FLUSH BINARY LOGS;
--let $first= query_get_value(SHOW BINARY LOGS, Log_name, 1)
if ($first != master-bin.000003)
{
--echo # Setup failed: expected the automatic purge to delete nothing.
--die The automatic purge was not refused, so nothing was cached
}
--echo # The automatic refusal states the correct reason in the error log:
--let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_PATTERN= Binary log .master-bin.000003. is not purged because less than .slave_connections_needed_for_purge. slaves have processed it
--source include/search_pattern_in_file.inc
--echo # Now the interactive purge. It should behave exactly as in part 1.
PURGE BINARY LOGS TO 'master-bin.000006';
--echo # On affected versions this reports master-bin.000003 as the current active
--echo # binlog, while master-bin.000008 is active:
SHOW WARNINGS;
--source include/show_binary_logs.inc
--evalp SET @@global.binlog_expire_logs_seconds = $save_binlog_expire_logs_seconds
--evalp SET @@global.max_binlog_total_size= $save_max_binlog_total_size
--evalp SET @@global.slave_connections_needed_for_purge= $save_slave_connections_needed_for_purge
RESET MASTER;

View File

@@ -3545,6 +3545,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
select distinct 1, row_number() over (order by 1) from t1 where a=0 group by a with rollup;
1 row_number() over (order by 1)
1 1
drop table t1;
explain
SELECT DISTINCT BIT_OR(100) OVER () FROM dual

View File

@@ -0,0 +1,115 @@
connection node_2;
connection node_1;
#
# Each pair below is two distinct values whose bytes fold to the same
# latin1_swedish_ci weight string (0xC0->0x41, 0x6A->0x4A, 0x6B->0x4B,
# 0xD2->0x4F, 0x7A->0x5A). A UUID is stored as a byte permutation of the
# value shown, which does not affect a byte-wise folding.
#
SELECT WEIGHT_STRING(_latin1 0xC0A8016B9E4D4F7AB3D26A1E8F9C0D1B) =
WEIGHT_STRING(_latin1 0x41A8014B9E4D4F5AB34F4A1E8F9C0D1B) AS uuid_pair_collides,
WEIGHT_STRING(_latin1 0x20010DB80000000000000000C0A8016B) =
WEIGHT_STRING(_latin1 0x20010DB8000000000000000041A8014B) AS inet6_pair_collides,
WEIGHT_STRING(_latin1 0xC0A8016B) =
WEIGHT_STRING(_latin1 0x41A8014B) AS inet4_pair_collides;
uuid_pair_collides inet6_pair_collides inet4_pair_collides
1 1 1
#
# UUID
#
connection node_1;
CREATE TABLE t (id UUID PRIMARY KEY, v INT) ENGINE=InnoDB;
INSERT INTO t VALUES ('c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b', 0), ('41a8014b-9e4d-4f5a-b34f-4a1e8f9c0d1b', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
UPDATE t SET v = 1 WHERE id = 'c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b';
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE t SET v = 2 WHERE id = '41a8014b-9e4d-4f5a-b34f-4a1e8f9c0d1b';
connection node_2;
no_false_dependency
1
SELECT COUNT(*) = 1 AS second_row_applied_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
second_row_applied_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT id, v FROM t ORDER BY v;
id v
c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b 1
41a8014b-9e4d-4f5a-b34f-4a1e8f9c0d1b 2
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE t;
#
# INET6
#
connection node_1;
CREATE TABLE t (id INET6 PRIMARY KEY, v INT) ENGINE=InnoDB;
INSERT INTO t VALUES ('2001:db8::c0a8:16b', 0), ('2001:db8::41a8:14b', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
UPDATE t SET v = 1 WHERE id = '2001:db8::c0a8:16b';
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE t SET v = 2 WHERE id = '2001:db8::41a8:14b';
connection node_2;
no_false_dependency
1
SELECT COUNT(*) = 1 AS second_row_applied_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
second_row_applied_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT id, v FROM t ORDER BY v;
id v
2001:db8::c0a8:16b 1
2001:db8::41a8:14b 2
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE t;
#
# INET4
#
connection node_1;
CREATE TABLE t (id INET4 PRIMARY KEY, v INT) ENGINE=InnoDB;
INSERT INTO t VALUES ('192.168.1.107', 0), ('65.168.1.75', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
UPDATE t SET v = 1 WHERE id = '192.168.1.107';
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE t SET v = 2 WHERE id = '65.168.1.75';
connection node_2;
no_false_dependency
1
SELECT COUNT(*) = 1 AS second_row_applied_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
second_row_applied_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT id, v FROM t ORDER BY v;
id v
192.168.1.107 1
65.168.1.75 2
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE t;

View File

@@ -0,0 +1,104 @@
connection node_2;
connection node_1;
#
# UUID
#
connection node_1;
CREATE TABLE p (id UUID PRIMARY KEY, v INT) ENGINE=InnoDB;
CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY, p_id UUID NOT NULL, KEY k (p_id), FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB;
INSERT INTO p VALUES ('c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
INSERT INTO c (p_id) VALUES ('c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b');
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE p SET v = v + 1 WHERE id = 'c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b';
connection node_2;
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
no_row_modified_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT v FROM p;
v
1
SELECT COUNT(*) FROM c;
COUNT(*)
1
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE c, p;
#
# INET6
#
connection node_1;
CREATE TABLE p (id INET6 PRIMARY KEY, v INT) ENGINE=InnoDB;
CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY, p_id INET6 NOT NULL, KEY k (p_id), FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB;
INSERT INTO p VALUES ('2001:db8::c0a8:16b', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
INSERT INTO c (p_id) VALUES ('2001:db8::c0a8:16b');
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE p SET v = v + 1 WHERE id = '2001:db8::c0a8:16b';
connection node_2;
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
no_row_modified_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT v FROM p;
v
1
SELECT COUNT(*) FROM c;
COUNT(*)
1
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE c, p;
#
# INET4
#
connection node_1;
CREATE TABLE p (id INET4 PRIMARY KEY, v INT) ENGINE=InnoDB;
CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY, p_id INET4 NOT NULL, KEY k (p_id), FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB;
INSERT INTO p VALUES ('192.168.1.107', 0);
connection node_2;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
INSERT INTO c (p_id) VALUES ('192.168.1.107');
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE p SET v = v + 1 WHERE id = '192.168.1.107';
connection node_2;
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
no_row_modified_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT v FROM p;
v
1
SELECT COUNT(*) FROM c;
COUNT(*)
1
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE c, p;

View File

@@ -0,0 +1,37 @@
connection node_2;
connection node_1;
connection node_1;
CREATE TABLE p (id CHAR(5) CHARACTER SET utf8mb4 PRIMARY KEY, v INT) ENGINE=InnoDB;
CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY,
p_id CHAR(5) CHARACTER SET utf8mb4 NOT NULL,
KEY k (p_id),
FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB;
INSERT INTO p VALUES (_utf8mb4 0xC3A9, 0);
connection node_2;
SET SESSION wsrep_sync_wait = 1;
SET SESSION wsrep_sync_wait = 0;
SET GLOBAL wsrep_slave_threads = 2;
SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync';
connection node_1;
INSERT INTO c (p_id) VALUES (_utf8mb4 0xC3A9);
connection node_2;
SET SESSION wsrep_on = 0;
SET SESSION wsrep_on = 1;
SET GLOBAL wsrep_provider_options = 'dbug=';
connection node_1;
UPDATE p SET v = v + 1 WHERE id = _utf8mb4 0xC3A9;
connection node_2;
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
no_row_modified_in_parallel
1
SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync';
SELECT v FROM p;
v
1
SELECT COUNT(*) FROM c;
COUNT(*)
1
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
connection node_1;
DROP TABLE c, p;

View File

@@ -0,0 +1,19 @@
connection node_2;
connection node_1;
connection node_2;
SET GLOBAL wsrep_slave_threads = 2;
connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2;
SET SESSION wsrep_sync_wait = 0;
# Shape 1: CHAR(36) utf8mb4
# Shape 2: CHAR(36) utf8mb3
# Shape 3: CHAR(36) utf8mb4, four byte characters
# Shape 4: CHAR(36) utf8mb4_bin
# Shape 5: CHAR(36) utf8mb4, ROW_FORMAT=REDUNDANT
# Shape 6: CHAR(36) utf16
# Shape 7: CHAR(36) ucs2
# Shape 8: CHAR(36) latin1
# Shape 9: CHAR(36) utf8mb4, ASCII value
# Shape 10: VARCHAR(36) utf8mb4
connection node_2;
SET GLOBAL wsrep_slave_threads = DEFAULT;
disconnect node_2a;

View File

@@ -227,3 +227,21 @@ customer_id customer_name store_id bill_number bill_date amount
8 Oscar S012 125 2019-07-24 300.00
9 Darryl S456 119 2019-08-02 492.20
DROP TABLE sales;
connection node_1;
CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1,0),(2,0),(3,0);
CREATE TABLE t2 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB PARTITION BY LIST (a MOD 2) (PARTITION p0 VALUES IN (0), PARTITION p1 VALUES IN (1)) AS SELECT * FROM t1;
SELECT COUNT(*) FROM t2;
COUNT(*)
3
connection node_2;
connection node_1;
DROP TABLE t1, t2;
CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB;
CREATE TABLE t2 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB PARTITION BY LIST (a MOD 2) (PARTITION p0 VALUES IN (0), PARTITION p1 VALUES IN (1)) AS SELECT * FROM t1;
SELECT COUNT(*) FROM t2;
COUNT(*)
0
connection node_2;
connection node_1;
DROP TABLE t1, t2;

View File

@@ -20,6 +20,8 @@ MIN(f1) >= 1000 COUNT(*) = 20 COUNT(DISTINCT f1) = 20
1 1 1
connection node_1;
ALTER TABLE t1 AUTO_INCREMENT = 5;
Warnings:
Note 4190 ALTER TABLE is implicitly changing the value of 'AUTO_INCREMENT' from '5' to '1039'
INSERT INTO t1 (f2) SELECT 1 FROM ten;
connection node_2;
INSERT INTO t1 (f2) SELECT 1 FROM ten;

View File

@@ -1,5 +1,6 @@
connection node_2;
connection node_1;
# Correct Galera library found
connection node_1;
connection node_2;
connection node_2;

View File

@@ -0,0 +1,67 @@
#
# Body of galera.MDEV-41012-collision, parameterized by the data type.
#
# $fbt_type - data type of the primary key column
# $fbt_value1 - a literal of that type
# $fbt_value2 - a *different* literal whose binary representation folds to
# the same latin1_swedish_ci weight string as $fbt_value1
#
--connection node_1
--eval CREATE TABLE t (id $fbt_type PRIMARY KEY, v INT) ENGINE=InnoDB
--eval INSERT INTO t VALUES ($fbt_value1, 0), ($fbt_value2, 0)
--connection node_2
SET SESSION wsrep_sync_wait = 0;
--let $wait_condition = SELECT COUNT(*) = 2 FROM t
--source include/wait_condition.inc
SET GLOBAL wsrep_slave_threads = 2;
# Park the applier of the first UPDATE on entry to the apply monitor.
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_set_sync_point.inc
--connection node_1
--eval UPDATE t SET v = 1 WHERE id = $fbt_value1
--connection node_2
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_wait_sync_point.inc
# Disarm, so the second applier runs into the real dependency check.
--source include/galera_clear_sync_point.inc
--let $apply_waits = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits'`
--connection node_1
--eval UPDATE t SET v = 2 WHERE id = $fbt_value2
--connection node_2
# The two statements modify two *different* rows, so their
# certification keys must differ, there must be no dependency between the two
# writesets, and the second applier must be free to apply its row while the
# first one is still parked.
--let $wait_condition = SELECT (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits') > $apply_waits OR EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0)
--source include/wait_condition.inc
# While the key was collated with latin1_swedish_ci the two keys were equal,
# the second writeset was made to depend on the first, and both of these
# reported 0.
--disable_query_log
--eval SELECT VARIABLE_VALUE = $apply_waits AS no_false_dependency FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits'
--enable_query_log
SELECT COUNT(*) = 1 AS second_row_applied_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_signal_sync_point.inc
--let $wait_condition = SELECT SUM(v) = 3 FROM t
--source include/wait_condition.inc
SELECT id, v FROM t ORDER BY v;
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
--connection node_1
DROP TABLE t;

View File

@@ -0,0 +1,64 @@
#
# MDEV-41012: certification key collision between distinct values of a fixed
# binary data type (UUID, INET6, INET4).
#
# Testing key collisions with data types on Field_fbt: UUID, INET6 and INET4,
# where different keys could potentially generate same certification keys.
# i.e. a scenario where two transactions modifying two *different*
# rows certified as a conflict and the later one was aborted with a spurious
# ER_LOCK_DEADLOCK.
#
# A shared key also gives the two writesets a false dependency on each other,
# which serializes applying that should have run in parallel. The test detects
# the collision that way, because it can be driven deterministically. Whether
# the spurious ER_LOCK_DEADLOCK is observed instead depends on whether the
# first writeset has already been applied by the time the second transaction
# replicates, which is a race.
#
# This needs no foreign key: it is the row key path,
# wsrep_store_key_val_for_row(), alone.
#
# This is the second half of MDEV-41012. The other half, a key *mismatch* for
# one and the same row, is covered by galera.MDEV-41012.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/galera_have_debug_sync.inc
--echo #
--echo # Each pair below is two distinct values whose bytes fold to the same
--echo # latin1_swedish_ci weight string (0xC0->0x41, 0x6A->0x4A, 0x6B->0x4B,
--echo # 0xD2->0x4F, 0x7A->0x5A). A UUID is stored as a byte permutation of the
--echo # value shown, which does not affect a byte-wise folding.
--echo #
SELECT WEIGHT_STRING(_latin1 0xC0A8016B9E4D4F7AB3D26A1E8F9C0D1B) =
WEIGHT_STRING(_latin1 0x41A8014B9E4D4F5AB34F4A1E8F9C0D1B) AS uuid_pair_collides,
WEIGHT_STRING(_latin1 0x20010DB80000000000000000C0A8016B) =
WEIGHT_STRING(_latin1 0x20010DB8000000000000000041A8014B) AS inet6_pair_collides,
WEIGHT_STRING(_latin1 0xC0A8016B) =
WEIGHT_STRING(_latin1 0x41A8014B) AS inet4_pair_collides;
--echo #
--echo # UUID
--echo #
--let $fbt_type = UUID
--let $fbt_value1 = 'c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b'
--let $fbt_value2 = '41a8014b-9e4d-4f5a-b34f-4a1e8f9c0d1b'
--source MDEV-41012-collision.inc
--echo #
--echo # INET6
--echo #
--let $fbt_type = INET6
--let $fbt_value1 = '2001:db8::c0a8:16b'
--let $fbt_value2 = '2001:db8::41a8:14b'
--source MDEV-41012-collision.inc
--echo #
--echo # INET4
--echo #
--let $fbt_type = INET4
--let $fbt_value1 = '192.168.1.107'
--let $fbt_value2 = '65.168.1.75'
--source MDEV-41012-collision.inc

View File

@@ -0,0 +1,67 @@
#
# Body of galera.MDEV-41012, parameterized by the data type of the foreign key.
#
# $fbt_type - data type of the parent PK / child FK column
# $fbt_value - a literal of that type whose binary representation contains
# bytes that latin1_swedish_ci folds (0xC0, 0x6A, 0x6B, 0xD2 ...)
#
--connection node_1
--eval CREATE TABLE p (id $fbt_type PRIMARY KEY, v INT) ENGINE=InnoDB
--eval CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY, p_id $fbt_type NOT NULL, KEY k (p_id), FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB
--eval INSERT INTO p VALUES ($fbt_value, 0)
--connection node_2
SET SESSION wsrep_sync_wait = 0;
--let $wait_condition = SELECT COUNT(*) = 1 FROM p
--source include/wait_condition.inc
# Two appliers, so that the two writesets below *can* be applied in parallel
# if certification does not make them depend on each other.
SET GLOBAL wsrep_slave_threads = 2;
# Park the first applier on entry to the apply monitor, i.e. before the child
# INSERT is applied and before its FK check locks the parent row.
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_set_sync_point.inc
--connection node_1
--eval INSERT INTO c (p_id) VALUES ($fbt_value)
--connection node_2
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_wait_sync_point.inc
# clear the sync point. The second applier must run into the real dependency
--source include/galera_clear_sync_point.inc
--let $apply_waits = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits'`
--connection node_1
--eval UPDATE p SET v = v + 1 WHERE id = $fbt_value
--connection node_2
# Wait until the second applier has blocked in the apply monitor because
--let $wait_condition = SELECT (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits') > $apply_waits
--source include/wait_condition.inc
# the second applier waited instead of applying it in parallel.
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
# Release the child INSERT
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_signal_sync_point.inc
--let $wait_condition = SELECT COUNT(*) = 1 FROM c
--source include/wait_condition.inc
--let $wait_condition = SELECT v = 1 FROM p
--source include/wait_condition.inc
SELECT v FROM p;
SELECT COUNT(*) FROM c;
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
--connection node_1
DROP TABLE c, p;

View File

@@ -0,0 +1,40 @@
#
# MDEV-41012: BF-BF conflict when applying a child INSERT and a parent UPDATE
# in parallel, for a foreign key on a fixed binary data type.
#
# The test sends two write sets having access with the same primary key of fixed
# binary data type, the transactions are:
# * INSERT into the child table havinga reference key for the parent row
# * UPDATE of the parent row having the parent's primary key,
#
# The test orders appliers so that the child INSERT is parked before it
# is applied, the parent UPDATE is replicated after it, and we verify that the
# UPDATE has to wait for the INSERT instead of being applied in parallel.
#
# All three Field_fbt types (UUID, INET4 and INET6) are covered.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/galera_have_debug_sync.inc
--echo #
--echo # UUID
--echo #
--let $fbt_type = UUID
--let $fbt_value = 'c0a8016b-9e4d-4f7a-b3d2-6a1e8f9c0d1b'
--source MDEV-41012.inc
--echo #
--echo # INET6
--echo #
--let $fbt_type = INET6
--let $fbt_value = '2001:db8::c0a8:16b'
--source MDEV-41012.inc
--echo #
--echo # INET4
--echo #
--let $fbt_type = INET4
--let $fbt_value = '192.168.1.107'
--source MDEV-41012.inc

View File

@@ -0,0 +1,94 @@
#
# A foreign key on a CHAR column of a variable length character set.
#
# The key of the child row is built from the MySQL record by
# wsrep_store_key_val_for_row() and the key of the parent row from the InnoDB
# record by wsrep_rec_get_foreign_key(). A CHAR is not space padded the same
# way in the two formats: the MySQL record pads it to n_chars * mbmaxlen
# bytes, while InnoDB strips that padding down to, but not below, n_chars
# bytes. As that compares a byte count with a character count, a value that
# holds a multi byte character and is shorter than the column ended up with a
# different number of characters on the two paths, the two keys differed, the
# child INSERT got no dependency on the parent row and the appliers were free
# to run it in parallel with a change of that very row.
#
# The value used below is one two byte character in a five character column,
# so it is padded, which is what used to make the two encodings diverge. It
# is written as a hex literal so that the test does not depend on the
# character set of the client connection.
#
# The test parks the applier of the child INSERT and replicates an UPDATE of
# the parent row after it, then verifies that the UPDATE had to wait.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/galera_have_debug_sync.inc
--connection node_1
CREATE TABLE p (id CHAR(5) CHARACTER SET utf8mb4 PRIMARY KEY, v INT) ENGINE=InnoDB;
CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY,
p_id CHAR(5) CHARACTER SET utf8mb4 NOT NULL,
KEY k (p_id),
FOREIGN KEY (p_id) REFERENCES p (id)) ENGINE=InnoDB;
INSERT INTO p VALUES (_utf8mb4 0xC3A9, 0);
--connection node_2
# Catch up with node_1 with a causal read before causal reads are turned off.
SET SESSION wsrep_sync_wait = 1;
--disable_query_log
--disable_result_log
SELECT COUNT(*) FROM p;
--enable_result_log
--enable_query_log
SET SESSION wsrep_sync_wait = 0;
# Two appliers, so that the two writesets below *can* be applied in parallel
# if certification does not make them depend on each other.
SET GLOBAL wsrep_slave_threads = 2;
# Park the first applier on entry to the apply monitor, i.e. before the child
# INSERT is applied and before its FK check locks the parent row.
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_set_sync_point.inc
--connection node_1
INSERT INTO c (p_id) VALUES (_utf8mb4 0xC3A9);
--connection node_2
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_wait_sync_point.inc
# Clear the sync point, the second applier must run into the real dependency.
--source include/galera_clear_sync_point.inc
--let $apply_waits = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits'`
--connection node_1
UPDATE p SET v = v + 1 WHERE id = _utf8mb4 0xC3A9;
--connection node_2
# Wait until the second applier has blocked in the apply monitor because
--let $wait_condition = SELECT (SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_apply_waits') > $apply_waits
--source include/wait_condition.inc
# the second applier waited instead of applying it in parallel.
SELECT COUNT(*) = 0 AS no_row_modified_in_parallel FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_rows_modified > 0;
# Release the child INSERT
--let $galera_sync_point = apply_monitor_slave_enter_sync
--source include/galera_signal_sync_point.inc
--let $wait_condition = SELECT COUNT(*) = 1 FROM c;
--source include/wait_condition.inc
--let $wait_condition = SELECT v = 1 FROM p;
--source include/wait_condition.inc
SELECT v FROM p;
SELECT COUNT(*) FROM c;
SET GLOBAL wsrep_slave_threads = DEFAULT;
SET SESSION wsrep_sync_wait = DEFAULT;
--connection node_1
DROP TABLE c, p;

View File

@@ -0,0 +1,130 @@
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_sequence.inc
--connection node_2
SET GLOBAL wsrep_slave_threads = 2;
--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2
SET SESSION wsrep_sync_wait = 0;
--let $shape = 0
while ($shape < 10)
{
--inc $shape
--let $opt = ENGINE=InnoDB
if ($shape == 1)
{
--let $name = CHAR(36) utf8mb4
--let $col = CHAR(36) CHARACTER SET utf8mb4
--let $val = _utf8mb4 0xC3A9C3A9C3A9C3A9C3A9
}
if ($shape == 2)
{
--let $name = CHAR(36) utf8mb3
--let $col = CHAR(36) CHARACTER SET utf8mb3
--let $val = _utf8mb3 0xC3A9C3A9C3A9C3A9C3A9
}
if ($shape == 3)
{
--let $name = CHAR(36) utf8mb4, four byte characters
--let $col = CHAR(36) CHARACTER SET utf8mb4
--let $val = _utf8mb4 0xF09F9880F09F9880F09F9880F09F9880F09F9880
}
if ($shape == 4)
{
--let $name = CHAR(36) utf8mb4_bin
--let $col = CHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin
--let $val = _utf8mb4 0xC3A9C3A9C3A9C3A9C3A9
}
if ($shape == 5)
{
--let $name = CHAR(36) utf8mb4, ROW_FORMAT=REDUNDANT
--let $col = CHAR(36) CHARACTER SET utf8mb4
--let $val = _utf8mb4 0xC3A9C3A9C3A9C3A9C3A9
--let $opt = ENGINE=InnoDB ROW_FORMAT=REDUNDANT
}
if ($shape == 6)
{
--let $name = CHAR(36) utf16
--let $col = CHAR(36) CHARACTER SET utf16
--let $val = _utf16 0x00E900E900E900E900E9
}
if ($shape == 7)
{
--let $name = CHAR(36) ucs2
--let $col = CHAR(36) CHARACTER SET ucs2
--let $val = _ucs2 0x00E900E900E900E900E9
}
if ($shape == 8)
{
--let $name = CHAR(36) latin1
--let $col = CHAR(36) CHARACTER SET latin1
--let $val = _latin1 0xE9E9E9E9E9
}
if ($shape == 9)
{
--let $name = CHAR(36) utf8mb4, ASCII value
--let $col = CHAR(36) CHARACTER SET utf8mb4
--let $val = _utf8mb4 0x6162636465
}
if ($shape == 10)
{
--let $name = VARCHAR(36) utf8mb4
--let $col = VARCHAR(36) CHARACTER SET utf8mb4
--let $val = _utf8mb4 0xC3A9C3A9C3A9C3A9C3A9
}
--echo # Shape $shape: $name
--disable_connect_log
--connection node_1
--disable_query_log
--eval CREATE TABLE p (id $col PRIMARY KEY, v INT) $opt
--eval CREATE TABLE c (id INT AUTO_INCREMENT PRIMARY KEY, p_id $col NOT NULL, KEY k (p_id), FOREIGN KEY (p_id) REFERENCES p (id)) $opt
--eval CREATE TABLE f (id INT AUTO_INCREMENT PRIMARY KEY, pad CHAR(255)) $opt
--eval INSERT INTO p VALUES ($val, 0)
--enable_query_log
--enable_connect_log
--let $i = 0
--let $round = 10
while ($round)
{
--inc $i
--disable_connect_log
--connection node_1
--disable_query_log
BEGIN;
INSERT INTO f (pad) SELECT REPEAT('x',255) FROM seq_1_to_2000;
--eval INSERT INTO c (p_id) VALUES ($val)
COMMIT;
--eval UPDATE p SET v = v + 1 WHERE id = $val
--enable_query_log
--connection node_2a
--let $wait_timeout = 30
--let $wait_condition = SELECT COUNT(*) = $i FROM c
--source include/wait_condition.inc
--enable_connect_log
if (!$success)
{
SELECT ID, STATE, INFO FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user';
SELECT trx_id, trx_state, trx_mysql_thread_id, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX;
--die Node 2 stopped applying. The two appliers are deadlocked on the foreign key parent row.
}
--dec $round
}
--disable_connect_log
--connection node_1
--disable_query_log
DROP TABLE c, p, f;
--enable_query_log
--enable_connect_log
}
--connection node_2
SET GLOBAL wsrep_slave_threads = DEFAULT;
--disconnect node_2a

View File

@@ -167,4 +167,40 @@ SELECT * FROM sales;
SET GLOBAL wsrep_mode=DEFAULT;
--connection node_2
SELECT * FROM sales;
DROP TABLE sales;
DROP TABLE sales;
#
# MDEV-40929 : CREATE TABLE ... AS SELECT is not replicated in Galera when the table is partitioned
#
--connection node_1
CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1,0),(2,0),(3,0);
CREATE TABLE t2 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB PARTITION BY LIST (a MOD 2) (PARTITION p0 VALUES IN (0), PARTITION p1 VALUES IN (1)) AS SELECT * FROM t1;
SELECT COUNT(*) FROM t2;
--connection node_2
--let $c= query_get_value(SELECT COUNT(*) AS c FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2', c, 1)
if ($c != 1) {
--die The new partitioned table exists on node 1 but not on node 2. It was not replicated to the cluster.
}
--let $c= query_get_value(SELECT COUNT(*) AS c FROM t2, c, 1)
if ($c != 3) {
--die The new partitioned table on node 2 is missing rows. The rows selected on node 1 were not replicated to the cluster.
}
--connection node_1
DROP TABLE t1, t2;
CREATE TABLE t1 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB;
CREATE TABLE t2 (a INT NOT NULL, b INT NOT NULL, PRIMARY KEY (a,b)) ENGINE=InnoDB PARTITION BY LIST (a MOD 2) (PARTITION p0 VALUES IN (0), PARTITION p1 VALUES IN (1)) AS SELECT * FROM t1;
SELECT COUNT(*) FROM t2;
--connection node_2
--let $c= query_get_value(SELECT COUNT(*) AS c FROM information_schema.tables WHERE table_schema = 'test' AND table_name = 't2', c, 1)
if ($c != 1) {
--die The new partitioned table exists on node 1 but not on node 2, with an empty source table. It was not replicated to the cluster.
}
--connection node_1
DROP TABLE t1, t2;

View File

@@ -8,6 +8,10 @@
# This is needed because we use wsrep-new-cluster
--source include/have_perfschema.inc
# Make sure that the test is operating on the right version of galera library.
--let $galera_version=26.4.28
source ../wsrep/include/check_galera_version.inc;
# Save original auto_increment_offset values.
--let $node_1=node_1
--let $node_2=node_2
@@ -86,6 +90,11 @@ SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME
--echo Starting server ...
--let $start_mysqld_params=
# Rejoining with an emptied datadir requires a full SST. That is much slower
# than a plain restart and on a loaded machine it does not finish within the
# default 60 seconds, which used to abort the test here while the SST was
# still running.
--let $galera_wait_ready_timeout=300
--source include/start_mysqld.inc
--source include/wait_until_ready.inc

View File

@@ -0,0 +1,28 @@
# Assert that purge has not freed the BLOB while a version-chain walk is parked
# at the dereference of its reference. Purging an update record is what would
# free it; the caller must have enabled and reset that monitor, the counter
# being cumulative over the lifetime of the server.
#
# The wait gives purge every chance to get there first, which is what makes the
# assertion fail rather than pass by accident; without the freeze it needs about
# a second. It cannot be a wait_condition, because a frozen purge_sys.view is
# precisely what stops every purge counter from moving.
--disable_query_log
let $purge_wait_rounds= 20;
while ($purge_wait_rounds)
{
let $freed= `SELECT count FROM information_schema.innodb_metrics WHERE name = 'purge_upd_exist_or_extern_records'`;
if ($freed)
{
let $purge_wait_rounds= 0;
}
if (!$freed)
{
real_sleep 0.1;
dec $purge_wait_rounds;
}
}
--enable_query_log
SELECT count AS upd_records_purged_while_parked FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';

View File

@@ -0,0 +1,46 @@
#
# MDEV-38056 An implicit-lock check must keep purge_sys.view frozen across
# the dereference of an externally stored column of a version it rebuilt.
# The transaction it references commits meanwhile, deregistering itself
# from trx_sys before it waits for the reader's reference, which makes its
# history purgeable under the reader.
#
InnoDB 0 transactions not purged
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('x', REPEAT('x', @@innodb_page_size)));
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
BEGIN;
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
UPDATE t SET b = CONCAT('z', REPEAT('z', @@innodb_page_size)) WHERE a = 1;
connect reader,localhost,root,,;
SET DEBUG_SYNC='row_vers_impl_x_locked_row_build SIGNAL parked WAIT_FOR resume';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
COMMIT;
connect con3,localhost,root,,;
SELECT count AS upd_records_purged_while_parked FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
upd_records_purged_while_parked
0
SET DEBUG_SYNC='now SIGNAL resume';
connection reader;
a
1
connection default;
disconnect reader;
connection con3;
connection default;
disconnect con3;
SET DEBUG_SYNC='RESET';
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;

View File

@@ -0,0 +1,57 @@
#
# MDEV-38056 CHECK TABLE ... EXTENDED must keep purge_sys.view frozen
# across the dereference of an externally stored column of a version it
# rebuilt. It reaches versions that purge_sys.view already permits to be
# freed, deciding reachability from the lagging purge_sys.end_view.
#
InnoDB 0 transactions not purged
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('z', REPEAT('z', @@innodb_page_size)));
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
connect victim,localhost,root,,;
BEGIN;
UPDATE t SET b = CONCAT('x', REPEAT('x', @@innodb_page_size)) WHERE a = 1;
connect checker,localhost,root,,;
SET DEBUG_SYNC='row_check_index_extended_match SIGNAL parked WAIT_FOR resume EXECUTE 2';
CHECK TABLE t EXTENDED;
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
disconnect purge_control;
connection default;
SELECT count AS upd_records_purged_while_parked FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
upd_records_purged_while_parked
0
SET DEBUG_SYNC='now SIGNAL resume';
connection checker;
Table Op Msg_type Msg_text
test.t check status OK
disconnect checker;
connection victim;
COMMIT;
connection default;
disconnect victim;
SET DEBUG_SYNC='RESET';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'xx%';
a
1
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;

View File

@@ -0,0 +1,90 @@
#
# MDEV-38056 Each of the three walks that freeze purge_sys.view across the
# dereference of an externally stored column re-establishes under the
# freeze that the version it rebuilt is still reachable. A debug keyword
# forces each of those tests to fail, so that the exit it guards is
# reachable without the race that produces it.
#
InnoDB 0 transactions not purged
SET @old_dbug= @@SESSION.debug_dbug;
#
# row_vers_impl_x_locked_purgeable: report no implicit lock.
#
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, REPEAT('x', @@innodb_page_size));
BEGIN;
UPDATE t SET b = REPEAT('y', @@innodb_page_size) WHERE a = 1;
UPDATE t SET b = REPEAT('z', @@innodb_page_size) WHERE a = 1;
connect reader,localhost,root,,;
SET @old_dbug= @@SESSION.debug_dbug;
SET SESSION debug_dbug='+d,row_vers_impl_x_locked_purgeable';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE SKIP LOCKED;
a
SET SESSION debug_dbug= @old_dbug;
connection default;
COMMIT;
connection reader;
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE SKIP LOCKED;
a
1
COMMIT;
connection default;
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;
#
# row_undo_mod_sec_is_unsafe_purgeable: report the secondary index entry
# as still needed, which only makes the rollback delete-mark an entry it
# could have removed.
#
CREATE TABLE t(a INT PRIMARY KEY, pad INT, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, 0, REPEAT('x', @@innodb_page_size));
UPDATE t SET pad = 1 WHERE a = 1;
UPDATE t SET b = REPEAT('y', @@innodb_page_size) WHERE a = 1;
BEGIN;
UPDATE t SET b = REPEAT('z', @@innodb_page_size) WHERE a = 1;
SET SESSION debug_dbug='+d,row_undo_mod_sec_is_unsafe_purgeable';
ROLLBACK;
SET SESSION debug_dbug= @old_dbug;
SELECT a, pad, LEFT(b, 2) FROM t FORCE INDEX(b) WHERE b LIKE 'yy%';
a pad LEFT(b, 2)
1 1 yy
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;
#
# row_check_index_purgeable: stop the walk and report nothing.
#
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, REPEAT('x', @@innodb_page_size));
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
UPDATE t SET b = 's' WHERE a = 1;
connect victim,localhost,root,,;
BEGIN;
UPDATE t SET b = 'q' WHERE a = 1;
connection default;
SET SESSION debug_dbug='+d,row_check_index_purgeable';
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
SET SESSION debug_dbug= @old_dbug;
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
connection victim;
ROLLBACK;
disconnect purge_control;
connection default;
disconnect reader;
disconnect victim;
CHECK TABLE t EXTENDED;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;

View File

@@ -0,0 +1,54 @@
#
# MDEV-38056 A rollback must keep purge_sys.view frozen across the
# dereference of an externally stored column of a version it rebuilt. It
# froze that view only inside trx_undo_prev_version_build(), so purge was
# free to release the BLOB pages before the caller fetched them.
#
InnoDB 0 transactions not purged
CREATE TABLE t(a INT PRIMARY KEY, pad INT, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, 0, CONCAT('x', REPEAT('x', @@innodb_page_size)));
INSERT INTO t
SELECT 100 + seq, 0, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
UPDATE t SET pad = 1 WHERE a = 1;
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
connect victim,localhost,root,,;
BEGIN;
UPDATE t SET b = CONCAT('z', REPEAT('z', @@innodb_page_size)) WHERE a = 1;
SET DEBUG_SYNC='row_undo_mod_sec_is_unsafe_row_build SIGNAL parked WAIT_FOR resume EXECUTE 3';
ROLLBACK;
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
disconnect purge_control;
connection default;
SELECT count AS upd_records_purged_while_parked FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
upd_records_purged_while_parked
0
SET DEBUG_SYNC='now SIGNAL resume';
connection victim;
connection default;
disconnect victim;
SET DEBUG_SYNC='RESET';
SELECT a, pad FROM t FORCE INDEX(b) WHERE b LIKE 'yy%';
a pad
1 1
CHECK TABLE t;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;

View File

@@ -0,0 +1,72 @@
#
# MDEV-38056 As innodb.old_blob, but the version is rebuilt from the undo
# log record of an update of a record that another, committed transaction
# delete-marked. Such a record stores only the 20-byte reference, so the
# frozen purge_sys.view is the only thing that can keep the dereference
# valid. Reaching that version needs a batch to be still in progress.
#
InnoDB 0 transactions not purged
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('x', REPEAT('x', @@innodb_page_size)));
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
SET GLOBAL innodb_monitor_reset_all='purge_del_mark_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_del_mark_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
connect purge_control,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
DELETE FROM t WHERE a = 1;
connect victim,localhost,root,,;
BEGIN;
INSERT INTO t VALUES (1, CONCAT('z', REPEAT('z', @@innodb_page_size)));
connection default;
SET @old_dbug= @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug='+d,purge_hold_cleanup';
disconnect purge_control;
connection default;
SET DEBUG_SYNC='now WAIT_FOR purge_batch_parked TIMEOUT 60';
SELECT count > 0 AS del_mark_records_purged
FROM information_schema.innodb_metrics WHERE name = 'purge_del_mark_records';
del_mark_records_purged
1
connect reader,localhost,root,,;
SET DEBUG_SYNC='row_vers_impl_x_locked_row_build SIGNAL parked WAIT_FOR resume';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET GLOBAL debug_dbug= @old_dbug;
SET DEBUG_SYNC='now SIGNAL purge_batch_resume';
connection victim;
COMMIT;
connection default;
SELECT count AS upd_records_purged_while_parked FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
upd_records_purged_while_parked
0
SET DEBUG_SYNC='now SIGNAL resume';
connection victim;
connection reader;
a
1
connection default;
disconnect reader;
disconnect victim;
SET DEBUG_SYNC='RESET';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'zz%';
a
1
CHECK TABLE t;
Table Op Msg_type Msg_text
test.t check status OK
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_del_mark_records';
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_del_mark_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;

View File

@@ -0,0 +1,75 @@
--source include/have_innodb.inc
--source include/have_innodb_max_16k.inc
--source include/have_debug_sync.inc
--source include/have_sequence.inc
--echo #
--echo # MDEV-38056 An implicit-lock check must keep purge_sys.view frozen across
--echo # the dereference of an externally stored column of a version it rebuilt.
--echo # The transaction it references commits meanwhile, deregistering itself
--echo # from trx_sys before it waits for the reader's reference, which makes its
--echo # history purgeable under the reader.
--echo #
--source include/wait_all_purged.inc
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('x', REPEAT('x', @@innodb_page_size)));
# Filler rows keep the delete-marked entries off the leaf page holding the live
# 'z...' entry, which the reader latches; purge would block on it otherwise.
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
# The counter is cumulative over the lifetime of the server.
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
BEGIN;
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
UPDATE t SET b = CONCAT('z', REPEAT('z', @@innodb_page_size)) WHERE a = 1;
# The locking read references the still active transaction above and parks after
# building the previous version, before dereferencing its BLOB pointers.
connect (reader,localhost,root,,);
SET DEBUG_SYNC='row_vers_impl_x_locked_row_build SIGNAL parked WAIT_FOR resume';
--send SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
--send COMMIT
# Both old BLOBs must still be allocated while the reader is parked on them.
connect (con3,localhost,root,,);
--source suite/innodb/include/assert_blob_not_purged.inc
SET DEBUG_SYNC='now SIGNAL resume';
connection reader;
--reap
connection default;
--reap
disconnect reader;
# Once the reader is done, purge advances, which confirms that only the frozen
# view held it back.
connection con3;
let $wait_condition=
SELECT count >= 2 FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
--source include/wait_condition.inc
connection default;
disconnect con3;
SET DEBUG_SYNC='RESET';
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
--disable_warnings
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
--enable_warnings

View File

@@ -0,0 +1,96 @@
--source include/have_innodb.inc
--source include/have_innodb_max_16k.inc
--source include/have_debug_sync.inc
--source include/have_sequence.inc
--echo #
--echo # MDEV-38056 CHECK TABLE ... EXTENDED must keep purge_sys.view frozen
--echo # across the dereference of an externally stored column of a version it
--echo # rebuilt. It reaches versions that purge_sys.view already permits to be
--echo # freed, deciding reachability from the lagging purge_sys.end_view.
--echo #
--source include/wait_all_purged.inc
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('z', REPEAT('z', @@innodb_page_size)));
# Filler rows keep the delete-marked 'z...' entry off the first leaf page, which
# CHECK TABLE holds while parked; purge would block on it otherwise.
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
# The counter is cumulative over the lifetime of the server.
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
# Pin a read view on the 'z...' state, so that the BLOB the update below disowns
# stays allocated until we release it.
connect (purge_control,localhost,root,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
# Committed update that disowns the 'z...' BLOB, which purge will free.
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
# An active transaction keeps the newest version invisible to CHECK TABLE, which
# is what makes it descend the version chain at all.
connect (victim,localhost,root,,);
BEGIN;
UPDATE t SET b = CONCAT('x', REPEAT('x', @@innodb_page_size)) WHERE a = 1;
# While checking the live 'x...' entry, CHECK TABLE rebuilds the 'y...' and then
# the 'z...' version and compares the column prefix of each against it.
connect (checker,localhost,root,,);
SET DEBUG_SYNC='row_check_index_extended_match SIGNAL parked WAIT_FOR resume EXECUTE 2';
--send CHECK TABLE t EXTENDED
connection default;
# Hit 1: the 'y...' version, whose BLOB the active transaction above disowned and
# which is therefore not freeable.
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
# Hit 2: the 'z...' version, whose BLOB the committed update disowned. Release
# the read view, the last thing that kept that BLOB from being freeable. Purge
# has to remove the delete-marked 'z...' entry first, and that sits on the last
# leaf page while CHECK TABLE holds the first.
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
disconnect purge_control;
connection default;
--source suite/innodb/include/assert_blob_not_purged.inc
SET DEBUG_SYNC='now SIGNAL resume';
connection checker;
--reap
disconnect checker;
connection victim;
COMMIT;
connection default;
disconnect victim;
# Once CHECK TABLE is done, purge advances and frees that BLOB, which confirms
# that only the frozen view held it back.
let $wait_condition=
SELECT count >= 1 FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
--source include/wait_condition.inc
SET DEBUG_SYNC='RESET';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'xx%';
CHECK TABLE t EXTENDED;
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
--disable_warnings
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
--enable_warnings

View File

@@ -0,0 +1,111 @@
--source include/have_innodb.inc
--source include/have_debug.inc
--echo #
--echo # MDEV-38056 Each of the three walks that freeze purge_sys.view across the
--echo # dereference of an externally stored column re-establishes under the
--echo # freeze that the version it rebuilt is still reachable. A debug keyword
--echo # forces each of those tests to fail, so that the exit it guards is
--echo # reachable without the race that produces it.
--echo #
--source include/wait_all_purged.inc
SET @old_dbug= @@SESSION.debug_dbug;
--echo #
--echo # row_vers_impl_x_locked_purgeable: report no implicit lock.
--echo #
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, REPEAT('x', @@innodb_page_size));
BEGIN;
UPDATE t SET b = REPEAT('y', @@innodb_page_size) WHERE a = 1;
UPDATE t SET b = REPEAT('z', @@innodb_page_size) WHERE a = 1;
# The implicit lock check walks the versions of the row this transaction still
# holds, and row_vers_impl_x_locked_purgeable makes it answer that the row is
# unlocked. The reader skips the row anyway, the clustered index record carrying
# the same implicit lock, so the row stays protected.
connect (reader,localhost,root,,);
SET @old_dbug= @@SESSION.debug_dbug;
SET SESSION debug_dbug='+d,row_vers_impl_x_locked_purgeable';
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE SKIP LOCKED;
SET SESSION debug_dbug= @old_dbug;
connection default;
COMMIT;
connection reader;
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE SKIP LOCKED;
COMMIT;
connection default;
CHECK TABLE t EXTENDED;
DROP TABLE t;
--echo #
--echo # row_undo_mod_sec_is_unsafe_purgeable: report the secondary index entry
--echo # as still needed, which only makes the rollback delete-mark an entry it
--echo # could have removed.
--echo #
CREATE TABLE t(a INT PRIMARY KEY, pad INT, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, 0, REPEAT('x', @@innodb_page_size));
# The pad update produces a version that merely inherits the reference to the
# 'x...' BLOB, which is what the walk below has to dereference.
UPDATE t SET pad = 1 WHERE a = 1;
UPDATE t SET b = REPEAT('y', @@innodb_page_size) WHERE a = 1;
BEGIN;
UPDATE t SET b = REPEAT('z', @@innodb_page_size) WHERE a = 1;
SET SESSION debug_dbug='+d,row_undo_mod_sec_is_unsafe_purgeable';
ROLLBACK;
SET SESSION debug_dbug= @old_dbug;
SELECT a, pad, LEFT(b, 2) FROM t FORCE INDEX(b) WHERE b LIKE 'yy%';
CHECK TABLE t EXTENDED;
DROP TABLE t;
--echo #
--echo # row_check_index_purgeable: stop the walk and report nothing.
--echo #
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC;
INSERT INTO t VALUES (1, REPEAT('x', @@innodb_page_size));
# Keep the 'x...' version reachable, so that the walk gets to dereference it.
connect (purge_control,localhost,root,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
# Two short values, so that only the third version down stores its column
# externally, and the walk reaches the freeze only there. Stopping that late
# costs no count: a version that the check can see was rejected higher up, so
# the record was never going to be counted, and the check still says OK.
UPDATE t SET b = 's' WHERE a = 1;
connect (victim,localhost,root,,);
BEGIN;
UPDATE t SET b = 'q' WHERE a = 1;
connection default;
SET SESSION debug_dbug='+d,row_check_index_purgeable';
CHECK TABLE t EXTENDED;
SET SESSION debug_dbug= @old_dbug;
CHECK TABLE t EXTENDED;
connection victim;
ROLLBACK;
disconnect purge_control;
connection default;
disconnect reader;
disconnect victim;
CHECK TABLE t EXTENDED;
DROP TABLE t;

View File

@@ -0,0 +1,99 @@
--source include/have_innodb.inc
--source include/have_innodb_max_16k.inc
--source include/have_debug_sync.inc
--source include/have_sequence.inc
--echo #
--echo # MDEV-38056 A rollback must keep purge_sys.view frozen across the
--echo # dereference of an externally stored column of a version it rebuilt. It
--echo # froze that view only inside trx_undo_prev_version_build(), so purge was
--echo # free to release the BLOB pages before the caller fetched them.
--echo #
--source include/wait_all_purged.inc
CREATE TABLE t(a INT PRIMARY KEY, pad INT, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, 0, CONCAT('x', REPEAT('x', @@innodb_page_size)));
# Filler rows keep the delete-marked entries off the leaf page holding the live
# 'z...' entry, which the rollback latches; purge would block on it otherwise.
INSERT INTO t
SELECT 100 + seq, 0, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
# The counter is cumulative over the lifetime of the server.
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
# Pin a read view on the 'x...' state, so that the BLOB the updates below disown
# stays allocated until we release it.
connect (purge_control,localhost,root,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
# Committed update that does not mention b: the version it produces inherits
# whatever reference b holds in the next newer version.
UPDATE t SET pad = 1 WHERE a = 1;
# Committed update that disowns the 'x...' BLOB, which purge will free.
UPDATE t SET b = CONCAT('y', REPEAT('y', @@innodb_page_size)) WHERE a = 1;
connect (victim,localhost,root,,);
BEGIN;
UPDATE t SET b = CONCAT('z', REPEAT('z', @@innodb_page_size)) WHERE a = 1;
# On ROLLBACK the walk is 'z...' -> 'y...' -> 'x...' -> 'x...' inherited. Park
# before each of the three row_build() calls.
SET DEBUG_SYNC='row_undo_mod_sec_is_unsafe_row_build SIGNAL parked WAIT_FOR resume EXECUTE 3';
--send ROLLBACK
connection default;
# Hit 1: the 'y...' version, whose BLOB belongs to the rolling back
# transaction and is never freeable.
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
# Hit 2: the 'x...' version, rebuilt from the b update's undo log record. The
# read view still pins that BLOB, so there is nothing to observe here.
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
SET DEBUG_SYNC='now SIGNAL resume';
# Hit 3: the version the pad update produced. Its undo log record says nothing
# about b, so it merely inherits the reference that the b update disowned, and a
# frozen purge_sys.view is the only thing that can keep the dereference valid.
# Release the read view, the last thing that kept that BLOB from being freeable.
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
disconnect purge_control;
connection default;
--source suite/innodb/include/assert_blob_not_purged.inc
SET DEBUG_SYNC='now SIGNAL resume';
connection victim;
--reap
connection default;
disconnect victim;
# Once the rollback is over, purge advances past both committed updates, which
# confirms that only the frozen view held it back. Only the b update frees a
# BLOB, the pad update having disowned none.
let $wait_condition=
SELECT count >= 2 FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
--source include/wait_condition.inc
SET DEBUG_SYNC='RESET';
# The rollback reverted to the committed 'y...' value, and the indexes agree.
SELECT a, pad FROM t FORCE INDEX(b) WHERE b LIKE 'yy%';
CHECK TABLE t;
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
--disable_warnings
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
--enable_warnings

View File

@@ -0,0 +1,118 @@
--source include/have_innodb.inc
--source include/have_innodb_max_16k.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_sequence.inc
--echo #
--echo # MDEV-38056 As innodb.old_blob, but the version is rebuilt from the undo
--echo # log record of an update of a record that another, committed transaction
--echo # delete-marked. Such a record stores only the 20-byte reference, so the
--echo # frozen purge_sys.view is the only thing that can keep the dereference
--echo # valid. Reaching that version needs a batch to be still in progress.
--echo #
--source include/wait_all_purged.inc
CREATE TABLE t(a INT PRIMARY KEY, b TEXT, UNIQUE(b(700)))
ENGINE=InnoDB STATS_PERSISTENT=0 ROW_FORMAT=COMPRESSED;
INSERT INTO t VALUES (1, CONCAT('x', REPEAT('x', @@innodb_page_size)));
# Filler rows keep the delete-marked 'x...' entry off the leaf page holding the
# live 'z...' entry, which the reader latches; purge would block on it otherwise.
INSERT INTO t
SELECT 100 + seq, CONCAT('y~', LPAD(seq, 3, '0'), REPEAT('-', 800))
FROM seq_60_to_1;
# The counters are cumulative over the lifetime of the server.
SET GLOBAL innodb_monitor_reset_all='purge_del_mark_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_enable='purge_del_mark_records';
SET GLOBAL innodb_monitor_enable='purge_upd_exist_or_extern_records';
# Pin purge so that the DELETE below stays unpurged until we choose.
connect (purge_control,localhost,root,,);
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
# T1 delete-marks the row; the 'x...' BLOB is still owned by the record.
DELETE FROM t WHERE a = 1;
# T2 re-inserts the same PRIMARY KEY: an update of a record that T1
# delete-marked, so the undo log record stores only the reference, no prefix.
connect (victim,localhost,root,,);
BEGIN;
INSERT INTO t VALUES (1, CONCAT('z', REPEAT('z', @@innodb_page_size)));
# Hold the batch below open once it has purged T1, which is the window in which
# purge_sys.end_view still presents that history as reconstructible.
connection default;
SET @old_dbug= @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug='+d,purge_hold_cleanup';
# Let purge see T1 and remove the delete-marked 'x...' entry: nobody holds the
# clustered index leaf yet. T2 is still active, so purge stops there.
disconnect purge_control;
connection default;
SET DEBUG_SYNC='now WAIT_FOR purge_batch_parked TIMEOUT 60';
# The batch is parked, so its last purged record is behind it and the counter is
# final. It removed the delete-marked 'x...' entry.
SELECT count > 0 AS del_mark_records_purged
FROM information_schema.innodb_metrics WHERE name = 'purge_del_mark_records';
# The reader references the active T2. end_view cannot see T1, so it rebuilds the
# delete-marked 'x...' version and parks before dereferencing it.
connect (reader,localhost,root,,);
SET DEBUG_SYNC='row_vers_impl_x_locked_row_build SIGNAL parked WAIT_FOR resume';
--send SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'z%' FOR UPDATE
connection default;
SET DEBUG_SYNC='now WAIT_FOR parked TIMEOUT 60';
# The reader has descended, so the batch may finish. The next one cannot start
# before the reader is done anyway, the reader holding the freeze. Clear
# purge_hold_cleanup first, so that the next batch does not park.
SET GLOBAL debug_dbug= @old_dbug;
SET DEBUG_SYNC='now SIGNAL purge_batch_resume';
# T2 commits, making purgeable the undo log record that disowned the 'x...' BLOB,
# and then waits for the reference the parked reader holds.
connection victim;
--send COMMIT
connection default;
--source suite/innodb/include/assert_blob_not_purged.inc
SET DEBUG_SYNC='now SIGNAL resume';
connection victim;
--reap
connection reader;
--reap
connection default;
disconnect reader;
disconnect victim;
SET DEBUG_SYNC='RESET';
# Once the reader is done, purge advances and frees that BLOB, which confirms
# that only the frozen view held it back.
let $wait_condition=
SELECT count >= 1 FROM information_schema.innodb_metrics
WHERE name = 'purge_upd_exist_or_extern_records';
--source include/wait_condition.inc
SELECT a FROM t FORCE INDEX(b) WHERE b LIKE 'zz%';
CHECK TABLE t;
DROP TABLE t;
SET GLOBAL innodb_monitor_disable='purge_del_mark_records';
SET GLOBAL innodb_monitor_disable='purge_upd_exist_or_extern_records';
SET GLOBAL innodb_monitor_reset_all='purge_del_mark_records';
SET GLOBAL innodb_monitor_reset_all='purge_upd_exist_or_extern_records';
--disable_warnings
SET GLOBAL innodb_monitor_enable=default;
SET GLOBAL innodb_monitor_disable=default;
SET GLOBAL innodb_monitor_reset_all=default;
--enable_warnings

View File

@@ -211,3 +211,11 @@ ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Not implemented for sys
ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=COPY;
ERROR HY000: Index 'FTS_DOC_ID_INDEX' is of wrong type for an InnoDB FULLTEXT index
DROP TABLE t1;
#
# MDEV-40985 row_end access out of bounds in ALTER TABLE
#
CREATE TABLE t1
(a TEXT,b INT AS(1),c INT AS(1),d INT AS(1),e INT AS(1),f INT AS(1),
FULLTEXT INDEX(a)) ENGINE=InnoDB;
ALTER TABLE t1 FORCE;
DROP TABLE t1;

View File

@@ -844,5 +844,18 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t2 fulltext b b 0 1 Using where
DROP TABLE t1, t2;
#
# MDEV-41022 Wildcard term returns no rows when combined with a
# phrase in boolean search
#
CREATE TABLE t1(f1 TEXT, FULLTEXT(f1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('auth code out');
SELECT * FROM t1 WHERE MATCH(f1) AGAINST('"zzzz" aut*' IN BOOLEAN MODE);
f1
auth code out
SELECT * FROM t1 WHERE MATCH(f1) AGAINST('"zzzz yyyy" @3 aut*' IN BOOLEAN MODE);
f1
auth code out
DROP TABLE t1;
#
# End of 10.11 tests
#

View File

@@ -142,3 +142,13 @@ ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=INPLACE;
--error ER_INNODB_FT_WRONG_DOCID_INDEX
ALTER TABLE t1 ADD FULLTEXT INDEX(b), ALGORITHM=COPY;
DROP TABLE t1;
--echo #
--echo # MDEV-40985 row_end access out of bounds in ALTER TABLE
--echo #
CREATE TABLE t1
(a TEXT,b INT AS(1),c INT AS(1),d INT AS(1),e INT AS(1),f INT AS(1),
FULLTEXT INDEX(a)) ENGINE=InnoDB;
ALTER TABLE t1 FORCE;
DROP TABLE t1;

View File

@@ -851,6 +851,16 @@ WHERE EXISTS (
# Cleanup
DROP TABLE t1, t2;
--echo #
--echo # MDEV-41022 Wildcard term returns no rows when combined with a
--echo # phrase in boolean search
--echo #
CREATE TABLE t1(f1 TEXT, FULLTEXT(f1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES ('auth code out');
SELECT * FROM t1 WHERE MATCH(f1) AGAINST('"zzzz" aut*' IN BOOLEAN MODE);
SELECT * FROM t1 WHERE MATCH(f1) AGAINST('"zzzz yyyy" @3 aut*' IN BOOLEAN MODE);
DROP TABLE t1;
--echo #
--echo # End of 10.11 tests
--echo #

View File

@@ -250,3 +250,39 @@ count(*)
drop procedure curdemo;
drop table t3;
drop table t1;
CREATE TABLE t1 (pt POINT NOT NULL, SPATIAL INDEX(pt)) ENGINE=InnoDB;
INSERT INTO t1 (pt) VALUES
(POINT(1,1)),(POINT(1,2)),(POINT(1,3)),(POINT(1,4)),
(POINT(2,1)),(POINT(2,2)),(POINT(2,3)),(POINT(2,4)),
(POINT(3,1)),(POINT(3,2)),(POINT(3,3)),(POINT(3,4)),
(POINT(4,1)),(POINT(4,2)),(POINT(4,3)),(POINT(4,4)),
(POINT(5,1)),(POINT(5,2)),(POINT(5,3)),(POINT(5,4)),
(POINT(6,1)),(POINT(6,2)),(POINT(6,3)),(POINT(6,4)),
(POINT(7,1)),(POINT(7,2)),(POINT(7,3)),(POINT(7,4)),
(POINT(8,1)),(POINT(8,2)),(POINT(8,3)),(POINT(8,4)),
(POINT(90,90)),(POINT(91,91)),(POINT(92,92)),(POINT(93,93)),
(POINT(94,94)),(POINT(95,95)),(POINT(96,96)),(POINT(97,97));
EXPLAIN SELECT COUNT(*) FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'));
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range pt pt 34 NULL # Using where
connect con1,localhost,root,,test;
BEGIN;
SELECT * FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'))
FOR UPDATE;
connect con2,localhost,root,,test;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT COUNT(*) FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'));
connection con1;
ROLLBACK;
connection con2;
COUNT(*)
32
COMMIT;
connection default;
disconnect con1;
disconnect con2;
DROP TABLE t1;

View File

@@ -138,4 +138,53 @@ select count(*) from t3;
drop procedure curdemo;
drop table t3;
drop table t1;
#
# MDEV-40404 : buf_page_t::init() assertion failure in rtr_init_match()
#
CREATE TABLE t1 (pt POINT NOT NULL, SPATIAL INDEX(pt)) ENGINE=InnoDB;
INSERT INTO t1 (pt) VALUES
(POINT(1,1)),(POINT(1,2)),(POINT(1,3)),(POINT(1,4)),
(POINT(2,1)),(POINT(2,2)),(POINT(2,3)),(POINT(2,4)),
(POINT(3,1)),(POINT(3,2)),(POINT(3,3)),(POINT(3,4)),
(POINT(4,1)),(POINT(4,2)),(POINT(4,3)),(POINT(4,4)),
(POINT(5,1)),(POINT(5,2)),(POINT(5,3)),(POINT(5,4)),
(POINT(6,1)),(POINT(6,2)),(POINT(6,3)),(POINT(6,4)),
(POINT(7,1)),(POINT(7,2)),(POINT(7,3)),(POINT(7,4)),
(POINT(8,1)),(POINT(8,2)),(POINT(8,3)),(POINT(8,4)),
(POINT(90,90)),(POINT(91,91)),(POINT(92,92)),(POINT(93,93)),
(POINT(94,94)),(POINT(95,95)),(POINT(96,96)),(POINT(97,97));
--replace_column 9 #
EXPLAIN SELECT COUNT(*) FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'));
--connect (con1,localhost,root,,test)
BEGIN;
--disable_result_log
SELECT * FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'))
FOR UPDATE;
--enable_result_log
--connect (con2,localhost,root,,test)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
--send
SELECT COUNT(*) FROM t1 FORCE INDEX(pt)
WHERE ST_Within(pt, ST_GeomFromText('POLYGON((0 0,0 10,10 10,10 0,0 0))'));
--connection con1
let $wait_condition=
SELECT COUNT(*)=1 FROM information_schema.innodb_trx
WHERE trx_state='LOCK WAIT';
--source include/wait_condition.inc
ROLLBACK;
--connection con2
--reap
COMMIT;
--connection default
--disconnect con1
--disconnect con2
DROP TABLE t1;

View File

@@ -0,0 +1,19 @@
SET @saved_replicate_do_db= @@GLOBAL.replicate_do_db;
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';
SELECT @@GLOBAL.name.replicate_do_db;
@@GLOBAL.name.replicate_do_db
named
SELECT @@GLOBAL.replicate_do_db;
@@GLOBAL.replicate_do_db
named
SELECT @@GLOBAL.``.replicate_do_db;
@@GLOBAL.``.replicate_do_db
unnamed
RESET SLAVE ALL;
SET @@SESSION.default_master_connection= @saved_default_master_connection;
SET @@GLOBAL.replicate_do_db= @saved_replicate_do_db;
# End of 10.11 tests

View File

@@ -0,0 +1,25 @@
--source include/not_embedded.inc
# MDEV-40296: SELECT @@GLOBAL.replicate_do_db ignores default_master_connection
# Setup
SET @saved_replicate_do_db= @@GLOBAL.replicate_do_db;
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';
SELECT @@GLOBAL.name.replicate_do_db;
SELECT @@GLOBAL.replicate_do_db;
# MDEV-40297: Explicit empty connection name
SELECT @@GLOBAL.``.replicate_do_db;
# Cleanup
RESET SLAVE ALL;
SET @@SESSION.default_master_connection= @saved_default_master_connection;
SET @@GLOBAL.replicate_do_db= @saved_replicate_do_db;
--echo # End of 10.11 tests

View File

@@ -468,6 +468,9 @@ Gtid_Slave_Pos
Master_last_event_time NULL
Slave_last_event_time NULL
Master_Slave_time_diff NULL
#
# MDEV-40996: the connection name may be given as a variable
#
stop all slaves;
Warnings:
Note 1938 SLAVE 'slave2' stopped

View File

@@ -77,6 +77,12 @@ reset slave 'slave1' all;
--replace_result $SERVER_MYPORT_1 MYPORT_1 $SERVER_MYPORT_2 MYPORT_2 $read_master_log_pos <read_master_log_pos> $relay_log_pos <relay_log_pos> $relay_log_space1 <relay_log_space1> $relay_log_space2 <relay_log_space2>
query_vertical show all slaves status;
--echo #
--echo # MDEV-40996: the connection name may be given as a variable
--echo #
--let $conn_name= slave2
--sync_with_master 0,$conn_name
stop all slaves;
--replace_result $SERVER_MYPORT_1 MYPORT_1 $SERVER_MYPORT_2 MYPORT_2 $read_master_log_pos <read_master_log_pos> $relay_log_pos <relay_log_pos> $relay_log_space1 <relay_log_space1> $relay_log_space2 <relay_log_space2>
query_vertical show all slaves status;

View File

@@ -352,3 +352,149 @@ SELECT * FROM t;
SELECT * FROM tp;
DROP TABLE t, tp, tsp;
--echo #
--echo # MDEV-38757 Exchange partition fails with compound expressions
--echo #
--echo # Test EXCHANGE PARTITION with generated columns containing AND/OR conditions
--echo # This verifies set comparison of generated column conditions during
--echo # partition exchange.
--echo # Test 1: Generated column with OR condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1) VALUES (1, 10), (1, 50), (1, 90);
--echo # This should succeed: EXCHANGE PARTITION with OR condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, vcol1 FROM t1 ORDER BY id;
DROP TABLE t1_working;
DROP TABLE t1;
--echo # Test 2: Generated column with AND condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 20, 50), (1, 5, 200);
--echo # This should succeed: EXCHANGE PARTITION with AND condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol2 FROM t1 ORDER BY id;
DROP TABLE t1_working;
DROP TABLE t1;
--echo # Test 3: Multiple generated columns with different AND/OR combinations
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
vcol3 TINYINT(1) GENERATED ALWAYS AS (col2 > 50 OR col2 < 10) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 15, 75), (1, 90, 5), (1, 50, 200);
--echo # This should succeed: EXCHANGE PARTITION with multiple AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol1, vcol2, vcol3 FROM t1 ORDER BY id;
DROP TABLE t1_working;
DROP TABLE t1;
--echo # Test 4: Nested AND/OR conditions
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS ((col1 > 10 AND col2 < 100) OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2, col3) VALUES (1, 20, 50, 30), (1, 5, 200, 60);
--echo # This should succeed: EXCHANGE PARTITION with nested AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
DROP TABLE t1_working;
DROP TABLE t1;
--echo # Test 5: Reordered condition with a duplicate term
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col3 > 50 OR col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1_working (userId, col1, col2, col3)
VALUES (1, 20, 50, 30), (1, 5, 200, 60);
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
DROP TABLE t1_working;
DROP TABLE t1;
--echo # End of 10.11 tests

View File

@@ -373,3 +373,136 @@ a b
63 Sixty three, new
99 End of values
DROP TABLE t, tp, tsp;
#
# MDEV-38757 Exchange partition fails with compound expressions
#
# Test EXCHANGE PARTITION with generated columns containing AND/OR conditions
# This verifies set comparison of generated column conditions during
# partition exchange.
# Test 1: Generated column with OR condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1) VALUES (1, 10), (1, 50), (1, 90);
# This should succeed: EXCHANGE PARTITION with OR condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, vcol1 FROM t1 ORDER BY id;
id userId col1 vcol1
1 1 10 1
2 1 50 0
3 1 90 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 2: Generated column with AND condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 20, 50), (1, 5, 200);
# This should succeed: EXCHANGE PARTITION with AND condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol2 FROM t1 ORDER BY id;
id userId col1 col2 vcol2
1 1 20 50 1
2 1 5 200 0
DROP TABLE t1_working;
DROP TABLE t1;
# Test 3: Multiple generated columns with different AND/OR combinations
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
vcol3 TINYINT(1) GENERATED ALWAYS AS (col2 > 50 OR col2 < 10) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 15, 75), (1, 90, 5), (1, 50, 200);
# This should succeed: EXCHANGE PARTITION with multiple AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol1, vcol2, vcol3 FROM t1 ORDER BY id;
id userId col1 col2 vcol1 vcol2 vcol3
1 1 15 75 1 1 1
2 1 90 5 1 1 1
3 1 50 200 0 0 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 4: Nested AND/OR conditions
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS ((col1 > 10 AND col2 < 100) OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2, col3) VALUES (1, 20, 50, 30), (1, 5, 200, 60);
# This should succeed: EXCHANGE PARTITION with nested AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 5: Reordered condition with a duplicate term
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col3 > 50 OR col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1_working (userId, col1, col2, col3)
VALUES (1, 20, 50, 30), (1, 5, 200, 60);
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# End of 10.11 tests

View File

@@ -373,3 +373,136 @@ a b
63 Sixty three, new
99 End of values
DROP TABLE t, tp, tsp;
#
# MDEV-38757 Exchange partition fails with compound expressions
#
# Test EXCHANGE PARTITION with generated columns containing AND/OR conditions
# This verifies set comparison of generated column conditions during
# partition exchange.
# Test 1: Generated column with OR condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1) VALUES (1, 10), (1, 50), (1, 90);
# This should succeed: EXCHANGE PARTITION with OR condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, vcol1 FROM t1 ORDER BY id;
id userId col1 vcol1
1 1 10 1
2 1 50 0
3 1 90 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 2: Generated column with AND condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 20, 50), (1, 5, 200);
# This should succeed: EXCHANGE PARTITION with AND condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol2 FROM t1 ORDER BY id;
id userId col1 col2 vcol2
1 1 20 50 1
2 1 5 200 0
DROP TABLE t1_working;
DROP TABLE t1;
# Test 3: Multiple generated columns with different AND/OR combinations
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
vcol3 TINYINT(1) GENERATED ALWAYS AS (col2 > 50 OR col2 < 10) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 15, 75), (1, 90, 5), (1, 50, 200);
# This should succeed: EXCHANGE PARTITION with multiple AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol1, vcol2, vcol3 FROM t1 ORDER BY id;
id userId col1 col2 vcol1 vcol2 vcol3
1 1 15 75 1 1 1
2 1 90 5 1 1 1
3 1 50 200 0 0 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 4: Nested AND/OR conditions
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS ((col1 > 10 AND col2 < 100) OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2, col3) VALUES (1, 20, 50, 30), (1, 5, 200, 60);
# This should succeed: EXCHANGE PARTITION with nested AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 5: Reordered condition with a duplicate term
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col3 > 50 OR col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1_working (userId, col1, col2, col3)
VALUES (1, 20, 50, 30), (1, 5, 200, 60);
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# End of 10.11 tests

View File

@@ -373,3 +373,136 @@ a b
63 Sixty three, new
99 End of values
DROP TABLE t, tp, tsp;
#
# MDEV-38757 Exchange partition fails with compound expressions
#
# Test EXCHANGE PARTITION with generated columns containing AND/OR conditions
# This verifies set comparison of generated column conditions during
# partition exchange.
# Test 1: Generated column with OR condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1) VALUES (1, 10), (1, 50), (1, 90);
# This should succeed: EXCHANGE PARTITION with OR condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, vcol1 FROM t1 ORDER BY id;
id userId col1 vcol1
1 1 10 1
2 1 50 0
3 1 90 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 2: Generated column with AND condition
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 20, 50), (1, 5, 200);
# This should succeed: EXCHANGE PARTITION with AND condition
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol2 FROM t1 ORDER BY id;
id userId col1 col2 vcol2
1 1 20 50 1
2 1 5 200 0
DROP TABLE t1_working;
DROP TABLE t1;
# Test 3: Multiple generated columns with different AND/OR combinations
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
vcol1 TINYINT(1) GENERATED ALWAYS AS (col1 > 80 OR col1 < 20) STORED,
vcol2 TINYINT(1) GENERATED ALWAYS AS (col1 > 10 AND col2 < 100) STORED,
vcol3 TINYINT(1) GENERATED ALWAYS AS (col2 > 50 OR col2 < 10) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1),
PARTITION p2 VALUES IN (2));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2) VALUES (1, 15, 75), (1, 90, 5), (1, 50, 200);
# This should succeed: EXCHANGE PARTITION with multiple AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, vcol1, vcol2, vcol3 FROM t1 ORDER BY id;
id userId col1 col2 vcol1 vcol2 vcol3
1 1 15 75 1 1 1
2 1 90 5 1 1 1
3 1 50 200 0 0 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 4: Nested AND/OR conditions
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS ((col1 > 10 AND col2 < 100) OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working LIKE t1;
ALTER TABLE t1_working REMOVE PARTITIONING;
INSERT INTO t1_working (userId, col1, col2, col3) VALUES (1, 20, 50, 30), (1, 5, 200, 60);
# This should succeed: EXCHANGE PARTITION with nested AND/OR conditions
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# Test 5: Reordered condition with a duplicate term
CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
PARTITION BY LIST (userId)
(PARTITION p1 VALUES IN (1));
CREATE TABLE t1_working (
id INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL,
vcol4 TINYINT(1) GENERATED ALWAYS AS
(col3 > 50 OR col1 > 10 AND col2 < 100 OR col3 > 50) STORED,
PRIMARY KEY (id, userId)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO t1_working (userId, col1, col2, col3)
VALUES (1, 20, 50, 30), (1, 5, 200, 60);
ALTER TABLE t1 EXCHANGE PARTITION p1 WITH TABLE t1_working;
SELECT id, userId, col1, col2, col3, vcol4 FROM t1 ORDER BY id;
id userId col1 col2 col3 vcol4
1 1 20 50 30 1
2 1 5 200 60 1
DROP TABLE t1_working;
DROP TABLE t1;
# End of 10.11 tests

View File

@@ -15,3 +15,4 @@ rpl_row_binlog_max_cache_size : MDEV-11092
rpl_row_index_choice : MDEV-11666
rpl_auto_increment_update_failure : disabled for now
rpl_current_user : waits for MDEV-22374 fix
rpl_parallel_multi_domain_xa : (2026-07-24) MDEV-34104: waits for MDEV-32020

View File

@@ -0,0 +1,17 @@
include/master-slave.inc
[connection master]
connection slave;
CHANGE MASTER TO MASTER_DELAY=1 FOR CHANNEL '################################################';
ERROR HY000: Could not initialize master info structure for '################################################'; more error messages can be found in the MariaDB error log
connection master;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
connection slave;
connection slave;
include/diff_tables.inc [master:t1, slave:t1]
CHANGE MASTER 'new_channel' TO MASTER_HOST='127.0.0.1', MASTER_PORT=1, MASTER_USER='root';
RESET SLAVE 'new_channel' ALL;
connection master;
DROP TABLE t1;
connection slave;
include/rpl_end.inc

View File

@@ -0,0 +1,40 @@
#
# CHANGE MASTER ... FOR CHANNEL with a name that overflows the relay
# log file name once escaped should return an error, not crash.
#
# STEPS:
# 1. Issue CHANGE MASTER ... FOR CHANNEL with a channel name that is
# within MAX_CONNECTION_NAME, but expands past the file name
# limit once escaped into the relay log file name.
# 2. Verify the statement fails with a clean error, not a crash.
# 3. Verify the server is still usable: a normal CHANGE MASTER for a
# new, short-named channel succeeds.
#
--source include/master-slave.inc
--connection slave
--let $channel= `SELECT REPEAT('#', 48)`
--error ER_MASTER_INFO
eval CHANGE MASTER TO MASTER_DELAY=1 FOR CHANNEL '$channel';
--connection master
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
--sync_slave_with_master
--connection slave
--let $diff_tables= master:t1, slave:t1
--source include/diff_tables.inc
CHANGE MASTER 'new_channel' TO MASTER_HOST='127.0.0.1', MASTER_PORT=1, MASTER_USER='root';
RESET SLAVE 'new_channel' ALL;
--disable_query_log
call mtr.add_suppression("Failed when trying to open logs");
--enable_query_log
--connection master
DROP TABLE t1;
--sync_slave_with_master
--source include/rpl_end.inc

View File

@@ -28,6 +28,20 @@ SET GLOBAL gtid_slave_pos= '1-2-3 ';
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '1-2-3,2-4';
ERROR HY000: Could not parse GTID list
SET GLOBAL gtid_slave_pos= '0-1-10,1-1-20,2-1-30';
SET GLOBAL gtid_slave_pos= '0-1-110,1-1-120,,2-1-130';
ERROR HY000: Could not parse GTID list
SELECT @@GLOBAL.gtid_slave_pos;
@@GLOBAL.gtid_slave_pos
0-1-10,1-1-20,2-1-30
SELECT A.domain_id, A.server_id, A.seq_no
FROM mysql.gtid_slave_pos A
WHERE A.sub_id=(SELECT MAX(B.sub_id) FROM mysql.gtid_slave_pos B WHERE A.domain_id=B.domain_id)
ORDER BY A.domain_id;
domain_id server_id seq_no
0 1 10
1 1 20
2 1 30
SET GLOBAL gtid_slave_pos= '0-1-10,0-2-20';
ERROR HY000: GTID 0-2-20 and 0-1-10 conflict (duplicate domain id 0)
SET GLOBAL gtid_slave_pos= '0-1-10,1-2-20,2-3-30,1-20-200,3-4-1';

View File

@@ -24,6 +24,17 @@ SET GLOBAL gtid_slave_pos= '1-2-3 ';
--error ER_INCORRECT_GTID_STATE
SET GLOBAL gtid_slave_pos= '1-2-3,2-4';
# Test that a parse error in the middle of the GTID string does not leave
# a partial updated value.
SET GLOBAL gtid_slave_pos= '0-1-10,1-1-20,2-1-30';
--error ER_INCORRECT_GTID_STATE
SET GLOBAL gtid_slave_pos= '0-1-110,1-1-120,,2-1-130';
SELECT @@GLOBAL.gtid_slave_pos;
SELECT A.domain_id, A.server_id, A.seq_no
FROM mysql.gtid_slave_pos A
WHERE A.sub_id=(SELECT MAX(B.sub_id) FROM mysql.gtid_slave_pos B WHERE A.domain_id=B.domain_id)
ORDER BY A.domain_id;
--error ER_DUPLICATE_GTID_DOMAIN
SET GLOBAL gtid_slave_pos= '0-1-10,0-2-20';
--error ER_DUPLICATE_GTID_DOMAIN

View File

@@ -1,7 +1,7 @@
# Correct Galera library found
show status like 'wsrep_protocol%';
Variable_name Value
wsrep_protocol_application 4
wsrep_protocol_application 5
wsrep_protocol_gcs 6
wsrep_protocol_replicator 11
wsrep_protocol_version 11

View File

@@ -234,6 +234,19 @@ get_socat_ver()
fi
}
check_for_socat_commonname()
{
[ -n "${SOCAT_COMMONNAME+x}" ] && return
# Some socat builds don't register "commonname" as an option at all
# (parseopts() rejects it as unknown regardless of value), so probe
# the actual binary instead of relying on the socat version number.
if socat -hhh 2>&1 | grep -qw 'openssl-commonname'; then
SOCAT_COMMONNAME=1
else
SOCAT_COMMONNAME=0
fi
}
get_transfer()
{
if [ "$tfmt" = 'nc' ]; then
@@ -434,6 +447,15 @@ get_transfer()
exit 22
fi
if [ -n "$CN_option" ]; then
check_for_socat_commonname
if [ "$SOCAT_COMMONNAME" -eq 0 ]; then
wsrep_log_info \
"socat lacks the 'commonname' option; skipping peer name verification"
CN_option=""
fi
fi
tcmd="$tcmd$CN_option$sockopt"
if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then

View File

@@ -6,12 +6,14 @@ In the current release model, versions are released like the following
Q1 Q2 Q3 Q4
2022 10.7 10.8 10.9 10.10
2023 10.11-LTS 11.0 11.1 11.2
2024 11.3 11.4 11.5 11.6
2025 11.7-LTS 12.0 12.1 12.2
2026 12.3 12.4 12.5 12.6
2027 12.7-LTS 13.0 13.1 13.2
2028 13.3 13.4 13.5 13.6
2029 13.7-LTS 14.0 14.1 14.2
2024 11.3 11.4-LTS 11.5 11.6
2025 11.7 11.8-LTS 12.0 12.1
2026 12.2 12.3-LTS 13.0 13.1
2027 13.2 13.3-LTS 14.0 14.1
2028 14.2 14.3-LTS 15.0 15.1
2029 15.2 15.3-LTS 16.0 16.1
2030 16.2 16.3-LTS 17.0 17.1
2031 17.2 17.3-LTS 18.0 18.1
...
A deprecated feature can be removed only when all releases when it's
@@ -28,9 +30,12 @@ template<uint V> static inline void check_deprecated_version(void)
{
static_assert (
V <= 1004 ? MYSQL_VERSION_ID < 110500 : /* until 10.4 EOL */
V <= 1005 ? MYSQL_VERSION_ID < 120100 : /* until 10.5 EOL */
V <= 1010 ? MYSQL_VERSION_ID < 120500 : /* until 10.6 EOL */
V <= 1107 ? MYSQL_VERSION_ID < 130400 : /* until 10.11 EOL */
V <= 1005 ? MYSQL_VERSION_ID < 120000 : /* until 10.5 EOL */
V <= 1010 ? MYSQL_VERSION_ID < 130000 : /* until 10.6 EOL */
V <= 1103 ? MYSQL_VERSION_ID < 140300 : /* until 10.11 EOL */
V <= 1107 ? MYSQL_VERSION_ID < 160000 : /* until 11.4 EOL */
V <= 1202 ? MYSQL_VERSION_ID < 170000 : /* until 11.8 EOL */
V <= 1302 ? MYSQL_VERSION_ID < 180000 : /* until 12.3 EOL */
V == 999999, /* only for sys_var::do_deprecated_warning() */
"check_deprecated_version failed"
);

View File

@@ -7823,7 +7823,7 @@ Field_string::compatible_field_size(uint field_metadata,
{
#ifdef HAVE_REPLICATION
const Check_field_param check_param = { this };
if (rpl_master_has_bug(rli_arg, 37426, TRUE,
if (rpl_master_has_bug(rli_arg, RPL_BUG_MYSQL_37426, TRUE,
check_field_for_37426, &check_param))
return FALSE; // Not compatible field sizes
#endif

View File

@@ -656,7 +656,8 @@ public:
bool cleanup_session_expr();
bool fix_and_check_expr(THD *thd, TABLE *table);
bool check_access(THD *thd);
inline bool is_equal(const Virtual_column_info* vcol, bool cmp_names) const;
inline bool is_equal(const Virtual_column_info* vcol, bool cmp_names,
bool unordered_conditions= false) const;
inline void print(String*);
};

View File

@@ -12136,10 +12136,16 @@ void Item_direct_ref_to_item::change_item(THD *thd, Item *i)
bool Item::cleanup_excluding_immutables_processor (void *arg)
{
if (!(get_extraction_flag() == MARKER_IMMUTABLE))
return cleanup_processor(arg);
else
/*
basic_const_item() is also true for an Item_ref wrapping a constant,
e.g. a reference to a constant select list item of a merged derived
table. Such a ref is never marked with MARKER_IMMUTABLE, but the
function enclosing it is, so it is not cleaned up here, and
fix_fields() does not descend into items that are already fixed.
*/
if (basic_const_item() || get_extraction_flag() == MARKER_IMMUTABLE)
return false;
return cleanup_processor(arg);
}

View File

@@ -1269,8 +1269,11 @@ public:
{
bool binary_cmp; /**< Make binary comparison */
bool omit_table_names; /**< Skip table and db names comparison */
Eq_config(bool binary_cmp, bool omit_table_names= false)
: binary_cmp(binary_cmp), omit_table_names(omit_table_names) {}
bool unordered_conditions; /**< Ignore AND/OR operand order and duplicates */
Eq_config(bool binary_cmp, bool omit_table_names= false,
bool unordered_conditions= false)
: binary_cmp(binary_cmp), omit_table_names(omit_table_names),
unordered_conditions(unordered_conditions) {}
};
virtual bool eq(const Item *, const Eq_config &config) const;
enum_field_types field_type() const
@@ -8500,11 +8503,13 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
int *escape);
inline bool Virtual_column_info::is_equal(const Virtual_column_info* vcol,
bool omit_table_names) const
bool omit_table_names,
bool unordered_conditions) const
{
return type_handler() == vcol->type_handler()
&& is_stored() == vcol->is_stored()
&& expr->eq(vcol->expr, {true, omit_table_names});
&& expr->eq(vcol->expr,
{true, omit_table_names, unordered_conditions});
}
inline void Virtual_column_info::print(String* str)

View File

@@ -5661,6 +5661,80 @@ bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel)
}
/**
Compare two Item_cond expressions for equality.
Distinct AND/OR items are normally not considered equal, preserving their
execution order. Some metadata comparisons can explicitly request a set
comparison because table definitions may be serialized with their
arguments reordered.
@param item The other Item to compare with
@param config Comparison configuration
@return true if expressions are equivalent, false otherwise
*/
bool Item_cond::eq(const Item *item, const Eq_config &config) const
{
if (this == item)
return true;
if (!config.unordered_conditions)
return false;
if (item->type() != COND_ITEM || (used_tables() & RAND_TABLE_BIT))
return false;
const Item_cond *item_cond= (const Item_cond *) item;
if (functype() != item_cond->functype())
return false;
/*
Treat the argument lists as sets. Checking both directions deliberately
ignores duplicate terms: A OR B and A OR B OR A are equivalent here.
*/
List_iterator_fast<Item> li1(const_cast<List<Item>&>(list));
Item *arg1;
while ((arg1= li1++))
{
bool found_match= false;
List_iterator_fast<Item> li2(const_cast<List<Item>&>(item_cond->list));
Item *arg2;
while ((arg2= li2++))
{
if (arg1->eq(arg2, config))
{
found_match= true;
break;
}
}
if (!found_match)
return false;
}
List_iterator_fast<Item> li2(const_cast<List<Item>&>(item_cond->list));
Item *arg2;
while ((arg2= li2++))
{
bool found_match= false;
List_iterator_fast<Item> li1(const_cast<List<Item>&>(list));
Item *arg1;
while ((arg1= li1++))
{
if (arg2->eq(arg1, config))
{
found_match= true;
break;
}
}
if (!found_match)
return false;
}
return true;
}
void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding)
{
List_iterator<Item> li(list);

View File

@@ -3390,6 +3390,7 @@ public:
Item *deep_copy(THD *thd) const override;
bool excl_dep_on_table(table_map tab_map) override;
bool excl_dep_on_grouping_fields(st_select_lex *sel) override;
bool eq(const Item *item, const Eq_config &config) const override;
private:
void merge_sub_condition(List_iterator<Item>& li);

View File

@@ -5326,8 +5326,6 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
if (!(name.str= (char*) thd->memdup(buf, name.length + 1)))
DBUG_RETURN(TRUE);
result_sink->get_tmp_table_param()->materialized_subquery= true;
if (item->substype() == Item_subselect::IN_SUBS &&
(item->get_IN_subquery()->is_jtbm_merged))
{

View File

@@ -3861,15 +3861,15 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
Create an index file that will hold all file names uses for logging.
Add new entries to the end of it.
*/
myf opt= MY_UNPACK_FILENAME;
myf opt= MY_UNPACK_FILENAME | MY_SAFE_PATH;
if (!index_file_name_arg)
{
index_file_name_arg= log_name; // Use same basename for index file
opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT;
opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_SAFE_PATH;
}
fn_format(index_file_name, index_file_name_arg, mysql_data_home,
".index", opt);
if ((index_file_nr= mysql_file_open(m_key_file_log_index,
if (!fn_format(index_file_name, index_file_name_arg, mysql_data_home,
".index", opt) ||
(index_file_nr= mysql_file_open(m_key_file_log_index,
index_file_name,
O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC,
MYF(MY_WME))) < 0 ||
@@ -5706,6 +5706,16 @@ err:
DBUG_RETURN(error);
}
/*
Three parameter state object describes the exclusive rhs of the
purgeable range (left is the past). The state modification is
guarded by LOCK_index.
*/
static bool waiting_for_slave_to_change_binlog= 0;
static ulonglong purge_sending_new_binlog_file= 0;
static char purge_binlog_name[FN_REFLEN];
static bool purge_warning_given= 0;
/*
@param log_file_name_arg Name of log file to check
@param interactive True if called by a PURGE BINLOG command.
@@ -5723,12 +5733,6 @@ err:
sending_new_binlog_file, cached in purge_sending_new_binlog_file,
is incremented every time a slave changes to use a new binary log.
*/
static bool waiting_for_slave_to_change_binlog= 0;
static ulonglong purge_sending_new_binlog_file= 0;
static char purge_binlog_name[FN_REFLEN];
static bool purge_warning_given= 0;
bool
MYSQL_BIN_LOG::can_purge_log(const char *log_file_name_arg,
bool interactive)
@@ -5736,14 +5740,26 @@ MYSQL_BIN_LOG::can_purge_log(const char *log_file_name_arg,
int res;
const char *reason;
if (is_active(log_file_name_arg) ||
(!is_relay_log && waiting_for_slave_to_change_binlog &&
purge_sending_new_binlog_file == sending_new_binlog_file &&
!strcmp(log_file_name_arg, purge_binlog_name)))
mysql_mutex_assert_owner(&LOCK_index);
if (is_active(log_file_name_arg))
{
reason= "it is the current active binlog";
goto error;
}
/*
If purge_sending_new_binlog_file == sending_new_binlog_file then
no dump thread has changed the state since we last computed it,
and we can re-use that state for the check.
*/
if (!interactive && !is_relay_log && waiting_for_slave_to_change_binlog &&
purge_sending_new_binlog_file == sending_new_binlog_file &&
!strcmp(log_file_name_arg, purge_binlog_name))
{
reason= "it is in use by a slave thread or less than "
"'slave_connections_needed_for_purge' slaves have processed it";
goto error;
}
DBUG_ASSERT(!is_relay_log || binlog_xid_count_list.is_empty());
if (!is_relay_log)

View File

@@ -158,6 +158,26 @@ class String;
#define NUM_LOAD_DELIM_STRS 5
/*
Master-side bugs that we work-around with rpl_master_has_bug().
Each define is a bit number (0, 1, 2, ...) set in
Relay_log_info::rpl_master_bug_bitmask.
These bit numbers are internal-only, can be changed (eg. to drop a bug
work-around for ancient version) without concern for backwards compatibility.
*/
enum enum_rpl_master_bug_ids {
RPL_BUG_MYSQL_24432,
RPL_BUG_MYSQL_33029,
RPL_BUG_MYSQL_37426,
RPL_BUG_MDEV_29621,
RPL_BUG_END /* To check size of Relay_log_info::rpl_master_bug_bitmask */
};
/* Must be large enough to hold all enum_rpl_master_bug_ids bits. */
typedef uint32_t rpl_bug_id_t;
/*
The following is the max table_map_id. This is limited by that we
are using 6 bytes for it in replication

View File

@@ -2589,6 +2589,14 @@ int Format_description_log_event::do_apply_event(rpl_group_info *rgi)
copy_crypto_data(rli->relay_log.description_event_for_exec);
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= this;
/*
The start of the relay log has our own generated format description
event followed by the real format description from the master.
Don't re-compute the bug bitmask from the generated event so we don't
leave a window where a wrong value is temporarily set.
*/
if (!is_relay_log_event())
rli->calc_master_bug_bitmask(this);
}
DBUG_RETURN(ret);
@@ -7069,8 +7077,8 @@ int Rows_log_event::update_sequence()
(table_rgi &&
!(table_rgi->gtid_ev_flags2 & Gtid_log_event::FL_DDL) &&
!(old_master=
rpl_master_has_bug(thd_rgi->rli,
29621, FALSE, FALSE, FALSE, TRUE))))
rpl_master_has_bug(thd_rgi->rli, RPL_BUG_MDEV_29621, FALSE,
NULL, NULL))))
{
/* This event come from a setval function executed on the master.
Update the sequence next_number and round, like we do with setval()

View File

@@ -7026,7 +7026,7 @@ bool JOIN::choose_tableless_subquery_plan()
*/
if (zero_result_cause)
{
if (!implicit_grouping)
if (!implicit_grouping && select_lex->olap != ROLLUP_TYPE)
{
/*
Both group by queries and non-group by queries without aggregate

View File

@@ -1411,6 +1411,7 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len,
}
if (state_from_master == end)
return 0;
int err= 0;
for (;;)
{
rpl_gtid gtid;
@@ -1421,14 +1422,26 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len,
!(sub_id= next_sub_id(gtid.domain_id)) ||
record_gtid(thd, &gtid, sub_id, false, in_statement, &hton) ||
update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL))
return 1;
{
err= 1;
break;
}
if (state_from_master == end)
break;
if (*state_from_master != ',')
return 1;
{
err= 1;
break;
}
++state_from_master;
}
return 0;
if (unlikely(err))
trans_rollback(thd);
else
err= trans_commit(thd);
return err;
}

View File

@@ -1487,7 +1487,8 @@ bool Master_info_index::remove_master_info(Master_info *mi, bool clear_log_files
/* This code is only executed when change_master() failes to create a new master info */
// Delete any temporary relay log files that could have been created by change_master()
mi->rli.relay_log.reset_logs(current_thd, 0, (rpl_gtid*) 0, 0, 0);
if (mi->rli.relay_log.is_open())
mi->rli.relay_log.reset_logs(current_thd, 0, (rpl_gtid*) 0, 0, 0);
/* Delete master-'connection'.info */
create_logfile_name_with_suffix(tmp_name,
sizeof(tmp_name),

View File

@@ -56,6 +56,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery, const char* thread_name)
group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0),
sql_thread_caught_up(true),
last_master_timestamp(0), newest_master_timestamp(0), slave_timestamp(0),
rpl_master_bug_bitmask(0),
slave_skip_counter(0),
abort_pos_wait(0), slave_run_id(0), sql_driver_thd(),
gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0),
@@ -731,6 +732,12 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
goto err;
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= fdev;
/*
The read_relay_log_description_event() returns the real FD from the
master found near the start of the relay log. Initialize the bitmask
of any known bugs in that master version.
*/
rli->calc_master_bug_bitmask(fdev);
}
my_b_seek(rli->cur_log,(off_t)pos);
DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%llu rli->event_relay_log_pos=%llu",
@@ -2667,4 +2674,159 @@ bool Relay_log_info::flush()
DBUG_RETURN(error);
}
struct st_version_range_for_one_bug {
rpl_bug_id_t bug_id;
uint bug_num;
bool mysql_bug; // Else Mariadb MDEV
Version introduced_in; // first version with bug
Version fixed_in; // first version with fix
};
static struct st_version_range_for_one_bug versions_for_all_bugs[]=
{
/*
Sorted with the most recent server version first, since this is the version
that will be printed in any error messages.
*/
/* MySQL bugs. */
{RPL_BUG_MYSQL_24432, 24432, true, { 5, 1, 12 }, { 5, 1, 17 } },
{RPL_BUG_MYSQL_24432, 24432, true, { 5, 0, 24 }, { 5, 0, 38 } },
{RPL_BUG_MYSQL_33029, 33029, true, { 5, 1, 0 }, { 5, 1, 12 } },
{RPL_BUG_MYSQL_33029, 33029, true, { 5, 0, 0 }, { 5, 0, 58 } },
{RPL_BUG_MYSQL_37426, 37426, true, { 5, 1, 0 }, { 5, 1, 26 } },
/* MariaDB bugs (MDEV-nnnnn). */
{RPL_BUG_MDEV_29621, 29621, false, { 10, 11,1 }, { 10, 11,3 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 10,1 }, { 10, 10,4 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 9, 2 }, { 10, 9, 6 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 8, 4 }, { 10, 8, 8 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 7, 5 }, { 10, 7, 9 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 6, 9 }, { 10, 6, 13 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 5, 17 }, { 10, 5, 20 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 4, 26 }, { 10, 4, 29 } },
{RPL_BUG_MDEV_29621, 29621, false, { 10, 3, 36 }, { 10, 3, 39 } },
};
/**
Detect, based on the version in format description event, any bugs on the
master that we have a work-around for on the slave. Calculate a bitmask
for this with a bit set for any detected bug.
*/
void
Relay_log_info::calc_master_bug_bitmask(const Format_description_log_event *
fd_event)
{
const Version &master_ver= fd_event->server_version_split;
uint all_size=
sizeof(versions_for_all_bugs)/sizeof(versions_for_all_bugs[0]);
rpl_bug_id_t bitmask= 0;
for (uint i= 0; i < all_size; i++)
{
const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
if (introduced_in <= master_ver && fixed_in > master_ver)
{
bitmask|= (1 << versions_for_all_bugs[i].bug_id);
}
}
rpl_master_bug_bitmask.store(bitmask, std::memory_order_relaxed);
}
/**
Detects, based on version of master's format description event, if master
has a certain bug. The bitset of bugs was pre-calculated by
Relay_log_info::calc_master_bug_bitmask().
The bitset is read out of the Relay_log_info without locking. However this
is ok, as the set of master bugs can only change with a master server
restart, and we do not replicate in parallel events crossing a master
restart (as identified by its restart format description event).
@param rli The Relay_log_info contains the pre-computed bug bitset.
@param bug_id Identifier of the bug as defined in enum_rpl_master_bug_ids.
@param report bool report error message.
@param pred Predicate function that will be called with @c param to
check for the bug. If the function return @c true, the bug is present,
otherwise, it is not.
@param param State passed to @c pred function.
@return TRUE if master has the bug, FALSE if it does not.
*/
bool
rpl_master_has_bug_ext(const Relay_log_info *rli, rpl_bug_id_t bug_id,
bool report,
bool (*pred)(const void *), const void *param)
{
rpl_bug_id_t mask=
rli->rpl_master_bug_bitmask.load(std::memory_order_relaxed);
if (!(mask & (1 << bug_id)))
return false;
if (pred != NULL && !(*pred)(param))
return false;
if (!report)
return true;
uint all_size=
sizeof(versions_for_all_bugs)/sizeof(versions_for_all_bugs[0]);
for (uint i= 0; i < all_size; i++)
{
const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
if (versions_for_all_bugs[i].bug_id == bug_id)
{
const char *bug_source= versions_for_all_bugs[i].mysql_bug ?
"http://bugs.mysql.com/bug.php?id=" :
"https://jira.mariadb.org/browse/MDEV-";
// a short message for SHOW SLAVE STATUS (message length constraints).
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
" %s%u"
" so slave stops; check error log on slave"
" for more info", MYF(0), bug_source,
versions_for_all_bugs[i].bug_num);
// a verbose message for the error log.
rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, NULL,
"Master's version is earlier than %d.%d.%d,"
" it is probable that master suffers from this bug:"
" %s%u"
" and thus replicating the current binary log event"
" may make the slave's data become different from the"
" master's data."
" To take no risk, slave refuses to replicate"
" this event and stops."
" We recommend that all updates be stopped on the"
" master and slave, that the data of both be"
" manually synchronized,"
" that master's binary logs be deleted,"
" that master be upgraded to a version at least"
" equal to '%d.%d.%d'. Then replication can be"
" restarted.",
fixed_in[0], fixed_in[1], fixed_in[2],
bug_source,
versions_for_all_bugs[i].bug_num,
fixed_in[0], fixed_in[1], fixed_in[2]);
return true;
}
}
DBUG_ASSERT(0 /* Impossible, as bitmap calculated from the same table. */);
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from a serious bug"
" so slave stops", MYF(0));
rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, NULL,
"Master's version is old,"
" it is probable that master suffers from a serious bug"
" and thus replicating the current binary log event"
" may make the slave's data become different from the"
" master's data."
" To take no risk, slave refuses to replicate"
" this event and stops."
" We recommend that all updates be stopped on the"
" master and slave, that the data of both be"
" manually synchronized,"
" that master's binary logs be deleted,"
" that master be upgraded to a recent version"
" Then replication can be"
" restarted.");
return true;
}
#endif

View File

@@ -294,6 +294,20 @@ public:
then set to when + exec_time -1 of the first seen event.
*/
time_t slave_timestamp;
/**
Bitmask of bugs present in the master version of the current
description_event_for_exec. Of atomic type, since it is written when
applying master's format description events, and read concurrently without
locking from parallel replication worker threads. Though note that the
value will only change during a master restart, and we do not replicate
events concurrently across such restart event, so the value will not change
in practice while worker threads are working.
*/
std::atomic<rpl_bug_id_t> rpl_master_bug_bitmask;
static_assert(sizeof(rpl_bug_id_t) > ((RPL_BUG_END - 1) >> 3),
"rpl_master_bug_bitmask too small to hold all bits");
void clear_until_condition();
/**
Reset the delay.
@@ -571,6 +585,7 @@ public:
int32 get_sql_delay() { return sql_delay; }
void set_sql_delay(int32 _sql_delay) { sql_delay= _sql_delay; }
time_t get_sql_delay_end() { return sql_delay_end; }
void calc_master_bug_bitmask(const Format_description_log_event * fd_event);
rpl_gtid last_seen_gtid;
ulong last_trans_retry_count;
private:
@@ -1109,4 +1124,19 @@ int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev);
void delete_or_keep_event_post_apply(rpl_group_info *rgi,
Log_event_type typ, Log_event *ev);
bool
rpl_master_has_bug_ext(const Relay_log_info *rli, rpl_bug_id_t bug_id,
bool report,
bool (*pred)(const void *), const void *param);
static inline bool
rpl_master_has_bug(const Relay_log_info *rli, rpl_bug_id_t bug_id,
bool report, bool (*pred)(const void *), const void *param)
{
rpl_bug_id_t mask=
rli->rpl_master_bug_bitmask.load(std::memory_order_relaxed);
if (likely(!(mask & (1 << bug_id))))
return false;
return rpl_master_has_bug_ext(rli, bug_id, report, pred, param);
}
#endif /* RPL_RLI_H */

View File

@@ -775,9 +775,8 @@ static void show_sql_type(const Conv_source &src, const Field &dst,
acceptable according to the current settings.
@param order The computed order of the conversion needed.
@param rli The relay log info data structure: for error reporting.
*/
static bool is_conversion_ok(enum_conv_type type, const Relay_log_info *rli,
static bool is_conversion_ok(enum_conv_type type,
ulonglong type_conversion_options)
{
DBUG_ENTER("is_conversion_ok");
@@ -948,12 +947,10 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi,
return false;
}
if (!h)
return false; // An unknown data type found in the binary log
Conv_source source(h, field_metadata(col), field->charset());
enum_conv_type convtype= can_convert_field_to(field, source, rli,
Conv_param(m_flags));
if (is_conversion_ok(convtype, rli, slave_type_conversions_options))
if (is_conversion_ok(convtype, slave_type_conversions_options))
{
DBUG_PRINT("debug", ("Checking column %d -"
" field '%s' can be converted - order: %d",

View File

@@ -220,7 +220,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
{
my_safe_printf_stderr("%s",
"The information page at "
"https://mariadb.com/docs/server/reference/product-development/mariadb-fault-finding/how-to-produce-a-full-stack-trace-for-mariadbd"
"https://mariadb.com/docs/server/reference/product-development/mariadb-fault-finding/how-to-produce-a-full-stack-trace-for-mariadbd\n"
"contains instructions to obtain a better version of the backtrace below.\n"
"Following these instructions will help MariaDB developers provide a fix quicker.\n\n"
"Attempting backtrace. Include this in the bug report.\n"

View File

@@ -8022,104 +8022,6 @@ end:
}
/**
Detects, based on master's version (as found in the relay log), if master
has a certain bug.
@param rli Relay_log_info which tells the master's version
@param bug_id Number of the bug as found in bugs.mysql.com
@param report bool report error message, default TRUE
@param pred Predicate function that will be called with @c param to
check for the bug. If the function return @c true, the bug is present,
otherwise, it is not.
@param param State passed to @c pred function.
@return TRUE if master has the bug, FALSE if it does not.
*/
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
bool (*pred)(const void *), const void *param,
bool maria_master)
{
struct st_version_range_for_one_bug {
uint bug_id;
Version introduced_in; // first version with bug
Version fixed_in; // first version with fix
};
static struct st_version_range_for_one_bug versions_for_their_bugs[]=
{
{24432, { 5, 0, 24 }, { 5, 0, 38 } },
{24432, { 5, 1, 12 }, { 5, 1, 17 } },
{33029, { 5, 0, 0 }, { 5, 0, 58 } },
{33029, { 5, 1, 0 }, { 5, 1, 12 } },
{37426, { 5, 1, 0 }, { 5, 1, 26 } },
};
static struct st_version_range_for_one_bug versions_for_our_bugs[]=
{
{29621, { 10, 3, 36 }, { 10, 3, 39 } },
{29621, { 10, 4, 26 }, { 10, 4, 29 } },
{29621, { 10, 5, 17 }, { 10, 5, 20 } },
{29621, { 10, 6, 9 }, { 10, 6, 13 } },
{29621, { 10, 7, 5 }, { 10, 7, 9 } },
{29621, { 10, 8, 4 }, { 10, 8, 8 } },
{29621, { 10, 9, 2 }, { 10, 9, 6 } },
{29621, { 10, 10,1 }, { 10, 10,4 } },
{29621, { 10, 11,1 }, { 10, 11,3 } },
};
const Version &master_ver=
rli->relay_log.description_event_for_exec->server_version_split;
struct st_version_range_for_one_bug* versions_for_all_bugs= maria_master ?
versions_for_our_bugs : versions_for_their_bugs;
uint all_size= maria_master ?
sizeof(versions_for_our_bugs)/sizeof(*versions_for_our_bugs) :
sizeof(versions_for_their_bugs)/sizeof(*versions_for_their_bugs);
for (uint i= 0; i < all_size; i++)
{
const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
if ((versions_for_all_bugs[i].bug_id == bug_id) &&
introduced_in <= master_ver &&
fixed_in > master_ver &&
(pred == NULL || (*pred)(param)))
{
const char *bug_source= maria_master ?
"https://jira.mariadb.org/browse/MDEV-" :
"http://bugs.mysql.com/bug.php?id=";
if (!report)
return TRUE;
// a short message for SHOW SLAVE STATUS (message length constraints)
my_printf_error(ER_UNKNOWN_ERROR, "master may suffer from"
" %s%u"
" so slave stops; check error log on slave"
" for more info", MYF(0), bug_source, bug_id);
// a verbose message for the error log
rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, NULL,
"According to the master's version ('%s'),"
" it is probable that master suffers from this bug:"
" %s%u"
" and thus replicating the current binary log event"
" may make the slave's data become different from the"
" master's data."
" To take no risk, slave refuses to replicate"
" this event and stops."
" We recommend that all updates be stopped on the"
" master and slave, that the data of both be"
" manually synchronized,"
" that master's binary logs be deleted,"
" that master be upgraded to a version at least"
" equal to '%d.%d.%d'. Then replication can be"
" restarted.",
rli->relay_log.description_event_for_exec->server_version,
bug_source,
bug_id,
fixed_in[0], fixed_in[1], fixed_in[2]);
return TRUE;
}
}
return FALSE;
}
/**
BUG#33029, For all 5.0 up to 5.0.58 exclusive, and 5.1 up to 5.1.12
exclusive, if one statement in a SP generated AUTO_INCREMENT value
@@ -8135,7 +8037,8 @@ bool rpl_master_erroneous_autoinc(THD *thd)
if (thd->rgi_slave)
{
DBUG_EXECUTE_IF("simulate_bug33029", return TRUE;);
return rpl_master_has_bug(thd->rgi_slave->rli, 33029, FALSE, NULL, NULL);
return rpl_master_has_bug(thd->rgi_slave->rli, RPL_BUG_MYSQL_33029, FALSE,
NULL, NULL);
}
return FALSE;
}

View File

@@ -230,9 +230,6 @@ bool show_master_info(THD* thd, Master_info* mi, bool full);
bool show_all_master_info(THD* thd);
void show_binlog_info_get_fields(THD *thd, List<Item> *field_list);
bool show_binlog_info(THD* thd);
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
bool (*pred)(const void *), const void *param,
bool maria_master= false);
bool rpl_master_erroneous_autoinc(THD* thd);
const char *print_slave_db_safe(const char *db);

View File

@@ -580,6 +580,7 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
m_thd_root(NULL),
m_thd(NULL),
m_pcont(new (&main_mem_root) sp_pcontext()),
m_pcont_list(NULL),
m_cont_level(0)
{
mem_root= &main_mem_root;
@@ -901,6 +902,17 @@ sp_head::~sp_head()
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
delete i;
delete_dynamic(&m_instr);
/*
Free the parse contexts iteratively, freeing them recursively overran
the thread stack on deeply nested BEGIN blocks.
*/
for (sp_pcontext *ctx= m_pcont_list; ctx; )
{
sp_pcontext *next= ctx->m_next_pcont;
delete ctx;
ctx= next;
}
delete m_pcont;
free_items();
@@ -1715,7 +1727,8 @@ sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
{
DBUG_ASSERT(args);
Row_definition_list defs;
m_pcont->retrieve_field_definitions(&defs);
if (m_pcont->retrieve_field_definitions(thd, &defs))
return NULL;
if (defs.adjust_formal_params_to_actual_params(thd, args))
return NULL;
return rcontext_create(thd, ret_value, &defs, true);
@@ -1726,7 +1739,8 @@ sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
Item **args, uint arg_count)
{
Row_definition_list defs;
m_pcont->retrieve_field_definitions(&defs);
if (m_pcont->retrieve_field_definitions(thd, &defs))
return NULL;
if (defs.adjust_formal_params_to_actual_params(thd, args, arg_count))
return NULL;
return rcontext_create(thd, ret_value, &defs, true);
@@ -1831,8 +1845,8 @@ sp_head::execute_trigger(THD *thd,
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
Row_definition_list defs;
m_pcont->retrieve_field_definitions(&defs);
if (!(nctx= rcontext_create(thd, NULL, &defs, false)))
if (m_pcont->retrieve_field_definitions(thd, &defs) ||
!(nctx= rcontext_create(thd, NULL, &defs, false)))
{
err_status= TRUE;
goto err_with_cleanup;

Some files were not shown because too many files have changed in this diff Show More