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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
3
debian/autobake-deb.sh
vendored
3
debian/autobake-deb.sh
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
24
mysql-test/suite/galera/r/galera_sst_cn_injection.result
Normal file
24
mysql-test/suite/galera/r/galera_sst_cn_injection.result
Normal 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:");
|
||||
@@ -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');
|
||||
@@ -1,7 +1,6 @@
|
||||
!include ../galera_2nodes.cnf
|
||||
|
||||
[mysqld]
|
||||
wsrep_sst_method=rsync
|
||||
wsrep_sst_auth="root:"
|
||||
wsrep_debug=1
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[rsync]
|
||||
wsrep-sst-method=rsync
|
||||
|
||||
[mariabackup]
|
||||
wsrep-sst-method=mariabackup
|
||||
|
||||
@@ -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:");
|
||||
@@ -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
|
||||
@@ -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');
|
||||
55
mysql-test/suite/galera_3nodes/r/MDEV-38843.result
Normal file
55
mysql-test/suite/galera_3nodes/r/MDEV-38843.result
Normal 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");
|
||||
8
mysql-test/suite/galera_3nodes/t/MDEV-38843.cnf
Normal file
8
mysql-test/suite/galera_3nodes/t/MDEV-38843.cnf
Normal file
@@ -0,0 +1,8 @@
|
||||
!include ../galera_3nodes.cnf
|
||||
|
||||
[mysqld]
|
||||
wsrep-slave-threads=4
|
||||
wsrep-ignore-apply-errors=0
|
||||
|
||||
[ENV]
|
||||
galera_cluster_size=3
|
||||
94
mysql-test/suite/galera_3nodes/t/MDEV-38843.test
Normal file
94
mysql-test/suite/galera_3nodes/t/MDEV-38843.test
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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*/
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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); */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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("
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user