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:
3
.github/copilot-instructions.md
vendored
3
.github/copilot-instructions.md
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 "^/")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
|
||||
54
mysql-test/main/mdev_40698.result
Normal file
54
mysql-test/main/mdev_40698.result
Normal 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
|
||||
30
mysql-test/main/mdev_40698.test
Normal file
30
mysql-test/main/mdev_40698.test
Normal 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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 #
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
115
mysql-test/suite/galera/r/MDEV-41012-collision.result
Normal file
115
mysql-test/suite/galera/r/MDEV-41012-collision.result
Normal 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;
|
||||
104
mysql-test/suite/galera/r/MDEV-41012.result
Normal file
104
mysql-test/suite/galera/r/MDEV-41012.result
Normal 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;
|
||||
37
mysql-test/suite/galera/r/galera_fk_char_key.result
Normal file
37
mysql-test/suite/galera/r/galera_fk_char_key.result
Normal 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;
|
||||
19
mysql-test/suite/galera/r/galera_fk_char_key_big.result
Normal file
19
mysql-test/suite/galera/r/galera_fk_char_key_big.result
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
connection node_2;
|
||||
connection node_1;
|
||||
# Correct Galera library found
|
||||
connection node_1;
|
||||
connection node_2;
|
||||
connection node_2;
|
||||
|
||||
67
mysql-test/suite/galera/t/MDEV-41012-collision.inc
Normal file
67
mysql-test/suite/galera/t/MDEV-41012-collision.inc
Normal 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;
|
||||
64
mysql-test/suite/galera/t/MDEV-41012-collision.test
Normal file
64
mysql-test/suite/galera/t/MDEV-41012-collision.test
Normal 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
|
||||
67
mysql-test/suite/galera/t/MDEV-41012.inc
Normal file
67
mysql-test/suite/galera/t/MDEV-41012.inc
Normal 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;
|
||||
40
mysql-test/suite/galera/t/MDEV-41012.test
Normal file
40
mysql-test/suite/galera/t/MDEV-41012.test
Normal 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
|
||||
94
mysql-test/suite/galera/t/galera_fk_char_key.test
Normal file
94
mysql-test/suite/galera/t/galera_fk_char_key.test
Normal 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;
|
||||
130
mysql-test/suite/galera/t/galera_fk_char_key_big.test
Normal file
130
mysql-test/suite/galera/t/galera_fk_char_key_big.test
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
28
mysql-test/suite/innodb/include/assert_blob_not_purged.inc
Normal file
28
mysql-test/suite/innodb/include/assert_blob_not_purged.inc
Normal 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';
|
||||
46
mysql-test/suite/innodb/r/old_blob.result
Normal file
46
mysql-test/suite/innodb/r/old_blob.result
Normal 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;
|
||||
57
mysql-test/suite/innodb/r/old_blob_check.result
Normal file
57
mysql-test/suite/innodb/r/old_blob_check.result
Normal 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;
|
||||
90
mysql-test/suite/innodb/r/old_blob_purgeable.result
Normal file
90
mysql-test/suite/innodb/r/old_blob_purgeable.result
Normal 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;
|
||||
54
mysql-test/suite/innodb/r/old_blob_rollback.result
Normal file
54
mysql-test/suite/innodb/r/old_blob_rollback.result
Normal 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;
|
||||
72
mysql-test/suite/innodb/r/old_blob_updel.result
Normal file
72
mysql-test/suite/innodb/r/old_blob_updel.result
Normal 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;
|
||||
75
mysql-test/suite/innodb/t/old_blob.test
Normal file
75
mysql-test/suite/innodb/t/old_blob.test
Normal 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
|
||||
96
mysql-test/suite/innodb/t/old_blob_check.test
Normal file
96
mysql-test/suite/innodb/t/old_blob_check.test
Normal 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
|
||||
111
mysql-test/suite/innodb/t/old_blob_purgeable.test
Normal file
111
mysql-test/suite/innodb/t/old_blob_purgeable.test
Normal 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;
|
||||
99
mysql-test/suite/innodb/t/old_blob_rollback.test
Normal file
99
mysql-test/suite/innodb/t/old_blob_rollback.test
Normal 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
|
||||
118
mysql-test/suite/innodb/t/old_blob_updel.test
Normal file
118
mysql-test/suite/innodb/t/old_blob_updel.test
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 #
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
25
mysql-test/suite/multi_source/rpl_filter_default_master.test
Normal file
25
mysql-test/suite/multi_source/rpl_filter_default_master.test
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*);
|
||||
};
|
||||
|
||||
|
||||
12
sql/item.cc
12
sql/item.cc
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
13
sql/item.h
13
sql/item.h
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
46
sql/log.cc
46
sql/log.cc
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, >id, 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
162
sql/rpl_rli.cc
162
sql/rpl_rli.cc
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
101
sql/slave.cc
101
sql/slave.cc
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user