1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

Merge branch '11.8' into '12.3'

This commit is contained in:
forkfun
2026-06-24 12:27:13 +02:00
60 changed files with 976 additions and 890 deletions

View File

@@ -1,380 +1,179 @@
# Coding Standards
This is a working document outlining the coding standard for the general MariaDB codebase.
The document can be found in the 11.0 and newer trees in the root directory as "CODING_STANDARDS.md"
This document outlines the coding standard for this repository.
It does not cover the coding standards for individual plugins, these should have their own coding standards documentation.
It can be overwritten or amended by `CODING_STANDARDS.md` documents in
individual subdirectories.
## Using Git with the MariaDB codebase
This is a concise agent-oriented version, which highlights differences
between "default" coding style that an agent would use anyway and MariaDB.
See human-oriented version at https://mariadb.org/about/coding-style/
### Git commit messages
## Match the surrounding codebase
Git commit messages must conform to the 50/72 rule.
This is a de facto git standard which is automatically enforced by some editors.
This means:
**This is the primary rule.** When writing code for MariaDB:
* 50 characters max for the first (description) line (see exception later)
* A blank line.
* 72 characters max for every subsequent line.
- Keep the code consistent.
- Use the same terminology, naming conventions, types, helpers, and data
structures as the existing code in the area you are touching.
- Before writing a new utility function or container, grep `mysys/` and
`include/` — it very likely already exists.
- Do not introduce STL containers or other constructs that duplicate
things already in the codebase.
In addition if there is a Jira ticket number, this should be the first thing in the description.
As an example:
## Formatting
```
MDEV-12345 Fixing Rockwell Turbo Encabulator
### Assignment spacing
The new principle involved is that instead of power being generated by
the relative motion of conductors and fluxes, it’s produced by the
modial interaction of magneto-reluctance and capacitive directance.
```
The only explicitly allowed exception to the 50-72 rules is that if the first line can be MDEV-###### title', even if the title would make the line longer than 50 characters.
The commit messages are typically rendered in [Markdown format](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), so markdown formatting is permitted for the message body.
### Branch handling
When updating your code, please make sure you perform a rebase, not a merge with the latest branch.
Pull requests should be a simple fast-forward of the branch they are intended to land on.
The correct way to rebase (if working on top of 10.11 branch):
```sh
git fetch upstream/10.11 # This assumes upstream is github.com/MariaDB/server
git rebase upstream/10.11
git push --force my_branch
```
### Target branch
Pull requests should be based against the correct MariaDB version.
New features should be based against the `main` branch, which is the GitHub default branch.
Bug fixes should be based against the earliest maintained branch in which the bug can be reproduced.
The earliest maintained branch is found at https://mariadb.org/about/#maintenance-policy.
## Coding Style (C / C++ files)
Everyone has a preferred coding style, there is no real correct style for all projects around the world.
What is important is that we stick to one common style throughout this code base.
### Indentation
We should use a variant of the [Allman indentation style](https://en.wikipedia.org/wiki/Indentation_style#Allman_style).
The variation is to use two spaces instead of tabs and has a couple of minor rule changes as below.
Allman style specifies that braces associated with a statement should be on the following line with the same indentation and the statements inside the braces are next level indented.
The closing braces are also on a new line at the same indentation as the original statement.
For example:
No space before `=`, one space after — **non-standard but grep-friendly**:
```cpp
while (x == y)
{
something();
somethingelse();
}
finalthing();
a= 1; // correct
a = 1; // wrong
```
#### Switch / Case statements
### Naming
For switch / case statements the `case` needs to be inline with the `switch`.
Preferably switch (expr) should be followed by '{' on the same line to
make the lineup of 'case:' nice:
For example:
- Variables and functions: `snake_case`
- Classes and structs: `Upper_snake_case` — first letter uppercase, rest
snake_case:
```cpp
switch(level) {
case ERROR:
sql_print_error("Error: %s", message.c_ptr_safe());
break;
case WARNING:
sql_print_warning("Warning: %s", message.c_ptr_safe());
break;
...
}
class Buffered_logs { ... };
```
#### If statements
### Pointer declarator
If the `if` statement only executes one line of code it is possible to write the statement without the braces such as this:
`*` binds to the name, not the type:
```cpp
if (opt_console)
opt_error_log= 0;
void my_function(THD *thd);
char *buf;
```
Prefer reducing indent level with the use of early return statements (or in special circumstances goto).
Rather than:
### Line length
80 characters max. When breaking a long line:
- Leave binary operators at the **end** of the broken line.
- Use `()` to guide alignment:
```cpp
if (condition)
{
<logic>
}
return error_code;
```
Use:
```cpp
if (!condition)
return error_code;
<logic>
return success;
```
#### Functions
Consecutive functions should be separated with 2 empty lines in between
```cpp
void my_function_1()
{
<logic>
}
void my_function_2()
{
<logic>
}
```
#### Preprocessor directives
Compiler preprocessor directives should have no indentation to them, even if in the middle of indented code.
For example:
```c
case SSL_TYPE_NONE: // SSL is not required
if (opt_require_secure_transport)
{
enum enum_vio_type type= vio_type(vio);
#ifdef HAVE_OPENSSL
return type != VIO_TYPE_SSL &&
#ifndef _WIN32
type != VIO_TYPE_SOCKET;
#else
type != VIO_TYPE_NAMEDPIPE;
#endif
#else
#ifndef _WIN32
return type != VIO_TYPE_SOCKET;
#else
return type != VIO_TYPE_NAMEDPIPE;
#endif
#endif
}
```
Comments reflecting the original `#if` condition can be appended to `#else` / `#endif` to provide additional clarity. This can be useful for large code blocks between the start and end preprocessor directives or nested preprocessor directives.
For example:
```c
#ifndef EMBEDDED_LIBRARY
...
#else /* ! EMBEDDED_LIBRARY */
...
#endif /* ! EMBEDDED_LIBRARY */
```
### File names
File names should be lower case with underscore word separators.
C file names use the `.c` extension, C++ files use the `.cc` extension and header files use the `.h` extension.
### Language standards
For pure-C files we use C99 (starting with 10.4.25) and for C++ we use C++17 (starting with 11.8.1).
The code need to be able to compile on multiple platforms using different compilers (for example: Windows / Linux, x86_64 / ARM).
### Line lengths
Lines should be no more than 80 characters.
The reason for this is that it makes it easier to have multiple editor
windows open side by side and still keep code readable without line
wraps.
When breaking long lines:
- use '()' to group expressions so that the editor can automatically help
you with the indentation.
- When breaking expressions, leave the operator (+,- etc) last on the previous
line.
```cpp
rows= tab->table->file->multi_range_read_info(tab->ref.key, 10, 20, tab->ref.key_parts, &bufsz, &flags, &cost);
->
rows= tab->table->file->multi_range_read_info(tab->ref.key, 10, 20,
tab->ref.key_parts, &bufsz,
&flags, &cost);
```
```cpp
tmp= aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;
->
tmp= (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa+
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb);
tab->ref.key_parts,
&bufsz, &flags, &cost);
```
### Comments
Single line / inline code comments can use the double slash (`//`) style of coding, whereas multi-line code comments should use `/*` as a start and `*/` at the end, with the text indented by 2 spaces, for example:
- Single-line / inline: `//`
- Multi-line: `/*` / `*/` with 2-space indent on the body:
```cpp
/*
This is a multi-line code comment.
It has an indentation of two spaces.
This explains a non-obvious invariant.
Second line.
*/
```
### Variables classes, and functions
### Whitespace
- No trailing whitespace on any line.
- No trailing blank lines at end of file.
- Line endings: LF (`\n`), not CRLF.
- No tabs
## Types
Avoid `long` and `unsigned long` (and the `ulong` alias) — `long` is 32-bit
on Windows but 64-bit on Linux/macOS, creating portability issues.
Absolutely avoid them in anything user-visible, configurable variables
and command-line options, no exceptions here.
Special-purpose:
- `File` — integer file descriptor
- `my_socket` — integer socket descriptor
- `size_t` / `ptrdiff_t` — buffer sizes and pointer arithmetic
## Memory allocation
Choose the allocator by lifetime:
- **Query-scoped (short-lived):** use `MEM_ROOT` — the entire pool is freed
at once at query end. Preferred forms:
```cpp
new (thd->mem_root) T(...)
alloc_root(mem_root, n)
thd->strmake(str, len)
```
pay attention to the `MEM_ROOT` life time.
- **Long-lived or large:** use `my_malloc` / `my_free` or plain `new` / `delete`:
```cpp
my_malloc(PSI_INSTRUMENT_ME, n, MYF(MY_WME))
```
- **Copying bytes/strings:** `my_memdup()` / `my_strdup()` / `my_strndup()`
instead of malloc+memcpy/strcpy.
- **Multiple objects in one call:** `my_multi_malloc()`.
## Strings
- `LEX_CSTRING` / `LEX_STRING` — a `(const char *, length)` pair; use for
charset-less constant strings or where a charset is always the same,
as in Lex_ident* family.
- `String` — dynamic, charset-aware string class; use when charset matters
or the string grows.
- use `StringBuffer<N>` template.
String helper functions (prefer over stdlib equivalents):
| Use | Instead of |
|-----|-----------|
| `strnmov(dst, src)` | `strcpy` (returns end pointer, chains well) |
| `strmake(dst, src, len)` | `strncpy` (always null-terminates) |
| `strxnmov(dst, s1, s2, ..., NullS)` | manual multi-part concatenation |
## Error handling
- Functions that can fail return `bool`: **`false` = success, `true` = error.**
- Integer error codes: **0 = success, non-zero = error code**
Variables and functions should be descriptive and in "snake case", for example:
```cpp
void my_function(uint16 variable_name)
{
if (do_something(thd)) // true means error
goto err;
```
Class names should also be "snake case" but should start with an upper-case character.
Such as this:
- Report errors with `my_error(ER_CODE, MYF(0), ...)`.
- Check for a prior error with `thd->is_error()`.
- Use `goto err` with a cleanup label for failure paths.
```cpp
class Buffered_logs
{
## Data structures
Prefer these over STL equivalents:
- `List<T>` / `List_iterator<T>` — doubly-linked list
- `Dynamic_array<T>` / `DYNAMIC_ARRAY`— growable array
- `Hash<T>` / `HASH` — hash table
- `TREE` — ordered binary search tree (with built-in `MEM_ROOT` pooling)
- `Queue<T>` / `QUEUE` — priority queue
- `IO_CACHE` — buffered sequential file I/O
## Git
### Commit messages
```
MDEV-12345 exactly issue summary as in Jira
Body wrapped at 72 characters per line. Subject is normally
50 characters max, but may exceed that when it is the exact
MDEV ticket title.
```
Assignments should not have a space on the left side of the equals, and one space on the right hand side. For example:
- JIRA ticket number (if any) must be the first thing on the subject line.
- Body is rendered as Markdown.
```cpp
a= 1; // Correct
a = 1; // Incorrect for the server code,
// ok for Storage Engines if they use it (aka Connect)
```
### Target branch
The above makes it easy to use 'grep' to find all assignments to a variable.
Please do not write conditions like this:
```cpp
if (0 == *error_code)
```
Please do this instead:
```cpp
if (*error_code == 0)
// Or even better
if (!*error_code)
```
Only use one-character variables (i,j,k...) in short loops. For anything else
use descriptive names!
### Variable declarations
Variables should be declared at the start of it's context (start of function, inside the 'if' statement.
The benefits of this:
- Code lines gets shorter
- It is easier to see the stack space used by a function.
- It is easier to find the declaration of the variable.
- If one has to add an 'if (error) goto end' construct, one can do
that without having to move variable declarations around.
### Variable initializations
Variables can be initialized using assignment operator or initializer and expression list.
For Example:
```cpp
int milliseconds= 1000;
char type= 't';
```
Or
```cpp
int milliseconds{1000};
char type{'t'};
```
### Constant integers
Constant integers that are used to define elements such as buffer sizes should be defined rather than used directly.
This is because the integer could change and uses of it could be missed.
For example:
```cpp
char *buffer= my_malloc(PSI_INSTRUMENT_ME, 1024, MYF(MY_WME));
snprint(buffer, 1024, "%d: %s", integer, text);
```
Could become:
```cpp
constexpr int buffer_size= 1024;
char *buffer= my_malloc(PSI_INSTRUMENT_ME, buffer_size, MYF(MY_WME));
snprint(buffer, buffer_size, "%d: %s", integer, text);
```
Alternatively the integer can be defined using an `enum` or `#define`.
### Spacing
#### Whitespace
* Lines should not have any trailing whitespace.
* There should not be any trailing blank lines at the end of a file.
* Line endings are POSIX style (`\n`).
* Two spaces for each indentation level, not tabs.
#### Pointers
The `*` of a pointer should be on the side of the variable name such as:
```cpp
void my_function(THD *thd)
{
```
As yet there is no standard as to whether the `*` in a casting should have a space or not.
Both of these are valid:
```cpp
name= (const char*)db_name;
name= (const char *) db_name;
```
#### Function variables
There should be a space after each comma in a definition and usage of a function.
For example:
```cpp
my_function(thd, db_name);
```
### Integer types
The usage of types `long` and `unsigned long` (and its `ulong` alias)
in new code is *strongly* discouraged. Its use brings no advantages,
only portability problems between Windows and Unixes. The reason for it is that `long`
appears to be the only standard C/C++ datatype, with [size that differs between mainstream 64bit OSes](https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models)
Instead of using `long`, use `size_t` and `ptrdiff_t` where appropriate,
buffer sizes for example. For integer socket descriptor use `my_socket`.
You may use types with fixed length, int32_t and similar, too. Yet, on all platforms we currently support,
* `char` is 8 bit
* `short` is 16 bit
* `int` is 32bit
* `long long` is 64bit
and the above is not likely to change for the decades to come. Those types are safe to use. When using `char`
though, be aware that its signed-ness can depend on compiler flags, so do not assume it can take negative values.
- New features → `main` branch.
- Bug fixes → oldest maintained branch that reproduces the bug, but not
older than three years since its first GA release.

View File

@@ -45,7 +45,7 @@ SET(CLIENT_PLUGIN_PVIO_SHMEM STATIC)
SET(CLIENT_PLUGIN_PVIO_SOCKET STATIC)
MESSAGE("== Configuring MariaDB Connector/C")
ADD_SUBDIRECTORY(libmariadb)
ADD_SUBMODULE_SUBDIRECTORY(libmariadb)
IF(MSVC AND TARGET mariadb_obj AND TARGET mariadbclient)
# With MSVC, do not produce LTCG-compiled static client libraries.

View File

@@ -57,6 +57,7 @@ MACRO (MYSQL_USE_BUNDLED_SSL)
SET(HAVE_evp_pkey ON CACHE INTERNAL "wolfssl does support EVP_PKEY API")
SET(HAVE_hkdf ON CACHE INTERNAL "wolfssl does support EVP_PKEY_HKDF API")
CHANGE_SSL_SETTINGS("bundled")
ADD_SUBMODULE(extra/wolfssl/wolfssl)
ADD_SUBDIRECTORY(extra/wolfssl)
MESSAGE_ONCE(SSL_LIBRARIES "SSL_LIBRARIES = ${SSL_LIBRARIES}")
ENDMACRO()

View File

@@ -1,50 +1,54 @@
# update submodules automatically
OPTION(UPDATE_SUBMODULES "Update submodules automatically" ON)
IF(NOT UPDATE_SUBMODULES)
RETURN()
ENDIF()
IF(GIT_EXECUTABLE AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
IF(NOT UPDATE_SUBMODULES)
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE "Disabled by -DUPDATE_SUBMODULES=OFF")
ELSEIF(NOT GIT_EXECUTABLE)
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE "git executable was not found")
ELSEIF(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE "Not inside a git repository")
ELSE()
EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" config --get cmake.update-submodules
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE cmake_update_submodules
OUTPUT_VARIABLE CMAKE_UPDATE_SUBMODULES
RESULT_VARIABLE git_config_get_result)
IF(cmake_update_submodules MATCHES no)
IF(CMAKE_UPDATE_SUBMODULES MATCHES no)
SET(update_result 0)
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE
"\n\nTo update submodules automatically, set cmake.update-submodules to 'yes', or 'force' to update automatically:
${GIT_EXECUTABLE} config cmake.update-submodules yes")
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE "Disabled by git config. To enable set cmake.update-submodules to 'yes', or 'force': ${GIT_EXECUTABLE} config cmake.update-submodules yes")
ELSEIF(git_config_get_result EQUAL 128)
SET(update_result 0)
ELSE()
SET(UPDATE_SUBMODULES_COMMAND
"${GIT_EXECUTABLE}" submodule update --init --recursive)
# Old Git may not work with "--depth 1".
# See also: https://github.com/git/git/commit/fb43e31f2b43076e7a30c9cd00d0241cb8cf97eb
IF(NOT GIT_VERSION_STRING VERSION_LESS "2.8.0")
SET(UPDATE_SUBMODULES_COMMAND ${UPDATE_SUBMODULES_COMMAND} --depth 1)
ENDIF()
IF(cmake_update_submodules MATCHES force)
MESSAGE(STATUS "Updating submodules (forced)")
EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} --force
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE update_result)
ELSEIF(cmake_update_submodules MATCHES yes)
EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE update_result)
ELSE()
MESSAGE(STATUS "Updating submodules")
EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE update_result)
ENDIF()
SET(SUBMODULE_UPDATE_CONFIG_MESSAGE "Git executable ${GIT_EXECUTABLE} failed to run")
ENDIF()
ENDIF()
IF(update_result OR NOT EXISTS ${CMAKE_SOURCE_DIR}/libmariadb/CMakeLists.txt)
MESSAGE(FATAL_ERROR "No MariaDB Connector/C! Run
${GIT_EXECUTABLE} submodule update --init --recursive
Then restart the build.${SUBMODULE_UPDATE_CONFIG_MESSAGE}")
ENDIF()
FUNCTION(ADD_SUBMODULE dir)
IF (ARGV1)
SET(file "${ARGV1}")
ELSE()
SET(file CMakeLists.txt)
ENDIF()
IF(SUBMODULE_UPDATE_CONFIG_MESSAGE)
IF(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/${file})
MESSAGE(FATAL_ERROR "Cannot download ${CMAKE_CURRENT_SOURCE_DIR}/${dir} submodule: ${SUBMODULE_UPDATE_CONFIG_MESSAGE}")
ENDIF()
ELSE()
MESSAGE(STATUS "Downloading ${CMAKE_CURRENT_SOURCE_DIR}/${dir} submodule...")
SET(UPDATE_SUBMODULES_COMMAND
"${GIT_EXECUTABLE}" submodule update --init --recursive --depth 1)
IF(CMAKE_UPDATE_SUBMODULES MATCHES force)
EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} --force ${dir}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE update_result)
ELSE()
EXECUTE_PROCESS(COMMAND ${UPDATE_SUBMODULES_COMMAND} ${dir}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE update_result)
ENDIF()
IF(update_result OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/${file})
MESSAGE(FATAL_ERROR "Failed to download ${CMAKE_CURRENT_SOURCE_DIR}/${dir} submodule")
ENDIF()
ENDIF()
ENDFUNCTION()
MACRO(ADD_SUBMODULE_SUBDIRECTORY dir)
ADD_SUBMODULE(${dir})
ADD_SUBDIRECTORY(${dir})
ENDMACRO()

View File

@@ -35,12 +35,8 @@ IF(WITH_WSREP)
# Set the patch version
SET(WSREP_PATCH_VERSION "22")
IF(NOT EXISTS "${CMAKE_SOURCE_DIR}/wsrep-lib/wsrep-API/v26/wsrep_api.h")
MESSAGE(FATAL_ERROR "No MariaDB wsrep-API code! Run
${GIT_EXECUTABLE} submodule update --init --recursive
Then restart the build.
")
ENDIF()
ADD_SUBMODULE(wsrep-lib)
# Obtain wsrep API version
FILE(STRINGS "${CMAKE_SOURCE_DIR}/wsrep-lib/wsrep-API/v26/wsrep_api.h" WSREP_API_VERSION
LIMIT_COUNT 1 REGEX "WSREP_INTERFACE_VERSION")

View File

@@ -162,7 +162,8 @@ then
# On Gitlab the output log must stay under 4MB so make the
# build less verbose
sed '/Add support for verbose builds/,/^$/d' -i debian/rules
elif [[ -d storage/columnstore/columnstore/debian ]] && [[ "$LSBNAME" = !(buster|bionic) ]]
elif grep -qs "$architecture" storage/columnstore/columnstore/debian/control && \
[[ "$LSBNAME" = !(buster|bionic) ]]
then
# ColumnStore is explicitly disabled in the native Debian build. Enable it
# now when build is triggered by autobake-deb.sh (MariaDB.org) and when the

View File

@@ -289,3 +289,11 @@ t1 CREATE TABLE `t1` (
`a` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
DROP TABLE t1;
#
# MDEV-40059 too long character_set_collations crash
#
SET @@session.character_set_collations='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=x';
ERROR 42000: Variable 'character_set_collations' can't be set to the value of 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...'
SET @@session.character_set_collations='utf8mb3=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
ERROR 42000: Variable 'character_set_collations' can't be set to the value of 'utf8mb3=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...'
# End of 11.4 tests

View File

@@ -235,3 +235,13 @@ SET @@character_set_collations='';
EXECUTE stmt;
SHOW CREATE TABLE t1;
DROP TABLE t1;
--echo #
--echo # MDEV-40059 too long character_set_collations crash
--echo #
--error ER_WRONG_VALUE_FOR_VAR
SET @@session.character_set_collations='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=x';
--error ER_WRONG_VALUE_FOR_VAR
SET @@session.character_set_collations='utf8mb3=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
--echo # End of 11.4 tests

View File

@@ -1,4 +1,4 @@
--loose-enable-performance-schema
--max-allowed-packet=32000000
--proxy-protocol-networks=::1/32,127.0.0.0/8,localhost
--proxy-protocol-networks=::1/32,127.0.0.0/8,localhost,2001:db8::6:6
--sequence=on

View File

@@ -1,4 +1,4 @@
--general-log --general-log-file=$MYSQLTEST_VARDIR/log/master.log --log-output=FILE,TABLE
--max-allowed-packet=32000000
--proxy-protocol-networks=::1,::ffff:127.0.0.1/97,localhost
--proxy-protocol-networks=::1,::ffff:127.0.0.1/97,localhost,2001:db8::6:6
--sequence=on

View File

@@ -2122,6 +2122,23 @@ i
2
1
1
DROP TEMPORARY TABLE t1;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
SET sql_mode=@save_sql_mode;
#
# MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1;
CREATE VIEW v1 AS SELECT 0 AS a;
UPDATE t1 SET a = 3;
ERROR HY000: The target table v1 of the DELETE is not updatable
CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a;
UPDATE t1 SET a = 4;
ERROR HY000: The target table v1 of the DELETE is not updatable
# restart
# Clean up
DROP VIEW v1;
DROP TABLE t1;

View File

@@ -2923,9 +2923,33 @@ $
--delimiter ;
CALL pkg1.p2();
DROP TEMPORARY TABLE t1;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
SET sql_mode=@save_sql_mode;
--echo #
--echo # MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1;
CREATE VIEW v1 AS SELECT 0 AS a;
--error ER_NON_UPDATABLE_TABLE
UPDATE t1 SET a = 3;
CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a;
--error ER_NON_UPDATABLE_TABLE
UPDATE t1 SET a = 4;
--source include/restart_mysqld.inc
--echo # Clean up
DROP VIEW v1;
DROP TABLE t1;
--enable_ps2_protocol

View File

@@ -0,0 +1,24 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
#
# MDEV-39413 wsrep unsafe handling of parameters
#
call mtr.add_suppression("WSREP: Illegal character in state transfer request:");
call mtr.add_suppression("WSREP: Bad remote auth string. SST canceled.");
call mtr.add_suppression("WSREP: SST preparation failed");
call mtr.add_suppression("WSREP: SST failed:");
call mtr.add_suppression(".*State transfer to:*");
call mtr.add_suppression("WSREP: Illegal character in variable:");
connection node_2;
# force SST again
# using a cert with shell-unsafe CN
# start the server
# the server failed to start
FOUND 1 /Bad remote auth string. SST canceled./ in mysqld.1.err
# cleanup
# restart
call mtr.add_suppression(".*WSREP.*Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP_SST:");

View File

@@ -1,27 +0,0 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
connection node_2;
FOUND 1 /wsrep_sst_rsync/ in mysqld.1.err
connection node_1;
call mtr.add_suppression('Invalid value for WSREP_SST_OPT_REMOTE_USER');
call mtr.add_suppression('Failed to read from: wsrep_sst_rsync');
call mtr.add_suppression('Process completed with error: wsrep_sst_rsync');
call mtr.add_suppression('Command did not run: wsrep_sst_rsync');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Error while getting data from donor node');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('Removing .*/sst_in_progress');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
connection node_2;
connection node_1;
FOUND 1 /Invalid value for WSREP_SST_OPT_REMOTE_USER/ in mysqld.1.err
connection node_2;
# restart
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('State transfer to .* failed');

View File

@@ -1,7 +1,6 @@
!include ../galera_2nodes.cnf
[mysqld]
wsrep_sst_method=rsync
wsrep_sst_auth="root:"
wsrep_debug=1

View File

@@ -0,0 +1,6 @@
[rsync]
wsrep-sst-method=rsync
[mariabackup]
wsrep-sst-method=mariabackup

View File

@@ -20,24 +20,16 @@ SELECT 1;
--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
--source include/wait_condition.inc
# Confirm that transfer was SSL-encrypted
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_PATTERN = Using openssl based encryption with socat: with key and crt
--source include/search_pattern_in_file.inc
--echo #
--echo # MDEV-39413 wsrep unsafe handling of parameters
--echo #
call mtr.add_suppression('Invalid value for WSREP_SST_OPT_REMOTE_USER');
call mtr.add_suppression('Failed to read from: wsrep_sst_mariabackup');
call mtr.add_suppression('Process completed with error: wsrep_sst_mariabackup');
call mtr.add_suppression('Command did not run: wsrep_sst_mariabackup');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Error while getting data from donor node');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('Removing .*/xtrabackup_galera_info file due to signal');
call mtr.add_suppression("WSREP: Illegal character in state transfer request:");
call mtr.add_suppression("WSREP: Bad remote auth string. SST canceled.");
call mtr.add_suppression("WSREP: SST preparation failed");
call mtr.add_suppression("WSREP: SST failed:");
call mtr.add_suppression(".*State transfer to:*");
call mtr.add_suppression("WSREP: Illegal character in variable:");
--connection node_2
--source include/shutdown_mysqld.inc
@@ -56,18 +48,20 @@ call mtr.add_suppression('Removing .*/xtrabackup_galera_info file due to signal'
--exec $MYSQLD_LAST_CMD
--echo # the server failed to start
--let SEARCH_PATTERN = Invalid value for WSREP_SST_OPT_REMOTE_USER
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_PATTERN = Bad remote auth string. SST canceled.
--source include/search_pattern_in_file.inc
--echo # cleanup
# we have to kill joiner's socat here, because the donor has aborted SST
# and joiner's socat will timeout in 5 minutes
# pkill exit code varies by platform 0 or 1
--error 0,1
--error 0,1,15
--exec pkill -f 'socat.*server-new-cert'
--exec echo ssl-cert=$MYSQL_TEST_DIR/std_data/server-cert.pem >> $MYSQLTEST_VARDIR/my.cnf
--exec echo ssl-key=$MYSQL_TEST_DIR/std_data/server-key.pem >> $MYSQLTEST_VARDIR/my.cnf
--source $MYSQL_TEST_DIR/include/start_mysqld.inc
--connection node_2
call mtr.add_suppression("Removing");
call mtr.add_suppression(".*WSREP.*Will never receive state. Need to abort.");
call mtr.add_suppression("WSREP_SST:");

View File

@@ -1,13 +0,0 @@
!include ../galera_2nodes.cnf
[mysqld]
wsrep_sst_method=mariabackup
wsrep_sst_auth="root:"
wsrep_debug=1
ssl-cert=@ENV.MYSQL_TEST_DIR/std_data/server-cert.pem
ssl-key=@ENV.MYSQL_TEST_DIR/std_data/server-key.pem
ssl-ca=@ENV.MYSQL_TEST_DIR/std_data/cacert.pem
[sst]
ssl-mode=VERIFY_CA

View File

@@ -1,99 +0,0 @@
#
# Verifies that wsrep_sst_rsync.sh rejects a joiner-supplied certificate
# whose CN contains shell-unsafe characters.
#
# Brings up a 2-node cluster with rsync SST and ssl-mode=VERIFY_CA, then
# forces a fresh SST on node_2 using std_data/server-new-cert.pem -- a
# cert whose CN intentionally contains shell metacharacters. Confirms
# that the donor (node_1) logs
# "Invalid value for WSREP_SST_OPT_REMOTE_USER"
# i.e. the rsync SST script refuses the value rather than interpolating
# it into stunnel.conf or the rsync magic file.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
SELECT 1;
--connection node_2
--let $wait_condition = SELECT VARIABLE_VALUE = 'Synced' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'WSREP_LOCAL_STATE_COMMENT'
--source include/wait_condition.inc
# Confirm the initial SST went via rsync + stunnel (sanity check for the
# test configuration).
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_PATTERN = wsrep_sst_rsync
--source include/search_pattern_in_file.inc
# Reject shell-unsafe joiner-supplied auth (rsync)
# Suppressions are per-server. node_1 will log the donor-side rejection
# ("Invalid value for WSREP_SST_OPT_REMOTE_USER"); node_2 will log the
# joiner-side "Will never receive state" abort. Add to both.
--connection node_1
call mtr.add_suppression('Invalid value for WSREP_SST_OPT_REMOTE_USER');
call mtr.add_suppression('Failed to read from: wsrep_sst_rsync');
call mtr.add_suppression('Process completed with error: wsrep_sst_rsync');
call mtr.add_suppression('Command did not run: wsrep_sst_rsync');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Error while getting data from donor node');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('Removing .*/sst_in_progress');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
--connection node_2
--source include/shutdown_mysqld.inc
# force SST again
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
# using a cert with shell-unsafe CN
--exec echo '[mysqld.2]' >> $MYSQLTEST_VARDIR/my.cnf
--exec echo ssl-cert=$MYSQL_TEST_DIR/std_data/server-new-cert.pem >> $MYSQLTEST_VARDIR/my.cnf
--exec echo ssl-key=$MYSQL_TEST_DIR/std_data/server-new-key.pem >> $MYSQLTEST_VARDIR/my.cnf
# start the server
# Joiner mariadbd exits when SST is aborted; the exit code varies by
# platform (clean 0 on some systems, signalled 134 / 1 on others).
--error 0,1,134
--exec $MYSQLD_LAST_CMD
# the donor refused the SST request
--connection node_1
# safe() in wsrep_sst_common.sh logs this when it rejects the joiner CN;
# wsrep_sst_rsync.sh wraps the joiner-supplied REMOTE_USER with $(safe ..)
# at line 249 so the value never reaches the stunnel.conf heredoc.
--let SEARCH_PATTERN = Invalid value for WSREP_SST_OPT_REMOTE_USER
--source include/search_pattern_in_file.inc
# cleanup
# Kill joiner's stunnel / rsync that may linger after the aborted SST.
# Use a perl block because --exec with pkill -f matches the mtr cmdline
# itself (which contains the pattern) and tears down the wrong process.
perl;
open(my $fh, '-|', 'ps', '-eo', 'pid,args') or die "ps: $!";
while (<$fh>) {
next unless /server-new-cert/;
next unless /^\s*(\d+)\s+(?:.*\/)?(stunnel|socat|rsync)\b/;
kill 'TERM', $1;
}
close $fh;
EOF
--exec echo ssl-cert=$MYSQL_TEST_DIR/std_data/server-cert.pem >> $MYSQLTEST_VARDIR/my.cnf
--exec echo ssl-key=$MYSQL_TEST_DIR/std_data/server-key.pem >> $MYSQLTEST_VARDIR/my.cnf
# Switch back to node_2 before restarting it; the connection associates
# with the soon-to-be-restarted server so mtr auto-reconnects and the
# wait_condition + late suppressions land on the new instance.
--connection node_2
--source $MYSQL_TEST_DIR/include/start_mysqld.inc
--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('State transfer to .* failed');

View File

@@ -0,0 +1,55 @@
connection node_3;
connection node_2;
connection node_1;
connection node_1;
connection node_2;
connection node_3;
connection node_1;
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 0),(2, 0),(3, 0),(4, 0);
connection node_2;
SET SESSION wsrep_on=OFF;
DELETE FROM t1 WHERE f1 = 2;
SET SESSION wsrep_on=ON;
SET @@global.debug_dbug='+d,simulate_rollback_failure_in_applier';
connection node_1;
UPDATE t1 SET f2 = 1 WHERE f1 = 2;
connection node_2;
SET SESSION wsrep_on = ON;
SET SESSION wsrep_sync_wait = 15;
SET SESSION wsrep_on = ON;
SET SESSION wsrep_sync_wait = 15;
connection node_1;
SET SESSION wsrep_on = ON;
SET SESSION wsrep_sync_wait = 15;
SHOW STATUS LIKE 'wsrep_cluster_size';
Variable_name Value
wsrep_cluster_size 2
INSERT INTO t1 VALUES (5, 0);
SELECT * FROM t1 ORDER BY f1;
f1 f2
1 0
2 1
3 0
4 0
5 0
connection node_3;
SELECT * FROM t1 ORDER BY f1;
f1 f2
1 0
2 1
3 0
4 0
5 0
connection node_2;
SET SESSION wsrep_on=OFF;
# restart
connection node_1;
DROP TABLE t1;
connection node_2;
CALL mtr.add_suppression("Can't find record in 't1'");
CALL mtr.add_suppression("Update_rows_v1 apply failed");
CALL mtr.add_suppression("Inconsistency detected: Inconsistent by consensus on");
CALL mtr.add_suppression("WSREP: Failed to apply write set: ");
CALL mtr.add_suppression("Wsrep_high_priority_service::rollback: trans_rollback returned");
CALL mtr.add_suppression("last left .* greater than drain seqno");

View File

@@ -0,0 +1,8 @@
!include ../galera_3nodes.cnf
[mysqld]
wsrep-slave-threads=4
wsrep-ignore-apply-errors=0
[ENV]
galera_cluster_size=3

View File

@@ -0,0 +1,94 @@
#
# MDEV-38843: BF applier failed on a node causing complete Cluster lockup
#
# On a 3-node cluster, make node 2's applier fail to apply a write set:
# a local (wsrep_on=OFF) DELETE removes a row, then an UPDATE of that row
# from node 1 cannot be applied on node 2. The DBUG injection
# simulate_rollback_failure_in_applier additionally forces the applier's
# transaction rollback to fail, exercising the apply-error path.
#
# Verify that node 2 loses the inconsistency vote and disconnects instead
# of hanging, that the surviving nodes 1 and 3 keep committing, and that
# node 2 can restart and rejoin the cluster.
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/galera_have_debug_sync.inc
# Save original auto_increment_offset values.
--let $node_1=node_1
--let $node_2=node_2
--let $node_3=node_3
--source ../galera/include/auto_increment_offset_save.inc
--connection node_1
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY, f2 INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, 0),(2, 0),(3, 0),(4, 0);
--connection node_2
# Wait until node 2 has applied the rows before creating the local
# inconsistency.
--let $wait_condition = SELECT COUNT(*) = 4 FROM t1
--source include/wait_condition.inc
# Introduce inconsistency so the applier will fail on the next UPDATE.
SET SESSION wsrep_on=OFF;
DELETE FROM t1 WHERE f1 = 2;
SET SESSION wsrep_on=ON;
# Force the applier's transaction rollback to return non-zero on apply error.
# Pre-fix, this would skip the call that passes the apply error to the
# Galera provider, leaving the seqno stuck in commit order and the cluster
# hanging. Post-fix the call always runs and the vote completes.
SET @@global.debug_dbug='+d,simulate_rollback_failure_in_applier';
--connection node_1
UPDATE t1 SET f2 = 1 WHERE f1 = 2;
--connection node_2
# Node 2 lost the consensus vote — it must disconnect from the cluster
# rather than silently hang. Without the fix, this step times out.
--source include/wsrep_wait_disconnect.inc
--let $members=0
--source include/wsrep_wait_membership.inc
--connection node_1
# Survivors form a 2-member primary component.
--let $members=2
--source include/wsrep_wait_membership.inc
--source include/wait_until_ready.inc
SHOW STATUS LIKE 'wsrep_cluster_size';
# The cluster keeps making progress after the eviction.
INSERT INTO t1 VALUES (5, 0);
SELECT * FROM t1 ORDER BY f1;
--connection node_3
--let $wait_condition = SELECT COUNT(*)=5 FROM t1
--source include/wait_condition.inc
SELECT * FROM t1 ORDER BY f1;
--connection node_2
# Restart the evicted node so it rejoins via IST/SST.
SET SESSION wsrep_on=OFF;
--source include/shutdown_mysqld.inc
--source include/start_mysqld.inc
--source include/galera_wait_ready.inc
--connection node_1
DROP TABLE t1;
# Suppressions for the expected log noise on node_2.
--connection node_2
CALL mtr.add_suppression("Can't find record in 't1'");
CALL mtr.add_suppression("Update_rows_v1 apply failed");
CALL mtr.add_suppression("Inconsistency detected: Inconsistent by consensus on");
CALL mtr.add_suppression("WSREP: Failed to apply write set: ");
CALL mtr.add_suppression("Wsrep_high_priority_service::rollback: trans_rollback returned");
CALL mtr.add_suppression("last left .* greater than drain seqno");
# Restore original auto_increment_offset values.
--source ../galera/include/auto_increment_offset_restore.inc

View File

@@ -143,3 +143,10 @@ SET SYSTEM_VERSIONING_ALTER_HISTORY= KEEP;
ALTER TABLE t ADD COLUMN v VARCHAR(128) GENERATED ALWAYS AS (CRC32('MariaDB'));
ALTER TABLE t ADD INDEX (v);
DROP TABLE t;
#
# MDEV-39448 Assertion `n_fields > n_cols || type & 32' failed
# in dict_index_t::init_change_cols on ALTER
#
CREATE OR REPLACE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'latin1' COLLATE 'latin1_bin' KEY) ENGINE=InnoDB;
ALTER TABLE t1 MODIFY c1 VARCHAR(2049) CHARACTER SET 'latin1' COLLATE 'latin1_bin',ADD INDEX (c1);
DROP TABLE t1;

View File

@@ -151,3 +151,13 @@ SET SYSTEM_VERSIONING_ALTER_HISTORY= KEEP;
ALTER TABLE t ADD COLUMN v VARCHAR(128) GENERATED ALWAYS AS (CRC32('MariaDB'));
ALTER TABLE t ADD INDEX (v);
DROP TABLE t;
--echo #
--echo # MDEV-39448 Assertion `n_fields > n_cols || type & 32' failed
--echo # in dict_index_t::init_change_cols on ALTER
--echo #
CREATE OR REPLACE TABLE t1 (c1 VARCHAR(1) CHARACTER SET 'latin1' COLLATE 'latin1_bin' KEY) ENGINE=InnoDB;
ALTER TABLE t1 MODIFY c1 VARCHAR(2049) CHARACTER SET 'latin1' COLLATE 'latin1_bin',ADD INDEX (c1);
DROP TABLE t1;

View File

@@ -31,6 +31,26 @@ ERROR 42000: Variable 'wsrep_node_address' can't be set to the value of 'NULL'
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address
SET @@global.wsrep_node_address="127.0.0.1'ps";
ERROR 42000: Variable 'wsrep_node_address' can't be set to the value of '127.0.0.1'ps'
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address
SET @@global.wsrep_node_address="127.0.0.1 ps";
ERROR 42000: Variable 'wsrep_node_address' can't be set to the value of '127.0.0.1 ps'
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address
SET @@global.wsrep_node_address="127.0.0.1`ps";
ERROR 42000: Variable 'wsrep_node_address' can't be set to the value of '127.0.0.1`ps'
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address
SET @@global.wsrep_node_address="127.0.0.1$ps";
ERROR 42000: Variable 'wsrep_node_address' can't be set to the value of '127.0.0.1$ps'
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address
SET @@global.wsrep_node_address=ON;
SELECT @@global.wsrep_node_address;
@@global.wsrep_node_address

View File

@@ -44,5 +44,29 @@ SELECT @@global.wsrep_sst_auth;
NULL
SET @@global.wsrep_sst_auth=user:pass;
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 ':pass' at line 1
SET @@global.wsrep_sst_auth='user new:pass';
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user new:pass'
SET @@global.wsrep_sst_auth='user$new:pass';
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user$new:pass'
SET @@global.wsrep_sst_auth="user'new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user'new:pass'
SET @@global.wsrep_sst_auth="user;new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user;new:pass'
SET @@global.wsrep_sst_auth="user|new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user|new:pass'
SET @@global.wsrep_sst_auth="user&new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user&new:pass'
SET @@global.wsrep_sst_auth="user\\new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user\new:pass'
SET @@global.wsrep_sst_auth="user\"new:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user"new:pass'
SET @@global.wsrep_sst_auth="user\nnew:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user
new:pass'
SET @@global.wsrep_sst_auth="user\tnew:pass";
ERROR 42000: Variable 'wsrep_sst_auth' can't be set to the value of 'user new:pass'
# restore the initial value
SET @@global.wsrep_sst_auth = @wsrep_sst_auth_global_saved;
call mtr.add_suppression("WSREP: Illegal character in variable:");
# End of test

View File

@@ -29,8 +29,21 @@ SELECT @@global.wsrep_node_address;
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_node_address=NULL;
SELECT @@global.wsrep_node_address;
# The values being assigned to wsrep_node_address are not verified so the
# following alues are currently valid too.
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_node_address="127.0.0.1'ps";
SELECT @@global.wsrep_node_address;
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_node_address="127.0.0.1 ps";
SELECT @@global.wsrep_node_address;
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_node_address="127.0.0.1`ps";
SELECT @@global.wsrep_node_address;
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_node_address="127.0.0.1$ps";
SELECT @@global.wsrep_node_address;
# Values assigned to wsrep_node_address are only checked for unsafe characters,
# so non-address strings like ON/OFF/junk are currently accepted too.
SET @@global.wsrep_node_address=ON;
SELECT @@global.wsrep_node_address;
SET @@global.wsrep_node_address='OFF';

View File

@@ -34,6 +34,31 @@ SET @@global.wsrep_sst_auth=1;
SELECT @@global.wsrep_sst_auth;
--error ER_PARSE_ERROR
SET @@global.wsrep_sst_auth=user:pass;
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth='user new:pass';
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth='user$new:pass';
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user'new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user;new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user|new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user&new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user\\new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user\"new:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user\nnew:pass";
--error ER_WRONG_VALUE_FOR_VAR
SET @@global.wsrep_sst_auth="user\tnew:pass";
--echo
--echo # restore the initial value
SET @@global.wsrep_sst_auth = @wsrep_sst_auth_global_saved;
call mtr.add_suppression("WSREP: Illegal character in variable:");
--echo # End of test

View File

@@ -56,7 +56,7 @@ IF (WIN32)
my_wintoken.c
my_wincond.c
my_winerr.c
my_winfile.c
my_winfile.cc
my_minidump.cc
my_win_popen.cc)
ENDIF()

View File

@@ -49,6 +49,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#include "mysys_priv.h"
#include <share.h>
#include <sys/stat.h>
#include <winternl.h>
extern "C" {
/* Associates a file descriptor with an existing operating-system file handle.*/
File my_open_osfhandle(HANDLE handle, int oflag)
@@ -690,12 +693,35 @@ int my_win_stat( const char *path, struct _stati64 *buf)
DBUG_RETURN(-1);
}
/* Dynamically load NtFlushBuffersFileEx, used in my_win_fsync_internal */
typedef NTSTATUS(WINAPI *pNtFlushBuffersFileEx)(
HANDLE FileHandle, ULONG Flags, PVOID Parameters, ULONG ParametersSize,
PIO_STATUS_BLOCK IoStatusBlock);
static int my_win_fsync_internal(HANDLE h)
{
static const pNtFlushBuffersFileEx my_NtFlushBuffersFileEx=
(pNtFlushBuffersFileEx) GetProcAddress(GetModuleHandle("ntdll"),
"NtFlushBuffersFileEx");
if (my_NtFlushBuffersFileEx)
{
IO_STATUS_BLOCK iosb{};
NTSTATUS status= my_NtFlushBuffersFileEx(h,
FLUSH_FLAGS_FILE_DATA_SYNC_ONLY, NULL, 0, &iosb);
if (!status)
return 0;
}
if (FlushFileBuffers(h))
return 0;
return -1;
}
int my_win_fsync(File fd)
{
DBUG_ENTER("my_win_fsync");
if(FlushFileBuffers(my_get_osfhandle(fd)))
if (!my_win_fsync_internal(my_get_osfhandle(fd)))
DBUG_RETURN(0);
my_osmaperr(GetLastError());
DBUG_RETURN(-1);
@@ -716,4 +742,6 @@ int my_win_dup(File fd)
DBUG_RETURN(-1);
}
} /* extern "C" */
#endif /*_WIN32*/

View File

@@ -124,6 +124,8 @@ static int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
plain_text[i]^= scramble[i % SCRAMBLE_LENGTH];
pkt= plain_text;
pkt_len= (int)plain_text_len;
if (pkt_len <= 0 || pkt[pkt_len-1])
return CR_ERROR;
}
/* now pkt contains plaintext password */

View File

@@ -1123,7 +1123,8 @@ if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
wsrep_log_info "Using '$itmpdir' as mariadb-backup working directory"
if [ -n "$WSREP_SST_OPT_USER" ]; then
INNOEXTRA="$INNOEXTRA --user='$WSREP_SST_OPT_USER'"
WSREP_SST_OPT_USER_SAFE="$(safe WSREP_SST_OPT_USER)"
INNOEXTRA="$INNOEXTRA --user='$WSREP_SST_OPT_USER_SAFE'"
fi
if [ -n "$WSREP_SST_OPT_PSWD" ]; then

View File

@@ -54,6 +54,10 @@ bool Charset_collation_map_st::insert_or_replace(
bool error_on_conflicting_duplicate,
myf utf8_flag)
{
if (cs_name.length > MY_CS_CHARACTER_SET_NAME_SIZE ||
cl_name.length > MY_CS_COLLATION_NAME_SIZE)
return true;
char charset_name_c[MY_CS_CHARACTER_SET_NAME_SIZE + 1/*for '\0'*/];
strmake(charset_name_c, cs_name.str, cs_name.length);
CHARSET_INFO *cs= get_charset_by_csname(charset_name_c,

View File

@@ -1267,7 +1267,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
buf_manager.reset_buffer_sizes= reset_buffer_sizes;
buf_manager.redistribute_buffer_space= redistribute_buffer_space;
}
else
else if (full_buf)
{
/* index strategy doesn't need buffer, give all space to rowids*/
rowid_buffer.set_buffer_space(full_buf, full_buf_end);
@@ -1275,6 +1275,8 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
(int)is_mrr_assoc * sizeof(range_id_t)))
goto use_default_impl;
}
else
goto use_default_impl;
// setup_two_handlers() will call dsmrr_close() will clears the filter.
// Save its value and restore afterwards.

View File

@@ -1589,7 +1589,7 @@ void handle_connections_sockets();
static bool read_init_file(char *file_name);
pthread_handler_t handle_slave(void *arg);
static void clean_up(bool print_message);
static void clean_up(bool print_message, bool use_dummy_thd= false);
static int test_if_case_insensitive(const char *dir_name);
#ifndef EMBEDDED_LIBRARY
@@ -1998,7 +1998,7 @@ static void mysqld_exit(int exit_code)
#endif /* !EMBEDDED_LIBRARY */
static void clean_up(bool print_message)
static void clean_up(bool print_message, bool use_dummy_thd)
{
DBUG_PRINT("exit",("clean_up"));
if (cleanup_done++)
@@ -2035,7 +2035,7 @@ static void clean_up(bool print_message)
lex_free(); /* Free some memory */
item_create_cleanup();
cleanup_json_schema_keyword_hash();
tdc_start_shutdown();
tdc_start_shutdown(use_dummy_thd);
#ifdef HAVE_REPLICATION
semi_sync_master_deinit();
#endif
@@ -6461,7 +6461,7 @@ int mysqld_main(int argc, char **argv)
close_connections();
ha_pre_shutdown();
clean_up(1);
clean_up(1, true);
sd_notify(0, "STATUS=MariaDB server is down");
/* (void) pthread_attr_destroy(&connection_attrib); */

View File

@@ -991,6 +991,13 @@ int thd_set_peer_addr(THD *thd,
{
int rc;
/* Free any previously resolved hostname before overwriting it. */
if (thd->main_security_ctx.host != my_localhost)
{
my_free((void *) thd->main_security_ctx.host);
thd->main_security_ctx.host= NULL;
}
rc = ip_to_hostname(addr,
thd->main_security_ctx.ip,
&thd->main_security_ctx.host,

View File

@@ -174,7 +174,7 @@ public:
uchar *end_of_space() override { return pos; }
bool have_space_for(size_t bytes) override
{
return (pos + bytes < end);
return (size_t) (end - pos) > bytes;
}
void write() override

View File

@@ -632,7 +632,7 @@ bool tdc_init(void)
minimal in order to reduce number of references to pluggable engines.
*/
void tdc_start_shutdown(void)
void tdc_start_shutdown(bool use_dummy_thd)
{
DBUG_ENTER("tdc_start_shutdown");
if (tdc_inited)
@@ -645,8 +645,50 @@ void tdc_start_shutdown(void)
*/
tdc_size= 0;
tc_size= 0;
THD *thd= nullptr;
bool reset_back_current_thd= false;
if (use_dummy_thd && _current_thd() == nullptr)
{
/*
There is no active THD, use the surrogate one to handle memory
allocations in Sql_alloc::operator new() that uses current_thd()
for getting THD to get access to memory allocation API
*/
thd= new THD(0);
DBUG_ASSERT(thd != nullptr);
thd->store_globals();
thd->init();
thd->set_query_inner((char*) STRING_WITH_LEN("intern:tdc_start_shutdown"),
default_charset_info);
reset_back_current_thd= true;
}
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
/*
purge_tables() iterates along opened tables close every one of them.
In case tables use triggers it results in destroying sp_head objects
for every associated trigger. Destroying an object of the sp_head class
leads to deleting sp_instr objects. In case some of SP instruction
were previously re-parsed by the reason of changes in tables metadata,
the method
sp_head::register_instr_mem_root_for_deallocation()
is called to add a memory root used for re-parsing to the list of
mem_roots to be freed on sp_head destruction. Mem_roots for later
destruction are placed on the List that use current_thd() for memory
allocation. That is the reason why the dummy THD is created before
in case there is no active THD at the moment the function
tdc_start_shutdown is invoked.
*/
purge_tables();
if (reset_back_current_thd)
{
thd->reset_globals();
delete thd;
}
}
DBUG_VOID_RETURN;
}

View File

@@ -66,7 +66,7 @@ extern ulong tc_size;
extern uint32 tc_instances;
extern bool tdc_init(void);
extern void tdc_start_shutdown(void);
extern void tdc_start_shutdown(bool use_dummy_thd);
extern void tdc_deinit(void);
extern ulong tdc_records(void);
extern void tdc_purge(bool all);

View File

@@ -371,6 +371,12 @@ int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
assert(ws_handle == wsrep::ws_handle());
}
int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
DBUG_EXECUTE_IF("simulate_rollback_failure_in_applier", ret= 1;);
if (ret)
WSREP_WARN("Wsrep_high_priority_service::rollback: trans_rollback "
"returned %d for thd %lu (killed=%d, seqno=%lld)",
ret, thd_get_thread_id(m_thd), m_thd->killed,
(long long) wsrep_thd_trx_seqno(m_thd));
WSREP_DEBUG("::rollback() thread: %lu, client_state %s "
"client_mode %s trans_state %s killed %d",

View File

@@ -186,48 +186,6 @@ static void* wsrep_sst_joiner_monitor_thread(void *arg __attribute__((unused)))
return NULL;
}
/* return true if character can be a part of a filename */
static bool filename_char(int const c)
{
return isalnum(c) || (c == '-') || (c == '_') || (c == '.');
}
/* return true if string is comma seprated list */
static bool comma_char(int const c)
{
return (c == ',');
}
/* return true if character can be a part of an address string */
static bool address_char(int const c)
{
return filename_char(c) ||
(c == ':') || (c == '[') || (c == ']') || (c == '/');
}
/* return true if character can be a part of an address string list */
static bool names_list(int const c)
{
return address_char(c) || comma_char(c);
}
static bool check_request_str(const char* const str,
bool (*check) (int c),
bool log_warn = true)
{
for (size_t i(0); str[i] != '\0'; ++i)
{
if (!check(str[i]))
{
if (log_warn) WSREP_WARN("Illegal character in state transfer request: %i (%c).",
str[i], str[i]);
return true;
}
}
return false;
}
bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
{
if ((! var->save_result.string_value.str) ||
@@ -240,8 +198,8 @@ bool wsrep_sst_method_check (sys_var *self, THD* thd, set_var* var)
}
/* check also that method name is alphanumeric string */
if (check_request_str(var->save_result.string_value.str,
filename_char, false))
if (wsrep_check_request_str(var->save_result.string_value.str,
wsrep_filename_char, false))
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
var->save_result.string_value.str ?
@@ -293,8 +251,8 @@ bool wsrep_sst_receive_address_check (sys_var *self, THD* thd, set_var* var)
}
/* check also that address contains only accepted characters */
if (check_request_str(var->save_result.string_value.str,
address_char, false))
if (wsrep_check_request_str(var->save_result.string_value.str,
wsrep_address_char, false))
{
goto err;
}
@@ -316,7 +274,37 @@ bool wsrep_sst_receive_address_update (sys_var *self, THD* thd,
bool wsrep_sst_auth_check (sys_var *self, THD* thd, set_var* var)
{
/* Allow empty value */
if (!var->save_result.string_value.str || var->save_result.string_value.length == 0)
return 0;
/* Check length */
if ((var->save_result.string_value.length > (FN_REFLEN - 1))) // safety
{
goto err;
}
{
/* Split sst_auth on ':'-character */
std::string auth= var->save_result.string_value.str;
std::string r_user= auth.substr(0, auth.find(":"));
/* check also that user contains only accepted characters,
password part is not validated. */
if (wsrep_check_request_str(r_user.c_str(),
wsrep_filename_char, true))
{
goto err;
}
}
return 0;
err:
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name.str,
var->save_result.string_value.str ?
var->save_result.string_value.str : "NULL");
return 1;
}
void wsrep_sst_auth_free()
@@ -370,8 +358,8 @@ bool wsrep_sst_donor_check (sys_var *self, THD* thd, set_var* var)
return 0;
/* check also that donor string contains only accepted characters */
if (check_request_str(var->save_result.string_value.str,
names_list, false))
if (wsrep_check_request_str(var->save_result.string_value.str,
wsrep_names_list, false))
{
goto err;
}
@@ -2355,7 +2343,7 @@ int wsrep_sst_donate(const std::string& msg,
const char* method= msg.data();
size_t method_len= strlen (method);
if (check_request_str(method, filename_char, true))
if (wsrep_check_request_str(method, wsrep_filename_char, true))
{
WSREP_ERROR("Bad SST method name. SST canceled.");
return WSREP_CB_FAILURE;
@@ -2377,7 +2365,14 @@ int wsrep_sst_donate(const std::string& msg,
addr= data;
}
if (check_request_str(addr, address_char, true))
if (remote_auth() &&
wsrep_check_request_str(remote_auth(), wsrep_shell_char, true))
{
WSREP_ERROR("Bad remote auth string. SST canceled.");
return WSREP_CB_FAILURE;
}
if (wsrep_check_request_str(addr, wsrep_address_char, true))
{
WSREP_ERROR("Bad SST address string. SST canceled.");
return WSREP_CB_FAILURE;

View File

@@ -1,4 +1,5 @@
/* Copyright 2010-2015 Codership Oy <http://www.codership.com>
Copyright 2025-2026 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -729,3 +730,52 @@ size_t wsrep_host_len(const char* const addr, size_t const addr_len)
return (colon ? colon - addr : addr_len);
}
}
/* return true if character can be a part of a filename */
bool wsrep_filename_char(const unsigned char c)
{
return isalnum(c) || (c == '-') || (c == '_') || (c == '.');
}
/* return true if string is comma separated list */
bool wsrep_comma_char(const unsigned char c)
{
return (c == ',');
}
/* return true if character can be a part of an address string */
bool wsrep_address_char(const unsigned char c)
{
return wsrep_filename_char(c) ||
(c == ':') || (c == '[') || (c == ']') || (c == '/');
}
bool wsrep_shell_char(const unsigned char c)
{
return (c != '`') && (c != '\'') && (c != '$') &&
(c != ' ') && (c != '\t') && (c != '\n') &&
(c != '\r') && (c != '\v') && (c != '\f');
}
/* return true if character can be a part of an address string list */
bool wsrep_names_list(const unsigned char c)
{
return wsrep_address_char(c) || wsrep_comma_char(c);
}
bool wsrep_check_request_str(const char* const str,
bool (*check) (const unsigned char),
bool log_warn)
{
for (size_t i(0); str[i] != '\0'; ++i)
{
if (!check(str[i]))
{
if (log_warn) WSREP_WARN("Illegal character in variable: %i (%c).",
str[i], str[i]);
return true;
}
}
return false;
}

View File

@@ -1,4 +1,5 @@
/* Copyright (C) 2013-2015 Codership Oy <info@codership.com>
Copyright (C) 2025-2026 MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -493,4 +494,14 @@ private:
} // namespace wsrep
// Functions to validate dynamic variable safety
bool wsrep_filename_char(const unsigned char c);
bool wsrep_comma_char(const unsigned char c);
bool wsrep_address_char(const unsigned char c);
bool wsrep_shell_char(const unsigned char c);
bool wsrep_names_list(const unsigned char c);
bool wsrep_check_request_str(const char* const str,
bool (*check) (const unsigned char),
bool log_warn = true);
#endif /* WSREP_UTILS_H */

View File

@@ -624,10 +624,10 @@ static int wsrep_cluster_address_verify (const char* cluster_address_str)
bool wsrep_cluster_address_check (sys_var *self, THD* thd, set_var* var)
{
char addr_buf[FN_REFLEN];
char addr_buf[FN_REFLEN+1];
if ((! var->save_result.string_value.str) ||
(var->save_result.string_value.length >= sizeof(addr_buf))) // safety
(var->save_result.string_value.length >= FN_REFLEN)) // safety
goto err;
strmake(addr_buf, var->save_result.string_value.str,
@@ -730,20 +730,19 @@ bool wsrep_node_name_update (sys_var *self, THD* thd, enum_var_type type)
return 0;
}
// TODO: do something more elaborate, like checking connectivity
bool wsrep_node_address_check (sys_var *self, THD* thd, set_var* var)
{
char addr_buf[FN_REFLEN];
if ((! var->save_result.string_value.str) ||
(var->save_result.string_value.length > (FN_REFLEN - 1))) // safety
(var->save_result.string_value.length >= FN_REFLEN)) // safety
goto err;
memcpy(addr_buf, var->save_result.string_value.str,
var->save_result.string_value.length);
addr_buf[var->save_result.string_value.length]= 0;
if (var->save_result.string_value.length)
{
if (wsrep_check_request_str(var->save_result.string_value.str,
wsrep_address_char, false))
goto err;
}
// TODO: for now 'allow' 0-length string to be valid (default)
return 0;
err:

View File

@@ -57,7 +57,7 @@ CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
MY_CHECK_AND_SET_COMPILER_FLAG("-fno-lto")
SET(PCRE_INCLUDES "${PCRE_INCLUDE_DIRS}")
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
add_subdirectory(columnstore)
add_submodule_subdirectory(columnstore)
IF(TARGET columnstore)
# Redo logic in cmake/plugin to prevent the attempted creation of *Symlinks package

View File

@@ -1,7 +1,3 @@
IF("NO" STREQUAL "${PLUGIN_DUCKDB}")
return()
ENDIF()
IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
MESSAGE_ONCE(duckdb "DuckDB: not Linux, skipping")
return()
@@ -13,6 +9,14 @@ IF(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR
return()
ENDIF()
# Catch -m32 / multilib builds (e.g. 32-bit build inside an x86_64 CI
# container): CMAKE_SYSTEM_PROCESSOR still reports x86_64, but the target is
# 32-bit. DuckDB (and its jemalloc extension) is 64-bit only.
IF(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
MESSAGE_ONCE(duckdb "DuckDB: not a 64-bit build, skipping")
return()
ENDIF()
# Check C++17 compiler support
MY_CHECK_CXX_COMPILER_FLAG("-std=c++17")
IF(NOT have_CXX__std_c__17)
@@ -25,23 +29,6 @@ ENDIF()
SET(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} -U_GLIBCXX_DEBUG -U_GLIBCXX_ASSERTIONS")
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb.cmake)
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb_target_setup.cmake)
# Provide MariaDB server include paths to sub-libraries.
# MYSQL_ADD_PLUGIN normally does this, but it runs after ADD_SUBDIRECTORY.
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${CMAKE_BINARY_DIR}/include
${PCRE_INCLUDE_DIRS}
${SSL_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS})
# Build sub-libraries.
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(convertor)
ADD_SUBDIRECTORY(runtime)
# Plugin sources (handler layer + UDFs whose extern "C" symbols must be
# in the final .so for dlsym() loading).
SET(DUCKDB_PLUGIN_SOURCES
@@ -50,13 +37,31 @@ SET(DUCKDB_PLUGIN_SOURCES
duckdb_udf.cc
)
MYSQL_ADD_PLUGIN(duckdb ${DUCKDB_PLUGIN_SOURCES}
STORAGE_ENGINE
MODULE_ONLY
LINK_LIBRARIES duckdb_runtime duckdb_convertor duckdb_common ${DUCKDB_LIBRARY}
)
MYSQL_ADD_PLUGIN(duckdb ${DUCKDB_PLUGIN_SOURCES} STORAGE_ENGINE MODULE_ONLY)
IF(TARGET duckdb)
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb.cmake)
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb_target_setup.cmake)
# Provide MariaDB server include paths to sub-libraries.
# MYSQL_ADD_PLUGIN normally does this, but it runs after ADD_SUBDIRECTORY.
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${CMAKE_BINARY_DIR}/include
${PCRE_INCLUDE_DIRS}
${SSL_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS})
# Build sub-libraries.
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(convertor)
ADD_SUBDIRECTORY(runtime)
target_link_libraries(duckdb duckdb_runtime duckdb_convertor duckdb_common
${DUCKDB_LIBRARY}
)
duckdb_setup_target(duckdb)
TARGET_INCLUDE_DIRECTORIES(duckdb PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/common

View File

@@ -25,13 +25,6 @@
SET(DUCKDB_SUBMODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_parties/duckdb")
SET(DUCKDB_INCLUDE_DIR "${DUCKDB_SUBMODULE_DIR}/src/include")
IF(NOT EXISTS "${DUCKDB_SUBMODULE_DIR}/CMakeLists.txt")
MESSAGE(FATAL_ERROR
"DuckDB submodule not found at ${DUCKDB_SUBMODULE_DIR}\n"
"Run: git submodule update --init ${DUCKDB_SUBMODULE_DIR}"
)
ENDIF()
INCLUDE(ExternalProject)
# Map MariaDB build type to a DuckDB-friendly one.
@@ -56,6 +49,7 @@ SET(_DUCKDB_STATIC_LIBS
)
MESSAGE(STATUS "=== Building DuckDB from submodule (${DUCKDB_SUBMODULE_DIR}) ===")
ADD_SUBMODULE(third_parties/duckdb)
ExternalProject_Add(duckdb_build
PREFIX "${_DUCKDB_BUILD_DIR}"

View File

@@ -16,6 +16,12 @@ MACRO(duckdb_setup_target _target)
TARGET_COMPILE_OPTIONS(${_target} PRIVATE
-U_GLIBCXX_DEBUG -U_GLIBCXX_ASSERTIONS
)
# GCC 16+ warns about DuckDB's CompressionInfo in SFINAE contexts.
# Silence it — upstream DuckDB issue, not ours to fix.
MY_CHECK_CXX_COMPILER_FLAG("-Wsfinae-incomplete")
IF(have_CXX__Wsfinae_incomplete)
TARGET_COMPILE_OPTIONS(${_target} PRIVATE -Wno-sfinae-incomplete)
ENDIF()
IF(DUCKDB_WERROR)
TARGET_COMPILE_OPTIONS(${_target} PRIVATE -Werror)
ENDIF()

View File

@@ -2798,7 +2798,7 @@ static int innobase_close_connection(THD *thd) noexcept
case TRX_STATE_PREPARED:
if (trx->has_logged_persistent())
{
trx_disconnect_prepared(trx);
trx->disconnect_prepared();
return 0;
}
/* fall through */

View File

@@ -180,11 +180,7 @@ static bool trx_i_s_common_fill_table(THD *thd, TABLE_LIST *tables)
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name.str);
/* update the cache */
trx_i_s_cache_start_write(trx_i_s_cache);
trx_i_s_possibly_fetch_data_into_cache(trx_i_s_cache);
trx_i_s_cache_end_write(trx_i_s_cache);
if (trx_i_s_cache_is_truncated(trx_i_s_cache))
if (trx_i_s_possibly_fetch_data_into_cache())
sql_print_warning("InnoDB: Data in %.*s truncated due to memory limit"
" of %u bytes",
int(tables->schema_table_name.length),
@@ -373,22 +369,20 @@ static int fill_innodb_trx_from_cache(THD *thd, TABLE_LIST *tables, Item*)
struct cache
{
cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
cache() { trx_i_s_cache_start_read(); }
~cache() { trx_i_s_cache_end_read(); }
} c;
Field** fields = tables->table->field;
rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
I_S_INNODB_TRX);
rows_num = trx_i_s_cache_get_rows_used(I_S_INNODB_TRX);
for (i = 0; i < rows_num; i++) {
i_s_trx_row_t* row;
row = (i_s_trx_row_t*)
trx_i_s_cache_get_nth_row(
trx_i_s_cache, I_S_INNODB_TRX, i);
trx_i_s_cache_get_nth_row(I_S_INNODB_TRX, i);
/* trx_id */
OK(fields[IDX_TRX_ID]->store(row->trx_id, true));
@@ -654,14 +648,13 @@ fill_innodb_locks_from_cache(
struct cache
{
cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
cache() { trx_i_s_cache_start_read(); }
~cache() { trx_i_s_cache_end_read(); }
} c;
Field** fields = tables->table->field;
rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
I_S_INNODB_LOCKS);
rows_num = trx_i_s_cache_get_rows_used(I_S_INNODB_LOCKS);
for (i = 0; i < rows_num; i++) {
@@ -670,8 +663,7 @@ fill_innodb_locks_from_cache(
const char* bufend;
row = (i_s_locks_row_t*)
trx_i_s_cache_get_nth_row(
trx_i_s_cache, I_S_INNODB_LOCKS, i);
trx_i_s_cache_get_nth_row(I_S_INNODB_LOCKS, i);
/* lock_id */
trx_i_s_create_lock_id(row, lock_id, sizeof(lock_id));
@@ -829,22 +821,20 @@ fill_innodb_lock_waits_from_cache(
struct cache
{
cache() { trx_i_s_cache_start_read(trx_i_s_cache); }
~cache() { trx_i_s_cache_end_read(trx_i_s_cache); }
cache() { trx_i_s_cache_start_read(); }
~cache() { trx_i_s_cache_end_read(); }
} c;
Field** fields = tables->table->field;
rows_num = trx_i_s_cache_get_rows_used(trx_i_s_cache,
I_S_INNODB_LOCK_WAITS);
rows_num = trx_i_s_cache_get_rows_used(I_S_INNODB_LOCK_WAITS);
for (i = 0; i < rows_num; i++) {
i_s_lock_waits_row_t* row;
row = (i_s_lock_waits_row_t*)
trx_i_s_cache_get_nth_row(
trx_i_s_cache, I_S_INNODB_LOCK_WAITS, i);
trx_i_s_cache_get_nth_row(I_S_INNODB_LOCK_WAITS, i);
/* requesting_trx_id */
OK(fields[IDX_REQUESTING_TRX_ID]->store(

View File

@@ -1345,7 +1345,9 @@ public:
@param n_cols number of columns whose collation is changing */
void init_change_cols(unsigned n_cols)
{
ut_ad(n_fields > n_cols || type & DICT_FTS);
/* Allow n_fields == n_cols when single column indexes
undergoes type change */
ut_ad(n_fields >= n_cols || type & DICT_FTS);
change_col_info= static_cast<col_info*>
(mem_heap_zalloc(heap, sizeof(col_info)));
change_col_info->n_cols= n_cols;

View File

@@ -146,9 +146,6 @@ struct i_s_lock_waits_row_t {
const i_s_locks_row_t* blocking_lock_row; /*!< blocking lock */
};
/** Cache of INFORMATION_SCHEMA table data */
struct trx_i_s_cache_t;
/** Auxiliary enum used by functions that need to select one of the
INFORMATION_SCHEMA tables */
enum i_s_table {
@@ -157,90 +154,42 @@ enum i_s_table {
I_S_INNODB_LOCK_WAITS /*!< INFORMATION_SCHEMA.innodb_lock_waits */
};
/** This is the intermediate buffer where data needed to fill the
INFORMATION SCHEMA tables is fetched and later retrieved by the C++
code in handler/i_s.cc. */
extern trx_i_s_cache_t* trx_i_s_cache;
/*******************************************************************//**
Initialize INFORMATION SCHEMA trx related cache. */
void
trx_i_s_cache_init(
/*===============*/
trx_i_s_cache_t* cache); /*!< out: cache to init */
void trx_i_s_cache_init();
/*******************************************************************//**
Free the INFORMATION SCHEMA trx related cache. */
void
trx_i_s_cache_free(
/*===============*/
trx_i_s_cache_t* cache); /*!< in/out: cache to free */
void trx_i_s_cache_free();
/*******************************************************************//**
Issue a shared/read lock on the tables cache. */
void
trx_i_s_cache_start_read(
/*=====================*/
trx_i_s_cache_t* cache); /*!< in: cache */
void trx_i_s_cache_start_read() noexcept;
/*******************************************************************//**
Release a shared/read lock on the tables cache. */
void
trx_i_s_cache_end_read(
/*===================*/
trx_i_s_cache_t* cache); /*!< in: cache */
/*******************************************************************//**
Issue an exclusive/write lock on the tables cache. */
void
trx_i_s_cache_start_write(
/*======================*/
trx_i_s_cache_t* cache); /*!< in: cache */
/*******************************************************************//**
Release an exclusive/write lock on the tables cache. */
void
trx_i_s_cache_end_write(
/*====================*/
trx_i_s_cache_t* cache); /*!< in: cache */
void trx_i_s_cache_end_read() noexcept;
/*******************************************************************//**
Retrieves the number of used rows in the cache for a given
INFORMATION SCHEMA table.
@param table table to be read
@return number of rows */
ulint
trx_i_s_cache_get_rows_used(
/*========================*/
trx_i_s_cache_t* cache, /*!< in: cache */
enum i_s_table table); /*!< in: which table */
ulint trx_i_s_cache_get_rows_used(i_s_table table);
/*******************************************************************//**
Retrieves the nth row in the cache for a given INFORMATION SCHEMA
table.
@param table table to read
@param n row number
@return row */
void*
trx_i_s_cache_get_nth_row(
/*======================*/
trx_i_s_cache_t* cache, /*!< in: cache */
enum i_s_table table, /*!< in: which table */
ulint n); /*!< in: row number */
void *trx_i_s_cache_get_nth_row(i_s_table table, ulint n);
/*******************************************************************//**
Update the transactions cache if it has not been read for some time.
@return 0 - fetched, 1 - not */
int
trx_i_s_possibly_fetch_data_into_cache(
/*===================================*/
trx_i_s_cache_t* cache); /*!< in/out: cache */
@retval false when fetched or cached
@retval true if fetched but the cache was truncated */
bool trx_i_s_possibly_fetch_data_into_cache();
/*******************************************************************//**
Returns true, if the data in the cache is truncated due to the memory
limit posed by TRX_I_S_MEM_LIMIT.
@return TRUE if truncated */
bool
trx_i_s_cache_is_truncated(
/*=======================*/
trx_i_s_cache_t* cache); /*!< in: cache */
/** The maximum length of a resulting lock_id_size in
trx_i_s_create_lock_id(), not including the terminating NUL.
":%lu:%lu:%lu" -> 63 chars */

View File

@@ -831,9 +831,6 @@ public:
mysql_mutex_unlock(&mutex);
}
void freeze() const { mysql_mutex_lock(&mutex); }
void unfreeze() const { mysql_mutex_unlock(&mutex); }
private:
alignas(CPU_LEVEL1_DCACHE_LINESIZE) mutable mysql_mutex_t mutex;
alignas(CPU_LEVEL1_DCACHE_LINESIZE) ilist<trx_t> trx_list;

View File

@@ -75,10 +75,6 @@ trx_t *trx_create();
/** At shutdown, frees a transaction object. */
void trx_free_at_shutdown(trx_t *trx);
/** Disconnect a prepared transaction from MySQL.
@param[in,out] trx transaction */
void trx_disconnect_prepared(trx_t *trx);
/** Initialize (resurrect) transactions at startup. */
dberr_t trx_lists_init_at_db_start();
@@ -787,7 +783,7 @@ public:
This field is accessed by the thread that owns the transaction,
without holding any mutex.
There is only one foreign-thread access in trx_print_low()
and a possible race condition with trx_disconnect_prepared(). */
and a possible race condition with disconnect_prepared(). */
bool is_recovered;
const char* op_info; /*!< English text describing the
current operation, or an empty
@@ -1035,6 +1031,8 @@ public:
/** Commit the transaction.
@retval false (always) */
bool commit() noexcept;
/** Disconnect a prepared transaction */
void disconnect_prepared() noexcept;
/** Try to drop a persistent table.
@param table persistent table

View File

@@ -572,7 +572,7 @@ static void srv_init()
&page_zip_stat_per_index_mutex, nullptr);
/* Initialize some INFORMATION SCHEMA internal structures */
trx_i_s_cache_init(trx_i_s_cache);
trx_i_s_cache_init();
}
/*********************************************************************//**
@@ -589,7 +589,7 @@ srv_free(void)
mysql_mutex_destroy(&page_zip_stat_per_index_mutex);
mysql_mutex_destroy(&srv_sys.tasks_mutex);
trx_i_s_cache_free(trx_i_s_cache);
trx_i_s_cache_free();
srv_thread_pool_end();
}

View File

@@ -134,11 +134,7 @@ struct trx_i_s_cache_t {
/** This is the intermediate buffer where data needed to fill the
INFORMATION SCHEMA tables is fetched and later retrieved by the C++
code in handler/i_s.cc. */
static trx_i_s_cache_t trx_i_s_cache_static;
/** This is the intermediate buffer where data needed to fill the
INFORMATION SCHEMA tables is fetched and later retrieved by the C++
code in handler/i_s.cc. */
trx_i_s_cache_t* trx_i_s_cache = &trx_i_s_cache_static;
static trx_i_s_cache_t cache;
/** @return the heap number of a record lock
@retval 0xFFFF for table locks */
@@ -1046,8 +1042,11 @@ static void fetch_data_into_cache_low(trx_i_s_cache_t *cache, const trx_t *trx)
static void fetch_data_into_cache(trx_i_s_cache_t *cache)
{
LockMutexGuard g{SRW_LOCK_CALL};
/* these are protected by cache->rw_lock.wr_lock() */
trx_i_s_cache_clear(cache);
/* this flag may be set by fetch_data_into_cache_low() below */
cache->is_truncated= false;
LockMutexGuard g{SRW_LOCK_CALL};
/* Capture the state of transactions */
trx_sys.trx_list.for_each([cache](trx_t &trx) {
@@ -1060,51 +1059,30 @@ static void fetch_data_into_cache(trx_i_s_cache_t *cache)
trx.mutex_unlock();
}
});
cache->is_truncated= false;
}
/*******************************************************************//**
Update the transactions cache if it has not been read for some time.
Called from handler/i_s.cc.
@return 0 - fetched, 1 - not */
int
trx_i_s_possibly_fetch_data_into_cache(
/*===================================*/
trx_i_s_cache_t* cache) /*!< in/out: cache */
@retval false when fetched or cached
@retval true if fetched but the cache was truncated */
bool trx_i_s_possibly_fetch_data_into_cache()
{
if (!can_cache_be_updated(cache)) {
return(1);
}
/* We need to read trx_sys and record/table lock queues */
fetch_data_into_cache(cache);
/* update cache last read time */
cache->last_read = my_interval_timer();
return(0);
bool is_truncated= false;
cache.rw_lock.wr_lock(SRW_LOCK_CALL);
if (can_cache_be_updated(&cache))
{
/* We need to read trx_sys and record/table lock queues */
fetch_data_into_cache(&cache);
/* update cache last read time */
cache.last_read= my_interval_timer();
is_truncated= cache.is_truncated;
}
cache.rw_lock.wr_unlock();
return is_truncated;
}
/*******************************************************************//**
Returns TRUE if the data in the cache is truncated due to the memory
limit posed by TRX_I_S_MEM_LIMIT.
@return TRUE if truncated */
bool
trx_i_s_cache_is_truncated(
/*=======================*/
trx_i_s_cache_t* cache) /*!< in: cache */
{
return(cache->is_truncated);
}
/*******************************************************************//**
Initialize INFORMATION SCHEMA trx related cache. */
void
trx_i_s_cache_init(
/*===============*/
trx_i_s_cache_t* cache) /*!< out: cache to init */
void trx_i_s_cache_init()
{
/* The latching is done in the following order:
acquire trx_i_s_cache_t::rw_lock, rwlock
@@ -1114,80 +1092,44 @@ trx_i_s_cache_init(
acquire trx_i_s_cache_t::rw_lock, rdlock
release trx_i_s_cache_t::rw_lock */
cache->rw_lock.SRW_LOCK_INIT(trx_i_s_cache_lock_key);
cache.rw_lock.SRW_LOCK_INIT(trx_i_s_cache_lock_key);
cache->last_read = 0;
cache.last_read = 0;
table_cache_init(&cache->innodb_trx, sizeof(i_s_trx_row_t));
table_cache_init(&cache->innodb_locks, sizeof(i_s_locks_row_t));
table_cache_init(&cache->innodb_lock_waits,
table_cache_init(&cache.innodb_trx, sizeof(i_s_trx_row_t));
table_cache_init(&cache.innodb_locks, sizeof(i_s_locks_row_t));
table_cache_init(&cache.innodb_lock_waits,
sizeof(i_s_lock_waits_row_t));
cache->locks_hash.create(LOCKS_HASH_CELLS_NUM);
cache.locks_hash.create(LOCKS_HASH_CELLS_NUM);
cache->storage = ha_storage_create(CACHE_STORAGE_INITIAL_SIZE,
cache.storage = ha_storage_create(CACHE_STORAGE_INITIAL_SIZE,
CACHE_STORAGE_HASH_CELLS);
cache->mem_allocd = 0;
cache.mem_allocd = 0;
cache->is_truncated = false;
cache.is_truncated = false;
}
/*******************************************************************//**
Free the INFORMATION SCHEMA trx related cache. */
void
trx_i_s_cache_free(
/*===============*/
trx_i_s_cache_t* cache) /*!< in, own: cache to free */
void trx_i_s_cache_free()
{
cache->rw_lock.destroy();
cache->locks_hash.free();
ha_storage_free(cache->storage);
table_cache_free(&cache->innodb_trx);
table_cache_free(&cache->innodb_locks);
table_cache_free(&cache->innodb_lock_waits);
cache.rw_lock.destroy();
cache.locks_hash.free();
ha_storage_free(cache.storage);
table_cache_free(&cache.innodb_trx);
table_cache_free(&cache.innodb_locks);
table_cache_free(&cache.innodb_lock_waits);
}
/*******************************************************************//**
Issue a shared/read lock on the tables cache. */
void
trx_i_s_cache_start_read(
/*=====================*/
trx_i_s_cache_t* cache) /*!< in: cache */
void trx_i_s_cache_start_read() noexcept
{
cache->rw_lock.rd_lock(SRW_LOCK_CALL);
cache.rw_lock.rd_lock(SRW_LOCK_CALL);
}
/*******************************************************************//**
Release a shared/read lock on the tables cache. */
void
trx_i_s_cache_end_read(
/*===================*/
trx_i_s_cache_t* cache) /*!< in: cache */
void trx_i_s_cache_end_read() noexcept
{
cache->last_read = my_interval_timer();
cache->rw_lock.rd_unlock();
}
/*******************************************************************//**
Issue an exclusive/write lock on the tables cache. */
void
trx_i_s_cache_start_write(
/*======================*/
trx_i_s_cache_t* cache) /*!< in: cache */
{
cache->rw_lock.wr_lock(SRW_LOCK_CALL);
}
/*******************************************************************//**
Release an exclusive/write lock on the tables cache. */
void
trx_i_s_cache_end_write(
/*====================*/
trx_i_s_cache_t* cache) /*!< in: cache */
{
cache->rw_lock.wr_unlock();
cache.last_read= my_interval_timer();
cache.rw_lock.rd_unlock();
}
/*******************************************************************//**
@@ -1197,55 +1139,33 @@ static
i_s_table_cache_t*
cache_select_table(
/*===============*/
trx_i_s_cache_t* cache, /*!< in: whole cache */
enum i_s_table table) /*!< in: which table */
{
switch (table) {
case I_S_INNODB_TRX:
return &cache->innodb_trx;
return &cache.innodb_trx;
case I_S_INNODB_LOCKS:
return &cache->innodb_locks;
return &cache.innodb_locks;
case I_S_INNODB_LOCK_WAITS:
return &cache->innodb_lock_waits;
return &cache.innodb_lock_waits;
}
ut_error;
return NULL;
}
/*******************************************************************//**
Retrieves the number of used rows in the cache for a given
INFORMATION SCHEMA table.
@return number of rows */
ulint
trx_i_s_cache_get_rows_used(
/*========================*/
trx_i_s_cache_t* cache, /*!< in: cache */
enum i_s_table table) /*!< in: which table */
ulint trx_i_s_cache_get_rows_used(i_s_table table)
{
i_s_table_cache_t* table_cache;
table_cache = cache_select_table(cache, table);
return(table_cache->rows_used);
return cache_select_table(table)->rows_used;
}
/*******************************************************************//**
Retrieves the nth row (zero-based) in the cache for a given
INFORMATION SCHEMA table.
@return row */
void*
trx_i_s_cache_get_nth_row(
/*======================*/
trx_i_s_cache_t* cache, /*!< in: cache */
enum i_s_table table, /*!< in: which table */
ulint n) /*!< in: row number */
void *trx_i_s_cache_get_nth_row(i_s_table table, ulint n)
{
i_s_table_cache_t* table_cache;
ulint i;
void* row;
table_cache = cache_select_table(cache, table);
table_cache = cache_select_table(table);
ut_a(n < table_cache->rows_used);

View File

@@ -550,23 +550,19 @@ TRANSACTIONAL_TARGET void trx_free_at_shutdown(trx_t *trx)
}
/**
Disconnect a prepared transaction from MySQL
@param[in,out] trx transaction
*/
void trx_disconnect_prepared(trx_t *trx)
void trx_t::disconnect_prepared() noexcept
{
ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED));
ut_ad(trx->mysql_thd);
ut_ad(!trx->mysql_log_file_name);
trx->read_view.close();
trx_sys.trx_list.freeze();
trx->is_recovered= true;
trx->mysql_thd= NULL;
trx_sys.trx_list.unfreeze();
ut_ad(trx_state_eq(this, TRX_STATE_PREPARED));
ut_ad(mysql_thd);
ut_ad(!mysql_log_file_name);
read_view.close();
mutex_lock();
is_recovered= true;
mysql_thd= nullptr;
mutex_unlock();
/* todo/fixme: suggest to do it at innodb prepare */
trx->will_lock= false;
trx_sys.rw_trx_hash.put_pins(trx);
will_lock= false;
trx_sys.rw_trx_hash.put_pins(this);
}
MY_ATTRIBUTE((nonnull, warn_unused_result))

View File

@@ -131,6 +131,7 @@ SET(CPACK_RPM_s3-engine_PACKAGE_SUMMARY "Amazon S3 archival storage engine for M
SET(CPACK_RPM_s3-engine_PACKAGE_DESCRIPTION "The S3 storage engine allows one to archive MariaDB tables in Amazon S3 (or any third-party public or private cloud that implements S3 API), but still have them accessible in MariaDB in read-only mode." PARENT_SCOPE)
IF(TARGET s3)
ADD_SUBMODULE(libmarias3 Makefile.am)
MYSQL_ADD_EXECUTABLE(aria_s3_copy aria_s3_copy.cc ${S3_SOURCES} COMPONENT s3-engine)
TARGET_LINK_LIBRARIES(aria_s3_copy aria myisam mysys mysys_ssl ${CURL_LIBRARIES} ${ZLIB_LIBRARIES})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/libmarias3)

View File

@@ -163,6 +163,8 @@ IF(NOT TARGET rocksdb)
RETURN()
ENDIF()
ADD_SUBMODULE(rocksdb)
INSTALL_MANPAGES(rocksdb-engine mariadb-ldb.1 myrocks_hotbackup.1)
CHECK_CXX_SOURCE_COMPILES("

View File

@@ -20804,6 +20804,84 @@ static void test_proxy_header_limits()
}
/*
MDEV-37556 memory leak in proxy protocol for non-loopback
connections.
Uses debug_dbug variable to set vio_peer_addr_fake_ipv6 etc
to emulate a remote connection
*/
static void test_proxy_header_dbug_remote_connection()
{
#ifndef DBUG_OFF
int rc;
MYSQL *m;
MYSQL_RES *result;
MYSQL_ROW row;
v2_proxy_header v2_header;
char addr_bin[16];
const char *proxy_ipv6 = "2001:db8::6:6";
int protocol= MYSQL_PROTOCOL_TCP;
myheader("test_proxy_header_dbug_remote_connection");
/* Save and override debug_dbug so name resolution does not hit real DNS. */
rc= mysql_query(mysql,
"SET @save_debug_dbug = @@GLOBAL.debug_dbug");
myquery(rc);
rc= mysql_query(mysql,
"SET GLOBAL debug_dbug='+d,vio_peer_addr_fake_ipv6"
",getnameinfo_fake_ipv6,getaddrinfo_fake_good_ipv6'");
myquery(rc);
/* Create a user identified by the hostname that the debug points will produce. */
rc= mysql_query(mysql,
"CREATE USER 'u'@'santa.claus.ipv6.example.com' IDENTIFIED BY 'password'");
myquery(rc);
/* Build a v2 proxy header announcing proxy_ipv6 as the client address. */
memset(&v2_header, 0, sizeof(v2_header));
memcpy(v2_header.sig, "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
v2_header.ver_cmd = (0x2 << 4) | 0x1; /* PROXY command */
v2_header.fam = 0x21; /* TCPv6 */
v2_header.len = htons(36);
inet_pton(AF_INET6, proxy_ipv6, addr_bin);
memcpy(v2_header.addr.ip6.src_addr, addr_bin, 16);
memcpy(v2_header.addr.ip6.dst_addr, addr_bin, 16);
v2_header.addr.ip6.src_port = htons(2222);
v2_header.addr.ip6.dst_port = htons(3306);
m = mysql_client_init(NULL);
DIE_UNLESS(m != NULL);
mysql_optionsv(m, MARIADB_OPT_PROXY_HEADER, &v2_header, (size_t)52);
mysql_optionsv(m, MYSQL_OPT_PROTOCOL, &protocol);
if (!mysql_real_connect(m, opt_host, "u", "password", NULL, opt_port, NULL, 0))
DIE(0);
/* Verify that the server sees the proxied port, and fake hostname. */
rc= mysql_query(m,
"SELECT host FROM information_schema.processlist"
" WHERE ID = connection_id()");
DIE_UNLESS(!rc);
result= mysql_store_result(m);
DIE_UNLESS(result);
row= mysql_fetch_row(result);
DIE_UNLESS(strcmp(row[0], "santa.claus.ipv6.example.com:2222") == 0);
mysql_free_result(result);
mysql_close(m);
rc= mysql_query(mysql, "DROP USER 'u'@'santa.claus.ipv6.example.com'");
myquery(rc);
/* Restore debug_dbug to whatever it was before this test. */
rc= mysql_query(mysql, "SET GLOBAL debug_dbug = @save_debug_dbug");
myquery(rc);
#endif /* !DBUG_OFF */
}
static void test_proxy_header()
{
myheader("test_proxy_header");
@@ -20813,6 +20891,7 @@ static void test_proxy_header()
test_proxy_header_localhost();
test_proxy_header_ignore();
test_proxy_header_limits();
test_proxy_header_dbug_remote_connection();
}