Merge branch 'develop' into MXS-1266
This commit is contained in:
commit
4993fd683c
@ -154,7 +154,7 @@ then
|
||||
echo "Error getting avro-c"
|
||||
exit 1
|
||||
fi
|
||||
avro_filename=`ls -1 *.tar.gz`
|
||||
avro_filename=`ls -1 avro*.tar.gz`
|
||||
avro_dir=`echo "$avro_filename" | sed "s/.tar.gz//"`
|
||||
tar -axf $avro_filename
|
||||
mkdir $avro_dir/build
|
||||
|
@ -16,7 +16,7 @@ endif()
|
||||
|
||||
# Set default values for cache entries and set the MaxScale version
|
||||
include(cmake/defaults.cmake)
|
||||
include(VERSION.cmake)
|
||||
include(VERSION22.cmake)
|
||||
include(ExternalProject)
|
||||
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
||||
@ -41,7 +41,6 @@ find_package(Git)
|
||||
find_package(CURL)
|
||||
find_package(RabbitMQ)
|
||||
find_package(LibUUID)
|
||||
find_package(Jansson)
|
||||
find_package(Avro)
|
||||
find_package(GSSAPI)
|
||||
find_package(SQLite)
|
||||
@ -62,11 +61,7 @@ else()
|
||||
message(STATUS "Using system Connector-C")
|
||||
endif()
|
||||
|
||||
if(NOT JANSSON_FOUND)
|
||||
message(STATUS "Building Jansson from source.")
|
||||
include(cmake/BuildJansson.cmake)
|
||||
endif()
|
||||
|
||||
include(cmake/BuildJansson.cmake)
|
||||
include(cmake/BuildMicroHttpd.cmake)
|
||||
|
||||
include_directories(${JANSSON_INCLUDE_DIR})
|
||||
|
@ -37,6 +37,7 @@ For more details, please refer to:
|
||||
as JSON objects (beta level functionality).
|
||||
|
||||
For more details, please refer to:
|
||||
* [MariaDB MaxScale 2.0.6 Release Notes](Release-Notes/MaxScale-2.0.6-Release-Notes.md)
|
||||
* [MariaDB MaxScale 2.0.5 Release Notes](Release-Notes/MaxScale-2.0.5-Release-Notes.md)
|
||||
* [MariaDB MaxScale 2.0.4 Release Notes](Release-Notes/MaxScale-2.0.4-Release-Notes.md)
|
||||
* [MariaDB MaxScale 2.0.3 Release Notes](Release-Notes/MaxScale-2.0.3-Release-Notes.md)
|
||||
|
@ -31,9 +31,41 @@ passwd=mypasswd
|
||||
filters=DataMartFilter
|
||||
```
|
||||
|
||||
## Filter Options
|
||||
## Filter Parameters
|
||||
|
||||
The tee filter accepts the following options.
|
||||
The tee filter requires a mandatory parameter to define the service to replicate
|
||||
statements to and accepts a number of optional parameters.
|
||||
|
||||
### `match`
|
||||
|
||||
An optional parameter used to limit the queries that will be replicated by the
|
||||
tee filter. The parameter value is a PCRE2 regular expression that is used to
|
||||
match against the SQL text. Only SQL statements that match the text passed as
|
||||
the value of this parameter will be sent to the service defined in the filter
|
||||
section.
|
||||
|
||||
```
|
||||
match=/insert.*into.*order*/
|
||||
```
|
||||
|
||||
### `exclude`
|
||||
|
||||
An optional parameter used to limit the queries that will be replicated by the
|
||||
tee filter. The parameter value is a PCRE2 regular expression that is used to
|
||||
match against the SQL text. Any SQL statements that match the text passed as the
|
||||
value of this parameter will be excluded from the replication stream.
|
||||
|
||||
```
|
||||
exclude=/select.*from.*t1/
|
||||
```
|
||||
|
||||
If both `match` and `exclude` parameters are defined, `exclude` takes
|
||||
precedence.
|
||||
|
||||
### `options`
|
||||
|
||||
The options parameter controls the regular expression options. The following
|
||||
options are accepted.
|
||||
|
||||
|Option |Description |
|
||||
|----------|--------------------------------------------|
|
||||
@ -47,43 +79,7 @@ To use multiple filter options, list them in a comma-separated list.
|
||||
options=case,extended
|
||||
```
|
||||
|
||||
## Filter Parameters
|
||||
|
||||
The tee filter requires a mandatory parameter to define the service to replicate
|
||||
statements to and accepts a number of optional parameters.
|
||||
|
||||
### Match
|
||||
|
||||
An optional parameter used to limit the queries that will be replicated by the
|
||||
tee filter. The parameter value is a regular expression that is used to match
|
||||
against the SQL text. Only SQL statements that matches the text passed as the
|
||||
value of this parameter will be sent to the service defined in the filter
|
||||
section.
|
||||
|
||||
```
|
||||
match=insert.*into.*order*
|
||||
```
|
||||
|
||||
All regular expressions are evaluated with the option to ignore the case of the
|
||||
text, therefore a match option of select will match both insert, INSERT and any
|
||||
form of the word with upper or lowercase characters.
|
||||
|
||||
### Exclude
|
||||
|
||||
An optional parameter used to limit the queries that will be replicated by the
|
||||
tee filter. The parameter value is a regular expression that is used to match
|
||||
against the SQL text. SQL statements that match the text passed as the value of
|
||||
this parameter will be excluded from the replication stream.
|
||||
|
||||
```
|
||||
exclude=select
|
||||
```
|
||||
|
||||
All regular expressions are evaluated with the option to ignore the case of the
|
||||
text, therefore an exclude option of select will exclude statements that contain
|
||||
both select, SELECT or any form of the word with upper or lowercase characters.
|
||||
|
||||
### Source
|
||||
### `source`
|
||||
|
||||
The optional source parameter defines an address that is used to match against
|
||||
the address from which the client connection to MariaDB MaxScale originates.
|
||||
@ -93,7 +89,7 @@ Only sessions that originate from this address will be replicated.
|
||||
source=127.0.0.1
|
||||
```
|
||||
|
||||
### User
|
||||
### `user`
|
||||
|
||||
The optional user parameter defines a user name that is used to match against
|
||||
the user from which the client connection to MariaDB MaxScale originates. Only
|
||||
|
@ -34,7 +34,7 @@ Ubuntu 16.04:
|
||||
sudo apt-get update
|
||||
sudo apt-get install git build-essential libssl-dev ncurses-dev bison flex \
|
||||
cmake perl libtool libcurl4-openssl-dev libpcre3-dev tcl tcl-dev uuid \
|
||||
uuid-dev libsqlite3-dev libgnutls30 libgcrypt20
|
||||
uuid-dev libsqlite3-dev gnutls-dev libgcrypt20-dev
|
||||
```
|
||||
|
||||
### Build and Install MaxScale
|
||||
|
@ -9,7 +9,9 @@ plugin modules that tailor the behavior of the program.
|
||||
|
||||
# Table of Contents
|
||||
|
||||
* [Glossary](#glossary)
|
||||
* [Configuration](#configuration)
|
||||
* [Special Parameter Types](#special-parameter-types)
|
||||
* [Global Settings](#global-settings)
|
||||
* [Service](#service)
|
||||
* [Server](#server)
|
||||
@ -79,7 +81,9 @@ The values of the parameter that are not on the first line need to have at least
|
||||
one whitespace character before them in order for them to be recognized as a
|
||||
part of the multi-line parameter.
|
||||
|
||||
### Sizes
|
||||
### Special Parameter Types
|
||||
|
||||
#### Sizes
|
||||
|
||||
Where _specifically noted_, a number denoting a size can be suffixed by a subset
|
||||
of the IEC binary prefixes or the SI prefixes. In the former case the number
|
||||
@ -104,6 +108,16 @@ max_size=1000000M
|
||||
max_size=1000G
|
||||
max_size=1T
|
||||
```
|
||||
#### Regular Expressions
|
||||
|
||||
When a regular expression (regex) parameter is accepted, the pattern string
|
||||
should be enclosed in slashes e.g. `match=/^select/` defines the pattern
|
||||
`^select`. The slashes allow whitespace to be read from the ends of the regex
|
||||
string contrary to a normal string parameter and are removed before compiling
|
||||
the pattern. For backwards compatibility, the slashes are not yet mandatory.
|
||||
Omitting them is, however, deprecated and will be rejected in the next release
|
||||
of MaxScale. Currently, *QLAFilter* accepts parameters in regular expression
|
||||
form.
|
||||
|
||||
### Global Settings
|
||||
|
||||
|
@ -72,7 +72,7 @@ Status: 200 OK
|
||||
"slaves": [ // List of slave server IDs
|
||||
3001
|
||||
],
|
||||
"statictics": { // Server statistics
|
||||
"statistics": { // Server statistics
|
||||
"connections": 0,
|
||||
"total_connections": 0,
|
||||
"active_operations": 0
|
||||
@ -156,7 +156,7 @@ Status: 200 OK
|
||||
"slaves": [
|
||||
3001
|
||||
],
|
||||
"statictics": {
|
||||
"statistics": {
|
||||
"connections": 0,
|
||||
"total_connections": 0,
|
||||
"active_operations": 0
|
||||
@ -205,7 +205,7 @@ Status: 200 OK
|
||||
"master_id": 3000,
|
||||
"replication_depth": 1,
|
||||
"slaves": [],
|
||||
"statictics": {
|
||||
"statistics": {
|
||||
"connections": 0,
|
||||
"total_connections": 0,
|
||||
"active_operations": 0
|
||||
@ -401,7 +401,7 @@ Response to `GET /v1/server/server1`:
|
||||
3001,
|
||||
3002
|
||||
],
|
||||
"statictics": {
|
||||
"statistics": {
|
||||
"connections": 0,
|
||||
"total_connections": 0,
|
||||
"active_operations": 0
|
||||
|
@ -17,11 +17,26 @@ Significant whitespace in object names is now deprecated. All object names
|
||||
squeezing repeating whitespace and replacing it with hyphens. If any
|
||||
object name conversions take place, a warning will be logged.
|
||||
|
||||
### Regular Expression Parameters
|
||||
|
||||
Modules may now use a built-in regular expression (regex) string parameter type
|
||||
instead of a normal string when accepting patterns. The regex parameters are
|
||||
checked by the config file loader to compile using the PCRE2 library embedded
|
||||
within MaxScale. The only module using the new regex parameter type is currently
|
||||
*QLAFilter*.
|
||||
|
||||
The only action users should take is enclose their regular expressions in
|
||||
slashes, e.g. `match=/^select/` defines the pattern `^select`. The slashes allow
|
||||
whitespace to be read from the ends of the regex string contrary to a normal
|
||||
string parameter and are removed before compiling the pattern. For backwards
|
||||
compatibility, the slashes are not yet mandatory. Omitting them is, however,
|
||||
deprecated and will be rejected in the next release of MaxScale.
|
||||
|
||||
### NamedServerFilter
|
||||
|
||||
This filter now uses the PCRE2-libarary to match queries. Previously, it used
|
||||
the POSIX-version of PCRE2. The filter also accepts multiple match-server pairs.
|
||||
Please see the NamedServerFilter documentation for details.
|
||||
The filter now accepts multiple match-server pairs. Please see the
|
||||
[NamedServerFilter](../Filters/Named-Server-Filter.md) documentation for
|
||||
details.
|
||||
|
||||
### Tee Filter
|
||||
|
||||
@ -35,6 +50,10 @@ In addition to the aforementioned requirements, a failure to create a branched
|
||||
session no longer causes the actual client session to be closed. In most cases,
|
||||
this is desired behavior.
|
||||
|
||||
The `match` and `exclude` parameters were changed to use PCRE2 syntax for the
|
||||
regular expressions. The regular expression should be enclosed by slashes
|
||||
e.g. `match=/select.*from.*test/`.
|
||||
|
||||
## Dropped Features
|
||||
|
||||
### MaxAdmin
|
||||
|
@ -101,11 +101,14 @@ two steps from above.
|
||||
|
||||
## Creating additional grants for users
|
||||
|
||||
Because MariaDB MaxScale sits between the clients and the backend databases,
|
||||
the backend databases will see all clients as if they were connecting from
|
||||
MariaDB MaxScale's address.
|
||||
This usually requires users to create additional grants for MariaDB MaxScale's hostname.
|
||||
The best way to describe this process is with an example.
|
||||
**Note:** The client host and MaxScale host must have the same username and
|
||||
password for both client and MaxScale hosts.
|
||||
|
||||
Because MariaDB MaxScale sits between the clients and the backend databases, the
|
||||
backend databases will see all clients as if they were connecting from MariaDB
|
||||
MaxScale's address. This usually requires users to create additional grants for
|
||||
MariaDB MaxScale's hostname. The best way to describe this process is with an
|
||||
example.
|
||||
|
||||
User `'jdoe'@'192.168.0.200` has the following grant on the cluster:
|
||||
`GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'jdoe'@'192.168.0.200'`.
|
||||
@ -134,18 +137,22 @@ MariaDB [(none)]> SHOW GRANTS FOR 'jdoe'@'192.168.0.200';
|
||||
```
|
||||
Then creating the user `'jdoe'@'192.168.0.101'` and giving it the same grants:
|
||||
```
|
||||
MariaDB [(none)]> CREATE USER 'jdoe'@'192.168.0.101';
|
||||
MariaDB [(none)]> CREATE USER 'jdoe'@'192.168.0.101' IDENTIFIED BY 'secret_password';
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
MariaDB [(none)]> GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'jdoe'@'192.168.0.101';
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
```
|
||||
|
||||
The other option is to use a wildcard grant like
|
||||
`GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'jdoe'@'%'`.
|
||||
This is more convenient but also less secure than having specific grants
|
||||
for both the client's address and MariaDB MaxScale's address.
|
||||
The other option is to use a wildcard grant like the following:
|
||||
|
||||
```
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'jdoe'@'%' IDENTIFIED BY 'secret_password'
|
||||
```
|
||||
|
||||
This is more convenient but less secure than having specific grants for both the
|
||||
client's address and MariaDB MaxScale's address as it allows access from all
|
||||
hosts.
|
||||
|
||||
## Creating the configuration file
|
||||
|
||||
|
20
Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md
Normal file
20
Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Upgrading MariaDB MaxScale from 2.1 to 2.2
|
||||
|
||||
This document describes possible issues upgrading MariaDB MaxScale from version
|
||||
2.1 to 2.2.
|
||||
|
||||
For more information about MariaDB MaxScale 2.2, please refer to the
|
||||
[ChangeLog](../Changelog.md).
|
||||
|
||||
For a complete list of changes in MaxScale 2.2.0, refer to the
|
||||
[MaxScale 2.2.0 Release Notes](../Release-Notes/MaxScale-2.2.0-Release-Notes.md).
|
||||
|
||||
Before starting the upgrade, we recommend you back up your current configuration
|
||||
file.
|
||||
|
||||
### Regular Expression Parameters
|
||||
|
||||
Modules may now use a built-in regular expression string parameter type instead
|
||||
of a normal string when accepting patterns. The modules that use the new regex
|
||||
parameter type are *qlafilter* and *tee*. When inputting pattern, enclose the
|
||||
string in slashes, e.g. `match=/^select/` defines the pattern `^select`.
|
14
VERSION21.cmake
Normal file
14
VERSION21.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
# MaxScale version for CMake
|
||||
#
|
||||
# This file contains cache values for CMake which control MaxScale's version
|
||||
# number.
|
||||
|
||||
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
|
||||
set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor version")
|
||||
set(MAXSCALE_VERSION_PATCH "4" CACHE STRING "Patch version")
|
||||
|
||||
# This should only be incremented if a package is rebuilt
|
||||
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")
|
||||
|
||||
set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
|
||||
set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
|
14
VERSION22.cmake
Normal file
14
VERSION22.cmake
Normal file
@ -0,0 +1,14 @@
|
||||
# MaxScale version for CMake
|
||||
#
|
||||
# This file contains cache values for CMake which control MaxScale's version
|
||||
# number.
|
||||
|
||||
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
|
||||
set(MAXSCALE_VERSION_MINOR "2" CACHE STRING "Minor version")
|
||||
set(MAXSCALE_VERSION_PATCH "0" CACHE STRING "Patch version")
|
||||
|
||||
# This should only be incremented if a package is rebuilt
|
||||
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")
|
||||
|
||||
set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
|
||||
set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
|
@ -8,7 +8,7 @@ set(JANSSON_TAG "v2.9" CACHE STRING "Jansson Git tag")
|
||||
ExternalProject_Add(jansson
|
||||
GIT_REPOSITORY ${JANSSON_REPO}
|
||||
GIT_TAG ${JANSSON_TAG}
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/jansson/install -DCMAKE_C_FLAGS=-fPIC
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/jansson/install -DCMAKE_C_FLAGS=-fPIC -DJANSSON_BUILD_DOCS=OFF
|
||||
BINARY_DIR ${CMAKE_BINARY_DIR}/jansson
|
||||
INSTALL_DIR ${CMAKE_BINARY_DIR}/jansson/install
|
||||
UPDATE_COMMAND "")
|
||||
|
@ -57,10 +57,10 @@ uint64_t atomic_add_uint64(uint64_t *variable, int64_t value);
|
||||
* @param variable Pointer the the variable to load from
|
||||
* @return The stored value
|
||||
*/
|
||||
int atomic_load_int32(int *variable);
|
||||
int64_t atomic_load_int64(int64_t *variable);
|
||||
uint64_t atomic_load_uint64(uint64_t *variable);
|
||||
void* atomic_load_ptr(void **variable);
|
||||
int atomic_load_int32(const int *variable);
|
||||
int64_t atomic_load_int64(const int64_t *variable);
|
||||
uint64_t atomic_load_uint64(const uint64_t *variable);
|
||||
void* atomic_load_ptr(void * const *variable);
|
||||
|
||||
/**
|
||||
* Implementation of an atomic store operation for the GCC environment.
|
||||
|
308
include/maxscale/backend.hh
Normal file
308
include/maxscale/backend.hh
Normal file
@ -0,0 +1,308 @@
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <tr1/memory>
|
||||
|
||||
#include <maxscale/service.h>
|
||||
#include <maxscale/session_command.hh>
|
||||
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
|
||||
class Backend
|
||||
{
|
||||
Backend(const Backend&);
|
||||
Backend& operator =(const Backend&);
|
||||
public:
|
||||
|
||||
/**
|
||||
* How is the backend being closed
|
||||
*/
|
||||
enum close_type
|
||||
{
|
||||
CLOSE_NORMAL,
|
||||
CLOSE_FATAL
|
||||
};
|
||||
|
||||
/**
|
||||
* What type of a response we expect from the backend
|
||||
*/
|
||||
enum response_type
|
||||
{
|
||||
EXPECT_RESPONSE,
|
||||
NO_RESPONSE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create new Backend
|
||||
*
|
||||
* @param ref Server reference used by this backend
|
||||
*/
|
||||
Backend(SERVER_REF* ref);
|
||||
|
||||
virtual ~Backend();
|
||||
|
||||
/**
|
||||
* @brief Execute the next session command in the queue
|
||||
*
|
||||
* @return True if the command was executed successfully
|
||||
*/
|
||||
virtual bool execute_session_command();
|
||||
|
||||
/**
|
||||
* @brief Add a new session command to the tail of the command queue
|
||||
*
|
||||
* @param buffer Session command to add
|
||||
* @param sequence Sequence identifier of this session command, returned when
|
||||
* the session command is completed
|
||||
*/
|
||||
void append_session_command(GWBUF* buffer, uint64_t sequence);
|
||||
void append_session_command(const SSessionCommand& sescmd);
|
||||
void append_session_command(const SessionCommandList& sescmdlist);
|
||||
|
||||
/**
|
||||
* @brief Mark the current session command as successfully executed
|
||||
*
|
||||
* This should be called when the response to the command is received
|
||||
*
|
||||
* @return The sequence identifier for this session command
|
||||
*/
|
||||
uint64_t complete_session_command();
|
||||
|
||||
/**
|
||||
* @brief Check if backend has session commands
|
||||
*
|
||||
* @return Number of session commands
|
||||
*/
|
||||
size_t session_command_count() const;
|
||||
|
||||
/**
|
||||
* @brief Get the first session command
|
||||
*
|
||||
* Returns the first session command in the list of session commands
|
||||
* to be executed.
|
||||
*
|
||||
* This should only be called when at least one session command has been
|
||||
* added to the backend. If no session commands have been added, behavior
|
||||
* is undefined.
|
||||
*
|
||||
* @return The first session command
|
||||
*/
|
||||
const SSessionCommand& next_session_command() const;
|
||||
|
||||
/**
|
||||
* @brief Get pointer to server reference
|
||||
*
|
||||
* @return Pointer to server reference
|
||||
*/
|
||||
SERVER_REF* backend() const;
|
||||
|
||||
/**
|
||||
* @brief Get pointer to server
|
||||
*
|
||||
* @return Pointer to server
|
||||
*/
|
||||
SERVER* server() const;
|
||||
|
||||
/**
|
||||
* @brief Check if a connection to this backend can be made
|
||||
*
|
||||
* @return True if the backend has not failed and a connection can be attempted
|
||||
*/
|
||||
bool can_connect() const;
|
||||
|
||||
/**
|
||||
* @brief Create a new connection
|
||||
*
|
||||
* @param session The session to which the connection is linked
|
||||
*
|
||||
* @return True if connection was successfully created
|
||||
*/
|
||||
bool connect(MXS_SESSION* session);
|
||||
|
||||
/**
|
||||
* @brief Close the backend
|
||||
*
|
||||
* This will close all active connections created by the backend.
|
||||
*/
|
||||
void close(close_type type = CLOSE_NORMAL);
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the internal DCB
|
||||
*
|
||||
* @return Pointer to internal DCB
|
||||
*/
|
||||
DCB* dcb() const;
|
||||
|
||||
/**
|
||||
* @brief Write data to the backend server
|
||||
*
|
||||
* @param buffer Buffer containing the data to write
|
||||
* @param expect_response Whether to expect a response to the query
|
||||
*
|
||||
* @return True if data was written successfully
|
||||
*/
|
||||
bool write(GWBUF* buffer, response_type type = EXPECT_RESPONSE);
|
||||
|
||||
/**
|
||||
* @brief Write an authentication switch request to the backend server
|
||||
*
|
||||
* @param buffer Buffer containing the authentication switch request
|
||||
*
|
||||
* @return True if request was successfully written
|
||||
*/
|
||||
bool auth(GWBUF* buffer);
|
||||
|
||||
/**
|
||||
* @brief Mark that a reply to a query was received and processed
|
||||
*/
|
||||
void ack_write();
|
||||
|
||||
/**
|
||||
* @brief Store a command
|
||||
*
|
||||
* The command is stored and executed once the session can execute
|
||||
* the next command.
|
||||
*
|
||||
* @param buffer Buffer to store
|
||||
*/
|
||||
void store_command(GWBUF* buffer);
|
||||
|
||||
/**
|
||||
* @brief Write the stored command to the backend server
|
||||
*
|
||||
* @return True if command was written successfully
|
||||
*/
|
||||
bool write_stored_command();
|
||||
|
||||
/**
|
||||
* @brief Check if backend is in use
|
||||
*
|
||||
* @return True if backend is in use
|
||||
*/
|
||||
bool in_use() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the backend server reference is active
|
||||
*
|
||||
* @return True if the server reference is active
|
||||
*/
|
||||
bool is_active() const;
|
||||
|
||||
/**
|
||||
* @brief Check if backend is waiting for a result
|
||||
*
|
||||
* @return True if backend is waiting for a result
|
||||
*/
|
||||
bool is_waiting_result() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the backend is closed
|
||||
*
|
||||
* @return True if the backend is closed
|
||||
*/
|
||||
bool is_closed() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the server is a master
|
||||
*
|
||||
* @return True if server is a master
|
||||
*/
|
||||
bool is_master() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the server is a slave
|
||||
*
|
||||
* @return True if the server is a slave
|
||||
*/
|
||||
bool is_slave() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the server is a relay server
|
||||
*
|
||||
* @return True if the server is a relay server
|
||||
*/
|
||||
bool is_relay() const;
|
||||
|
||||
/**
|
||||
* @brief Check if the backend has failed fatally
|
||||
*
|
||||
* When a fatal failure occurs in a backend, the backend server can no longer
|
||||
* be used by this session. Fatal failures can occur when the execution of
|
||||
* a session command fails on the backend but the expected result is different.
|
||||
*
|
||||
* @return True if a fatal failure has occurred in the backend server
|
||||
*/
|
||||
bool has_failed() const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the object name of this server
|
||||
*
|
||||
* @return The unique object name of this server
|
||||
*/
|
||||
const char* name() const;
|
||||
|
||||
/**
|
||||
* @brief Get the address and port as a string
|
||||
*
|
||||
* @return The address and port combined into one string
|
||||
*/
|
||||
const char* uri() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Internal state of the backend
|
||||
*/
|
||||
enum backend_state
|
||||
{
|
||||
IN_USE = 0x01, /**< Backend has been taken into use */
|
||||
WAITING_RESULT = 0x02, /**< Waiting for a reply */
|
||||
FATAL_FAILURE = 0x04 /**< Backend references that should be dropped */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Clear state
|
||||
*
|
||||
* @param state State to clear
|
||||
*/
|
||||
void clear_state(backend_state state);
|
||||
|
||||
/**
|
||||
* @brief Set state
|
||||
*
|
||||
* @param state State to set
|
||||
*/
|
||||
void set_state(backend_state state);
|
||||
|
||||
|
||||
bool m_closed; /**< True if a connection has been opened and closed */
|
||||
SERVER_REF* m_backend; /**< Backend server */
|
||||
DCB* m_dcb; /**< Backend DCB */
|
||||
mxs::Buffer m_pending_cmd; /**< Pending commands */
|
||||
int m_state; /**< State of the backend */
|
||||
SessionCommandList m_session_commands; /**< List of session commands that are
|
||||
* to be executed on this backend server */
|
||||
std::string m_uri; /**< The combined address and port */
|
||||
};
|
||||
|
||||
typedef std::tr1::shared_ptr<Backend> SBackend;
|
||||
typedef std::list<SBackend> BackendList;
|
||||
}
|
@ -369,6 +369,28 @@ pcre2_code* config_get_compiled_regex(const MXS_CONFIG_PARAMETER *params,
|
||||
const char *key, uint32_t options,
|
||||
uint32_t* output_ovec_size);
|
||||
|
||||
/**
|
||||
* Get multiple compiled regular expressions and the maximum ovector size of
|
||||
* the patterns. The returned regex codes should be freed by the caller.
|
||||
*
|
||||
* @param params List of configuration parameters
|
||||
* @param keys An array of parameter names. If an element is not found in @c params,
|
||||
* the corresponding spot in @c out_codes is set to NULL.
|
||||
* @param keys_size Size of both @c keys and @c out_arr
|
||||
* @param options PCRE2 compilation options
|
||||
* @param out_ovec_size If not NULL, the maximum ovector size of successfully
|
||||
* compiled patterns is written here.
|
||||
* @param out_codes An array of handles to compiled codes. The referenced pointers
|
||||
* will be set to point to the compiled codes. The array size must be equal to
|
||||
* @c keys array size and it must contain valid pointers.
|
||||
*
|
||||
* @return True, if all patterns given by @c keys were successfully compiled.
|
||||
* False otherwise.
|
||||
*/
|
||||
bool config_get_compiled_regexes(const MXS_CONFIG_PARAMETER *params,
|
||||
const char* keys[], int keys_size,
|
||||
uint32_t options, uint32_t* out_ovec_size,
|
||||
pcre2_code** out_codes[]);
|
||||
/**
|
||||
* Parse a list of server names and write the results in an array of strings
|
||||
* with one server name in each. The output array and its elements should be
|
||||
|
@ -83,4 +83,12 @@ mxs_mysql_name_kind_t mxs_mysql_name_to_pcre(char *pcre,
|
||||
const char *mysql,
|
||||
mxs_pcre_quote_approach_t approach);
|
||||
|
||||
/**
|
||||
* Set the server information
|
||||
*
|
||||
* @param mysql A MySQL handle to the server.
|
||||
* @param server The server whose version information should be updated.
|
||||
*/
|
||||
void mxs_mysql_set_server_version(MYSQL* mysql, SERVER* server);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -55,4 +55,26 @@ mxs_pcre2_result_t mxs_pcre2_simple_match(const char* pattern, const char* subje
|
||||
void mxs_pcre2_print_error(int errorcode, const char *module_name, const char *filename,
|
||||
int line_num, const char* func_name);
|
||||
|
||||
/**
|
||||
* Check that @c subject is valid. A valid subject matches @c re_match yet does
|
||||
* not match @c re_exclude. If an error occurs, an error code is written to
|
||||
* @c match_error_out.
|
||||
*
|
||||
* @param re_match If not NULL, the subject must match this to be valid. If NULL,
|
||||
* all inputs are considered valid.
|
||||
* @param re_exclude If not NULL, will invalidate a matching subject. Even subjects
|
||||
* validated by @c re_match can be invalidated. If NULL, invalidates nothing.
|
||||
* @param md PCRE2 match data block
|
||||
* @param subject Subject string. Should NOT be an empty string.
|
||||
* @param length Length of subject. Can be zero for 0-terminated strings.
|
||||
* @param calling_module Which module the function was called from. Can be NULL.
|
||||
* Used for log messages.
|
||||
*
|
||||
* @return True, if subject is considered valid. False if subject is not valid or
|
||||
* an error occurred.
|
||||
*/
|
||||
bool mxs_pcre2_check_match_exclude(pcre2_code* re_match, pcre2_code* re_exclude,
|
||||
pcre2_match_data* md, const char* subject,
|
||||
int length, const char* calling_module);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <maxscale/utils.h>
|
||||
#include <mysql.h>
|
||||
#include <mysql_com.h>
|
||||
#include <mysqld_error.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -484,4 +485,23 @@ mysql_server_cmd_t mxs_mysql_current_command(MXS_SESSION* session);
|
||||
void mysql_num_response_packets(GWBUF *buf, uint8_t cmd,
|
||||
int* npackets, size_t *nbytes);
|
||||
|
||||
/**
|
||||
* @brief Return current database of the session
|
||||
*
|
||||
* If no active database is in use, the database is an empty string.
|
||||
*
|
||||
* @param session Session to inspect
|
||||
*
|
||||
* @return The current database
|
||||
*/
|
||||
const char* mxs_mysql_get_current_db(MXS_SESSION* session);
|
||||
|
||||
/**
|
||||
* @brief Set the currently active database for a session
|
||||
*
|
||||
* @param session Session to modify
|
||||
* @param db The new database
|
||||
*/
|
||||
void mxs_mysql_set_current_db(MXS_SESSION* session, const char* db);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -82,19 +82,20 @@ typedef enum qc_query_type
|
||||
*/
|
||||
typedef enum qc_query_op
|
||||
{
|
||||
QUERY_OP_UNDEFINED = 0,
|
||||
QUERY_OP_SELECT = (1 << 0),
|
||||
QUERY_OP_UPDATE = (1 << 1),
|
||||
QUERY_OP_INSERT = (1 << 2),
|
||||
QUERY_OP_DELETE = (1 << 3),
|
||||
QUERY_OP_TRUNCATE = (1 << 4),
|
||||
QUERY_OP_ALTER = (1 << 5),
|
||||
QUERY_OP_CREATE = (1 << 6),
|
||||
QUERY_OP_DROP = (1 << 7),
|
||||
QUERY_OP_CHANGE_DB = (1 << 8),
|
||||
QUERY_OP_LOAD = (1 << 9),
|
||||
QUERY_OP_GRANT = (1 << 10),
|
||||
QUERY_OP_REVOKE = (1 << 11)
|
||||
QUERY_OP_UNDEFINED = 0,
|
||||
QUERY_OP_SELECT,
|
||||
QUERY_OP_UPDATE,
|
||||
QUERY_OP_INSERT,
|
||||
QUERY_OP_DELETE,
|
||||
QUERY_OP_TRUNCATE,
|
||||
QUERY_OP_ALTER,
|
||||
QUERY_OP_CREATE,
|
||||
QUERY_OP_DROP,
|
||||
QUERY_OP_CHANGE_DB,
|
||||
QUERY_OP_LOAD,
|
||||
QUERY_OP_GRANT,
|
||||
QUERY_OP_REVOKE,
|
||||
QUERY_OP_EXECUTE,
|
||||
} qc_query_op_t;
|
||||
|
||||
/**
|
||||
@ -373,6 +374,25 @@ typedef struct query_classifier
|
||||
* exhaustion or equivalent.
|
||||
*/
|
||||
int32_t (*qc_get_preparable_stmt)(GWBUF* stmt, GWBUF** preparable_stmt);
|
||||
|
||||
/**
|
||||
* Set the version of the server. The version may affect how a statement
|
||||
* is classified. Note that the server version is maintained separately
|
||||
* for each thread.
|
||||
*
|
||||
* @param version Version encoded as MariaDB encodes the version, i.e.:
|
||||
* version = major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
void (*qc_set_server_version)(uint64_t version);
|
||||
|
||||
/**
|
||||
* Get the thread specific version assumed of the server. If the version has
|
||||
* not been set, all values are 0.
|
||||
*
|
||||
* @param version The version encoded as MariaDB encodes the version, i.e.:
|
||||
* version = major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
void (*qc_get_server_version)(uint64_t* version);
|
||||
} QUERY_CLASSIFIER;
|
||||
|
||||
/**
|
||||
@ -758,4 +778,23 @@ const char* qc_type_to_string(qc_query_type_t type);
|
||||
*/
|
||||
char* qc_typemask_to_string(uint32_t typemask);
|
||||
|
||||
/**
|
||||
* Set the version of the server. The version may affect how a statement
|
||||
* is classified. Note that the server version is maintained separately
|
||||
* for each thread.
|
||||
*
|
||||
* @param version Version encoded as MariaDB encodes the version, i.e.:
|
||||
* version = major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
void qc_set_server_version(uint64_t version);
|
||||
|
||||
/**
|
||||
* Get the thread specific version assumed of the server. If the version has
|
||||
* not been set, all values are 0.
|
||||
*
|
||||
* @return The version as MariaDB encodes the version, i.e:
|
||||
* version = major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
uint64_t qc_get_server_version();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -25,9 +25,11 @@
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
#define MAX_SERVER_NAME_LEN 1024
|
||||
#define MAX_SERVER_ADDRESS_LEN 1024
|
||||
#define MAX_SERVER_MONUSER_LEN 1024
|
||||
#define MAX_SERVER_MONPW_LEN 1024
|
||||
#define MAX_SERVER_MONPW_LEN 1024
|
||||
#define MAX_SERVER_VERSION_LEN 256
|
||||
|
||||
#define MAX_NUM_SLAVES 128 /**< Maximum number of slaves under a single server*/
|
||||
|
||||
/**
|
||||
@ -61,6 +63,32 @@ typedef struct
|
||||
int n_persistent; /**< Current persistent pool */
|
||||
} SERVER_STATS;
|
||||
|
||||
/**
|
||||
* The server version.
|
||||
*/
|
||||
typedef struct server_version
|
||||
{
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t patch;
|
||||
} SERVER_VERSION;
|
||||
|
||||
static inline void server_decode_version(uint64_t version, SERVER_VERSION* server_version)
|
||||
{
|
||||
uint32_t major = version / 10000;
|
||||
uint32_t minor = (version - major * 10000) / 100;
|
||||
uint32_t patch = version - major * 10000 - minor * 100;
|
||||
|
||||
server_version->major = major;
|
||||
server_version->minor = minor;
|
||||
server_version->patch = patch;
|
||||
}
|
||||
|
||||
static uint64_t server_encode_version(const SERVER_VERSION* server_version)
|
||||
{
|
||||
return server_version->major * 10000 + server_version->minor * 100 + server_version->patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SERVER structure defines a backend server. Each server has a name
|
||||
* or IP address for the server, a port that the server listens on and
|
||||
@ -74,7 +102,7 @@ typedef struct server
|
||||
#endif
|
||||
SPINLOCK lock; /**< Common access lock */
|
||||
char *unique_name; /**< Unique name for the server */
|
||||
char name[MAX_SERVER_NAME_LEN]; /**< Server name/IP address*/
|
||||
char name[MAX_SERVER_ADDRESS_LEN]; /**< Server name/IP address*/
|
||||
unsigned short port; /**< Port to listen on */
|
||||
char *protocol; /**< Protocol module to use */
|
||||
char *authenticator; /**< Authenticator module name */
|
||||
@ -88,7 +116,8 @@ typedef struct server
|
||||
SERVER_STATS stats; /**< The server statistics */
|
||||
struct server *next; /**< Next server */
|
||||
struct server *nextdb; /**< Next server in list attached to a service */
|
||||
char *server_string; /**< Server version string, i.e. MySQL server version */
|
||||
char version_string[MAX_SERVER_VERSION_LEN]; /**< Server version string, i.e. MySQL server version */
|
||||
uint64_t version; /**< Server version */
|
||||
long node_id; /**< Node id, server_id for M/S or local_index for Galera */
|
||||
int rlag; /**< Replication Lag for Master / Slave replication */
|
||||
unsigned long node_ts; /**< Last timestamp set from M/S monitor module */
|
||||
@ -308,7 +337,9 @@ extern DCB *server_get_persistent(SERVER *server, const char *user, const char
|
||||
extern void server_update_address(SERVER *server, const char *address);
|
||||
extern void server_update_port(SERVER *server, unsigned short port);
|
||||
extern unsigned int server_map_status(const char *str);
|
||||
extern bool server_set_version_string(SERVER* server, const char* string);
|
||||
extern void server_set_version_string(SERVER* server, const char* version_string);
|
||||
extern void server_set_version(SERVER* server, const char* version_string, uint64_t version);
|
||||
extern uint64_t server_get_version(const SERVER* server);
|
||||
extern void server_set_status(SERVER *server, int bit);
|
||||
extern void server_clear_status(SERVER *server, int bit);
|
||||
|
||||
|
@ -355,4 +355,28 @@ static inline uint64_t service_get_capabilities(const SERVICE *service)
|
||||
return service->capabilities;
|
||||
}
|
||||
|
||||
typedef enum service_version_which_t
|
||||
{
|
||||
SERVICE_VERSION_ANY, /*< Any version of the servers of a service. */
|
||||
SERVICE_VERSION_MIN, /*< The minimum version. */
|
||||
SERVICE_VERSION_MAX, /*< The maximum version. */
|
||||
} service_version_which_t;
|
||||
|
||||
/**
|
||||
* Return the version of the service. The returned version can be
|
||||
*
|
||||
* - the version of any (in practice the first) server associated
|
||||
* with the service,
|
||||
* - the smallest version of any of the servers associated with
|
||||
* the service, or
|
||||
* - the largest version of any of the servers associated with
|
||||
* the service.
|
||||
*
|
||||
* @param service The service.
|
||||
* @param which Which version.
|
||||
*
|
||||
* @return The version of the service.
|
||||
*/
|
||||
uint64_t service_get_version(const SERVICE *service, service_version_which_t which);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -12,18 +12,21 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
|
||||
#include <tr1/memory>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <maxscale/buffer.hh>
|
||||
|
||||
using namespace maxscale;
|
||||
|
||||
class SessionCommand;
|
||||
typedef std::list<SessionCommand> SessionCommandList;
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
class SessionCommand
|
||||
{
|
||||
SessionCommand(const SessionCommand&);
|
||||
SessionCommand& operator=(const SessionCommand&);
|
||||
public:
|
||||
/**
|
||||
* @brief Mark reply as received
|
||||
@ -54,7 +57,7 @@ public:
|
||||
* @brief Creates a copy of the internal buffer
|
||||
* @return A copy of the internal buffer
|
||||
*/
|
||||
Buffer copy_buffer() const;
|
||||
mxs::Buffer copy_buffer() const;
|
||||
|
||||
/**
|
||||
* @brief Create a new session command
|
||||
@ -75,8 +78,13 @@ public:
|
||||
std::string to_string();
|
||||
|
||||
private:
|
||||
Buffer m_buffer; /**< The buffer containing the command */
|
||||
uint8_t m_command; /**< The command being executed */
|
||||
uint64_t m_pos; /**< Unique position identifier */
|
||||
bool m_reply_sent; /**< Whether the session command reply has been sent */
|
||||
mxs::Buffer m_buffer; /**< The buffer containing the command */
|
||||
uint8_t m_command; /**< The command being executed */
|
||||
uint64_t m_pos; /**< Unique position identifier */
|
||||
bool m_reply_sent; /**< Whether the session command reply has been sent */
|
||||
};
|
||||
|
||||
typedef std::tr1::shared_ptr<SessionCommand> SSessionCommand;
|
||||
typedef std::list<SSessionCommand> SessionCommandList;
|
||||
|
||||
}
|
@ -607,6 +607,7 @@ add_test_executable(test_hints.cpp test_hints hints2 LABELS hintfilter LIGHT REP
|
||||
|
||||
# Binlogrouter tests, these heavily alter the replication so they are run last
|
||||
add_test_executable(avro.cpp avro avro LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
|
||||
add_test_executable(avro_alter.cpp avro_alter avro LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
|
||||
|
||||
# Test avrorouter file compression
|
||||
#add_test_script(avro_compression avro avro_compression LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
|
||||
|
@ -87,6 +87,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
execute_query(test.repl->nodes[0], "DROP TABLE test.t1;RESET MASTER");
|
||||
test.stop_timeout();
|
||||
test.repl->fix_replication();
|
||||
|
||||
return test.global_result;
|
||||
|
57
maxscale-system-test/avro_alter.cpp
Normal file
57
maxscale-system-test/avro_alter.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @file avro_alter.cpp Test ALTER TABLE handling of avrorouter
|
||||
*/
|
||||
|
||||
#include "testconnections.h"
|
||||
#include <sstream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
TestConnections test(argc, argv);
|
||||
test.set_timeout(600);
|
||||
test.ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
|
||||
|
||||
/** Start master to binlogrouter replication */
|
||||
if (!test.replicate_from_master())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
test.set_timeout(120);
|
||||
test.repl->connect();
|
||||
|
||||
execute_query_silent(test.repl->nodes[0], "DROP TABLE test.t1");
|
||||
execute_query(test.repl->nodes[0], "CREATE TABLE test.t1(id INT)");
|
||||
execute_query(test.repl->nodes[0], "INSERT INTO test.t1 VALUES (1)");
|
||||
execute_query(test.repl->nodes[0], "ALTER TABLE test.t1 ADD COLUMN a VARCHAR(100)");
|
||||
execute_query(test.repl->nodes[0], "INSERT INTO test.t1 VALUES (2, \"a\")");
|
||||
execute_query(test.repl->nodes[0], "ALTER TABLE test.t1 ADD COLUMN b FLOAT");
|
||||
execute_query(test.repl->nodes[0], "INSERT INTO test.t1 VALUES (3, \"b\", 3.0)");
|
||||
execute_query(test.repl->nodes[0], "ALTER TABLE test.t1 CHANGE COLUMN b c DATETIME(3)");
|
||||
execute_query(test.repl->nodes[0], "INSERT INTO test.t1 VALUES (4, \"c\", NOW())");
|
||||
execute_query(test.repl->nodes[0], "ALTER TABLE test.t1 DROP COLUMN c");
|
||||
execute_query(test.repl->nodes[0], "INSERT INTO test.t1 VALUES (5, \"d\")");
|
||||
|
||||
test.repl->close_connections();
|
||||
|
||||
/** Give avrorouter some time to process the events */
|
||||
test.stop_timeout();
|
||||
sleep(10);
|
||||
test.set_timeout(120);
|
||||
|
||||
for (int i = 1; i <=5; i++)
|
||||
{
|
||||
std::stringstream cmd;
|
||||
cmd << "maxavrocheck -d /var/lib/maxscale/avro/test.t1.00000" << i << ".avro|wc -l";
|
||||
char* rows = test.ssh_maxscale_output(true, cmd.str().c_str());
|
||||
int nrows = atoi(rows);
|
||||
free(rows);
|
||||
test.add_result(nrows != 1, "Expected 1 line in file number %d, got %d", i, nrows);
|
||||
}
|
||||
|
||||
execute_query(test.repl->nodes[0], "DROP TABLE test.t1;RESET MASTER");
|
||||
test.repl->fix_replication();
|
||||
|
||||
return test.global_result;
|
||||
}
|
@ -11,7 +11,7 @@ user=maxskysql
|
||||
passwd= skysql
|
||||
|
||||
[RW Split Router]
|
||||
connection_timeout=30
|
||||
connection_timeout=10
|
||||
type=service
|
||||
router= readwritesplit
|
||||
servers=server1, server2, server3,server4
|
||||
|
@ -47,7 +47,8 @@ int main(int argc, char** argv)
|
||||
test.add_result(!mysql_stmt_prepare(stmt, query, strlen(query)), "Binary protocol preparation should fail");
|
||||
mysql_stmt_close(stmt);
|
||||
|
||||
test.try_query(test.conn_rwsplit, "DROP TABLE test.t1");
|
||||
test.repl->connect();
|
||||
test.try_query(test.repl->nodes[0], "DROP TABLE test.t1");
|
||||
|
||||
return test.global_result;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ int main(int argc, char *argv[])
|
||||
sprintf(str, "rules%d", i);
|
||||
Test->set_timeout(180);
|
||||
copy_rules(Test, str, rules_dir);
|
||||
Test->ssh_maxscale(true, "maxadmin call command dbfwfilter rules/reload \"Database Firewall\"");
|
||||
Test->ssh_maxscale(true, "maxadmin call command dbfwfilter rules/reload Database-Firewall");
|
||||
|
||||
int local_result = 0;
|
||||
sprintf(pass_file, "%s/fw/pass%d", test_dir, i);
|
||||
|
@ -432,6 +432,7 @@ int Mariadb_nodes::start_replication()
|
||||
"mysql -u root %s -e \"STOP SLAVE; RESET SLAVE; RESET SLAVE ALL; RESET MASTER; SET GLOBAL read_only=OFF;\"",
|
||||
socket_cmd[i]);
|
||||
ssh_node(i, str, true);
|
||||
ssh_node(i, "sudo rm -f /etc/my.cnf.d/kerb.cnf", true);
|
||||
}
|
||||
|
||||
sprintf(str, "%s/create_user.sh", test_dir);
|
||||
|
@ -5,7 +5,7 @@
|
||||
"column": "a"
|
||||
},
|
||||
"with": {
|
||||
"fill": "X"
|
||||
"fill": "Y"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -22,7 +22,7 @@
|
||||
},
|
||||
"with": {
|
||||
"value": "012345-ABCD",
|
||||
"fill": "X"
|
||||
"fill": "Y"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -11,12 +11,12 @@ insert into masking values ("hello", NULL, NULL);
|
||||
insert into masking values (NULL, NULL, NULL);
|
||||
select * from masking;
|
||||
a x y
|
||||
XXXXX hello hello
|
||||
YYYYY hello hello
|
||||
NULL hello hello
|
||||
XXXXX NULL hello
|
||||
XXXXX hello NULL
|
||||
YYYYY NULL hello
|
||||
YYYYY hello NULL
|
||||
NULL NULL hello
|
||||
XXXXX NULL NULL
|
||||
YYYYY NULL NULL
|
||||
NULL NULL NULL
|
||||
drop table masking;
|
||||
create table masking (x TEXT, a TEXT, y TEXT);
|
||||
@ -29,10 +29,10 @@ insert into masking values ("hello", NULL, NULL);
|
||||
insert into masking values (NULL, NULL, NULL);
|
||||
select * from masking;
|
||||
x a y
|
||||
hello XXXXX hello
|
||||
NULL XXXXX hello
|
||||
hello YYYYY hello
|
||||
NULL YYYYY hello
|
||||
hello NULL hello
|
||||
hello XXXXX NULL
|
||||
hello YYYYY NULL
|
||||
NULL NULL hello
|
||||
hello NULL NULL
|
||||
NULL NULL NULL
|
||||
@ -47,11 +47,11 @@ insert into masking values ("hello", NULL, NULL);
|
||||
insert into masking values (NULL, NULL, NULL);
|
||||
select * from masking;
|
||||
x y a
|
||||
hello hello XXXXX
|
||||
NULL hello XXXXX
|
||||
hello NULL XXXXX
|
||||
hello hello YYYYY
|
||||
NULL hello YYYYY
|
||||
hello NULL YYYYY
|
||||
hello hello NULL
|
||||
NULL NULL XXXXX
|
||||
NULL NULL YYYYY
|
||||
hello NULL NULL
|
||||
NULL NULL NULL
|
||||
drop table masking;
|
||||
|
@ -5,15 +5,15 @@ create table masking (a TEXT, b TEXT, c TEXT);
|
||||
insert into masking values ("blah", "012345-ABC", "012345-ABC");
|
||||
select * from masking;
|
||||
a b c
|
||||
XXXX 012345-ABC XXXXXXXXXX
|
||||
YYYY XXXXXXXXXX YYYYYYYYYY
|
||||
delete from masking;
|
||||
insert into masking values ("blahblah", "221073-01AB", "012345-ABC");
|
||||
select * from masking;
|
||||
a b c
|
||||
XXXXXXXX 012345-ABCD XXXXXXXXXX
|
||||
YYYYYYYY 012345-ABCD YYYYYYYYYY
|
||||
delete from masking;
|
||||
insert into masking values ("221073-01AB", "221073-01AB", "221073-01AB");
|
||||
select * from masking;
|
||||
a b c
|
||||
XXXXXXXXXXX 012345-ABCD 012345-ABCD
|
||||
YYYYYYYYYYY 012345-ABCD 012345-ABCD
|
||||
delete from masking;
|
||||
|
@ -31,46 +31,46 @@ insert into masking_ENUM values ("aaa");
|
||||
insert into masking_SET values ("aaa");
|
||||
select * from masking_BINARY;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_VARBINARY;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_CHAR;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_VARCHAR;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_BLOB;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_TINYBLOB;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_MEDIUMBLOB;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_LONGBLOB;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_TEXT;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_TINYTEXT;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_MEDIUMTEXT;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_LONGTEXT;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_ENUM;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
select * from masking_SET;
|
||||
a
|
||||
XXX
|
||||
YYY
|
||||
create table masking_INT (a INT);
|
||||
create table masking_REAL (a REAL(3, 2));
|
||||
create table masking_DECIMAL (a DECIMAL(3, 2));
|
||||
|
@ -10,7 +10,7 @@
|
||||
# "column": "a"
|
||||
# },
|
||||
# "with": {
|
||||
# "fill": "X"
|
||||
# "fill": "Y"
|
||||
# }
|
||||
# },
|
||||
# {
|
||||
@ -27,7 +27,7 @@
|
||||
# },
|
||||
# "with": {
|
||||
# "value": "012345-ABCD",
|
||||
# "fill": "X"
|
||||
# "fill": "Y"
|
||||
# }
|
||||
# }
|
||||
# ]
|
||||
@ -46,38 +46,38 @@ use maskingdb;
|
||||
#
|
||||
create table masking (a TEXT, b TEXT, c TEXT);
|
||||
|
||||
# - a should be just "X...",
|
||||
# - b should be unchanged as the length does not match the string of "value", and
|
||||
# there is no catch all "fill".
|
||||
# - c should be just "X..." as the length does not match, so "value" is not applied
|
||||
# - a should be just "Y...",
|
||||
# - b should be changed into "X..." as the length does not match and there is no
|
||||
# specific fill value, so the default "X" is used.
|
||||
# - c should be just "Y..." as the length does not match, so "value" is not applied
|
||||
# and has "fill", which is applied.
|
||||
#
|
||||
#a b c
|
||||
#XXXX 012345-ABC XXXXXXXXXX
|
||||
#YYYY XXXXXXXXXX YYYYYYYYYY
|
||||
insert into masking values ("blah", "012345-ABC", "012345-ABC");
|
||||
select * from masking;
|
||||
delete from masking;
|
||||
|
||||
# - a should be just "X...",
|
||||
# - a should be just "Y...",
|
||||
# - b should be changed as the length matches the length of the string of "value"
|
||||
# - c should be just "X..." as the length does not match, so "value" is not applied
|
||||
# - c should be just "Y..." as the length does not match, so "value" is not applied
|
||||
# and has "fill", which is applied.
|
||||
#
|
||||
#a b c
|
||||
#XXXXXXXX 012345-ABCD XXXXXXXXXX
|
||||
#YYYYYYYY 012345-ABCD YYYYYYYYYY
|
||||
insert into masking values ("blahblah", "221073-01AB", "012345-ABC");
|
||||
select * from masking;
|
||||
delete from masking;
|
||||
|
||||
# - a should be just "X...",
|
||||
# - a should be just "Y...",
|
||||
# - b should be changed as the length matches the length of the string of "value"
|
||||
# - c should be chanched into a specific string as the length matches the string of
|
||||
# "value"
|
||||
#
|
||||
#a b c
|
||||
#a b c
|
||||
#XXXXXXXXXXX 012345-ABCD 012345-ABCD
|
||||
# a should still be just "X", b should be "012345-ABCD" and c should be "012345-ABCD"
|
||||
#YYYYYYYYYYY 012345-ABCD 012345-ABCD
|
||||
# a should still be just "Y", b should be "012345-ABCD" and c should be "012345-ABCD"
|
||||
insert into masking values ("221073-01AB", "221073-01AB", "221073-01AB");
|
||||
select * from masking;
|
||||
delete from masking;
|
||||
|
@ -42,7 +42,7 @@ void check_status(TestConnections *Test, const char *server, const char *status)
|
||||
void check_group(TestConnections *Test, const char *server, const char *group)
|
||||
{
|
||||
|
||||
char *output = Test->ssh_maxscale_output(true, "maxadmin show monitor \"MySQL Monitor\"");
|
||||
char *output = Test->ssh_maxscale_output(true, "maxadmin show monitor MySQL-Monitor");
|
||||
|
||||
if (output == NULL)
|
||||
{
|
||||
|
@ -34,10 +34,9 @@ int main(int argc, char *argv[])
|
||||
test->ssh_maxscale(true, "cp /etc/maxscale.cnf.backup /etc/maxscale.cnf");
|
||||
|
||||
/** Set router_options to a bad value */
|
||||
// Disabled for 2.0
|
||||
//test->ssh_maxscale(true, "sed -i -e 's/router_options.*/router_options=bad_option=true/' /etc/maxscale.cnf");
|
||||
//test->add_result(baseline == test->ssh_maxscale(true, "maxscale -c --user=maxscale"),
|
||||
// "Bad router_options should be detected.\n");
|
||||
test->ssh_maxscale(true, "sed -i -e 's/router_options.*/router_options=bad_option=true/' /etc/maxscale.cnf");
|
||||
test->add_result(baseline == test->ssh_maxscale(true, "maxscale -c --user=maxscale"),
|
||||
"Bad router_options should be detected.\n");
|
||||
|
||||
test->ssh_maxscale(true, "cp /etc/maxscale.cnf.backup /etc/maxscale.cnf");
|
||||
|
||||
|
@ -132,7 +132,7 @@ int main(int argc, char *argv[])
|
||||
Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
|
||||
Test->connect_rwsplit();
|
||||
|
||||
Test->execute_maxadmin_command((char *) "shutdown monitor \"MySQL Monitor\"");
|
||||
Test->execute_maxadmin_command((char *) "shutdown monitor MySQL-Monitor");
|
||||
|
||||
get_global_status_allnodes(&selects[0], &inserts[0], Test->repl, silent);
|
||||
|
||||
|
@ -14,49 +14,40 @@ router_options=max_sescmd_history=10
|
||||
* - execute one more session commad, excpect failure
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include "testconnections.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TestConnections * Test = new TestConnections(argc, argv);
|
||||
Test->set_timeout(200);
|
||||
int i;
|
||||
char sql[256];
|
||||
TestConnections test(argc, argv);
|
||||
int first_sleep = 5;
|
||||
int second_sleep = 12;
|
||||
|
||||
Test->tprintf("Open session and wait 20 seconds\n");
|
||||
Test->connect_maxscale();
|
||||
sleep(20);
|
||||
Test->tprintf("Execute query to check session\n");
|
||||
Test->try_query(Test->conn_rwsplit, "SELECT 1");
|
||||
test.set_timeout(200);
|
||||
|
||||
Test->tprintf("Wait 35 seconds more and try quiry again expecting failure\n");
|
||||
sleep(35);
|
||||
if (execute_query(Test->conn_rwsplit, "SELECT 1") == 0)
|
||||
test.tprintf("Open session, wait %d seconds and execute a query", first_sleep);
|
||||
test.connect_maxscale();
|
||||
sleep(first_sleep);
|
||||
test.try_query(test.conn_rwsplit, "SELECT 1");
|
||||
|
||||
test.tprintf("Wait %d seconds and execute query, expecting failure", second_sleep);
|
||||
sleep(second_sleep);
|
||||
test.add_result(execute_query(test.conn_rwsplit, "SELECT 1") == 0,
|
||||
"Session was not closed after %d seconds",
|
||||
second_sleep);
|
||||
test.close_maxscale_connections();
|
||||
|
||||
test.tprintf("Open session and execute 10 session commands");
|
||||
test.connect_maxscale();
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Test->add_result(1, "Session was not closed after 40 seconds\n");
|
||||
test.try_query(test.conn_rwsplit, "set @test=1");
|
||||
}
|
||||
Test->close_maxscale_connections();
|
||||
|
||||
Test->tprintf("Open session and execute 10 session commands\n");
|
||||
fflush(stdout);
|
||||
Test->connect_maxscale();
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
sprintf(sql, "set @test=%d", i);
|
||||
Test->try_query(Test->conn_rwsplit, sql);
|
||||
}
|
||||
Test->tprintf("done!\n");
|
||||
test.tprintf("Execute one more session command and expect message in error log");
|
||||
execute_query(test.conn_rwsplit, "set @test=1");
|
||||
sleep(1);
|
||||
test.check_log_err("Router session exceeded session command history limit", true);
|
||||
test.close_maxscale_connections();
|
||||
|
||||
Test->tprintf("Execute one more session command and expect message in error log\n");
|
||||
execute_query(Test->conn_rwsplit, "set @test=11");
|
||||
sleep(5);
|
||||
Test->check_log_err((char *) "Router session exceeded session command history limit", true);
|
||||
Test->close_maxscale_connections();
|
||||
|
||||
int rval = Test->global_result;
|
||||
delete Test;
|
||||
return rval;
|
||||
return test.global_result;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static char** sql = NULL;
|
||||
static size_t sql_size = 0;
|
||||
|
||||
int execute_select_query_and_check(MYSQL *conn, char *sql, unsigned long long int rows)
|
||||
int execute_select_query_and_check(MYSQL *conn, const char *sql, unsigned long long int rows)
|
||||
{
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
|
@ -11,7 +11,7 @@
|
||||
* @param rows Expected number of rows
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int execute_select_query_and_check(MYSQL *conn, char *sql, unsigned long long int rows);
|
||||
int execute_select_query_and_check(MYSQL *conn, const char *sql, unsigned long long int rows);
|
||||
|
||||
/**
|
||||
* @brief create_t1 Create t1 table, fileds: (x1 int, fl int)
|
||||
|
@ -1,16 +1,16 @@
|
||||
/**
|
||||
* @file temporal_tables.cpp Check temporal tables commands functionality (relates to bug 430)
|
||||
* Check temporary tables commands functionality (relates to bug 430)
|
||||
*
|
||||
* - create t1 table and put some data into it
|
||||
* - create tempral table t1
|
||||
* - create temporary table t1
|
||||
* - insert different data into t1
|
||||
* - check that SELECT FROM t1 gives data from tempral table
|
||||
* - create other connections using all Maxscale services and check that SELECT via these connections gives data from main t1, not temporal
|
||||
* - dropping tempral t1
|
||||
* - check that SELECT FROM t1 gives data from temporary table
|
||||
* - create other connections using all MaxScale services and check that SELECT
|
||||
* via these connections gives data from main t1, not temporary
|
||||
* - dropping temporary t1
|
||||
* - check that data from main t1 is not affected
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include "testconnections.h"
|
||||
#include "sql_t1.h"
|
||||
|
||||
@ -18,82 +18,45 @@ using namespace std;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TestConnections test(argc, argv);
|
||||
test.connect_maxscale();
|
||||
|
||||
TestConnections * Test = new TestConnections(argc, argv);
|
||||
test.tprintf("Create a table and insert two rows into it");
|
||||
test.set_timeout(30);
|
||||
|
||||
Test->repl->connect();
|
||||
execute_query(test.conn_rwsplit, "USE test");
|
||||
create_t1(test.conn_rwsplit);
|
||||
execute_query(test.conn_rwsplit, "INSERT INTO t1 (x1, fl) VALUES(0, 1)");
|
||||
execute_query(test.conn_rwsplit, "INSERT INTO t1 (x1, fl) VALUES(1, 1)");
|
||||
|
||||
MYSQL * conn;
|
||||
char sql[100];
|
||||
test.tprintf("Create temporary table and insert one row");
|
||||
test.set_timeout(30);
|
||||
|
||||
Test->set_timeout(40);
|
||||
conn = Test->open_rwsplit_connection();
|
||||
execute_query(test.conn_rwsplit, "create temporary table t1 as (SELECT * FROM t1 WHERE fl=3)");
|
||||
execute_query(test.conn_rwsplit, "INSERT INTO t1 (x1, fl) VALUES(0, 1)");
|
||||
|
||||
Test->tprintf("Cleaning up DB\n");
|
||||
execute_query(conn, (char *) "DROP DATABASE IF EXISTS test");
|
||||
execute_query(conn, (char *) "CREATE DATABASE test");
|
||||
execute_query(conn, (char *) "USE test");
|
||||
test.tprintf("Check that the temporary table has one row");
|
||||
test.set_timeout(90);
|
||||
|
||||
Test->tprintf("creating table t1\n");
|
||||
Test->set_timeout(40);
|
||||
create_t1(conn);
|
||||
test.add_result(execute_select_query_and_check(test.conn_rwsplit, "SELECT * FROM t1", 1),
|
||||
"Current connection should show one row");
|
||||
test.add_result(execute_select_query_and_check(test.conn_master, "SELECT * FROM t1", 2),
|
||||
"New connection should show two rows");
|
||||
test.add_result(execute_select_query_and_check(test.conn_slave, "SELECT * FROM t1", 2),
|
||||
"New connection should show two rows");
|
||||
|
||||
Test->tprintf("Inserting two rows into t1\n");
|
||||
Test->set_timeout(40);
|
||||
execute_query(conn, "INSERT INTO t1 (x1, fl) VALUES(0, 1);");
|
||||
execute_query(conn, "INSERT INTO t1 (x1, fl) VALUES(1, 1);");
|
||||
printf("Drop temporary table and check that the real table has two rows");
|
||||
test.set_timeout(90);
|
||||
|
||||
Test->tprintf("Creating temporal table t1\n");
|
||||
execute_query(conn, "create temporary table t1 as (SELECT * FROM t1 WHERE fl=3);");
|
||||
execute_query(test.conn_rwsplit, "DROP TABLE t1");
|
||||
test.add_result(execute_select_query_and_check(test.conn_rwsplit, "SELECT * FROM t1", 2),
|
||||
"check failed");
|
||||
test.add_result(execute_select_query_and_check(test.conn_master, "SELECT * FROM t1", 2),
|
||||
"check failed");
|
||||
test.add_result(execute_select_query_and_check(test.conn_slave, "SELECT * FROM t1", 2),
|
||||
"check failed");
|
||||
|
||||
Test->tprintf("Inserting one row into temporal table\n");
|
||||
execute_query(conn, "INSERT INTO t1 (x1, fl) VALUES(0, 1);");
|
||||
test.close_maxscale_connections();
|
||||
|
||||
Test->tprintf("Checking t1 temporal table\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(conn, (char *) "SELECT * FROM t1;", 1), "check failed\n");
|
||||
|
||||
|
||||
Test->tprintf("Connecting to all MaxScale routers and checking main t1 table (not temporal)\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(Test->connect_maxscale(), "Connectiong to Maxscale failed\n");
|
||||
Test->tprintf("Checking t1 table using RWSplit router\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_rwsplit, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->tprintf("Checking t1 table using ReadConn router in master mode\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_master, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->tprintf("Checking t1 table using ReadConn router in slave mode\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_slave, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->close_maxscale_connections();
|
||||
|
||||
|
||||
printf("Dropping temparal table and check main table again\n");
|
||||
execute_query(conn, "DROP TABLE t1;");
|
||||
|
||||
printf("Connecting to all MaxScale routers and checking main t1 table (not temporal)\n");
|
||||
Test->add_result(Test->connect_maxscale(), "Connectiong to Maxscale failed\n");
|
||||
Test->tprintf("Checking t1 table using RWSplit router\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_rwsplit, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->tprintf("Checking t1 table using ReadConn router in master mode\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_master, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->tprintf("Checking t1 table using ReadConn router in slave mode\n");
|
||||
Test->set_timeout(240);
|
||||
Test->add_result(execute_select_query_and_check(Test->conn_slave, (char *) "SELECT * FROM t1;", 2),
|
||||
"check failed\n");
|
||||
Test->close_maxscale_connections();
|
||||
|
||||
mysql_close(conn);
|
||||
|
||||
int rval = Test->global_result;
|
||||
delete Test;
|
||||
return rval;
|
||||
return test.global_result;
|
||||
}
|
||||
|
@ -1933,7 +1933,7 @@ int TestConnections::check_maxadmin_param(const char *command, const char *param
|
||||
return rval;
|
||||
}
|
||||
|
||||
int TestConnections::get_maxadmin_param(char *command, char *param, char *result)
|
||||
int TestConnections::get_maxadmin_param(const char *command, const char *param, char *result)
|
||||
{
|
||||
char * buf;
|
||||
|
||||
|
@ -647,7 +647,7 @@ public:
|
||||
int execute_maxadmin_command(char * cmd);
|
||||
int execute_maxadmin_command_print(char * cmd);
|
||||
int check_maxadmin_param(const char *command, const char *param, const char *value);
|
||||
int get_maxadmin_param(char *command, char *param, char *result);
|
||||
int get_maxadmin_param(const char *command, const char *param, char *result);
|
||||
void check_current_operations(int value);
|
||||
void check_current_connections(int value);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
add_subdirectory(qc_mysqlembedded)
|
||||
add_subdirectory(qc_sqlite)
|
||||
add_subdirectory(qc_dummy)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
|
@ -1,4 +0,0 @@
|
||||
add_library(qc_dummy SHARED qc_dummy.cc)
|
||||
set_target_properties(qc_dummy PROPERTIES VERSION "1.0.0")
|
||||
set_target_properties(qc_dummy PROPERTIES LINK_FLAGS -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/qc_dummy.map)
|
||||
install_module(qc_dummy core)
|
@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "qc_sqlite"
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
#include "../../server/core/maxscale/config.h"
|
||||
|
||||
int32_t qc_dummy_parse(GWBUF* querybuf, uint32_t collect, int32_t* pResult)
|
||||
{
|
||||
*pResult = QC_QUERY_INVALID;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_type_mask(GWBUF* querybuf, uint32_t* pType_mask)
|
||||
{
|
||||
*pType_mask = QUERY_TYPE_UNKNOWN;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_table_names(GWBUF* querybuf, int32_t fullnames, char*** ppzNames, int32_t* pSize)
|
||||
{
|
||||
*ppzNames = NULL;
|
||||
*pSize = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_created_table_name(GWBUF* querybuf, char** pzName)
|
||||
{
|
||||
*pzName = NULL;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_is_drop_table_query(GWBUF* querybuf, int32_t* pIs_drop_table)
|
||||
{
|
||||
*pIs_drop_table = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_query_has_clause(GWBUF* buf, int32_t *pHas_clause)
|
||||
{
|
||||
*pHas_clause = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_database_names(GWBUF* querybuf, char*** ppzNames, int32_t* pSize)
|
||||
{
|
||||
*ppzNames = NULL;
|
||||
*pSize = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_operation(GWBUF* querybuf, int32_t* pOp)
|
||||
{
|
||||
*pOp = QUERY_OP_UNDEFINED;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_prepare_name(GWBUF* query, char** pzName)
|
||||
{
|
||||
*pzName = NULL;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_field_info(GWBUF* query, const QC_FIELD_INFO** ppInfos, uint32_t* nInfos)
|
||||
{
|
||||
*ppInfos = NULL;
|
||||
*nInfos = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** ppInfos, uint32_t* nInfos)
|
||||
{
|
||||
*ppInfos = NULL;
|
||||
*nInfos = 0;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_setup(const char* args)
|
||||
{
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t qc_dummy_process_init(void)
|
||||
{
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
void qc_dummy_process_end(void)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t qc_dummy_thread_init(void)
|
||||
{
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
void qc_dummy_thread_end(void)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t qc_dummy_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt)
|
||||
{
|
||||
*preparable_stmt = NULL;
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static QUERY_CLASSIFIER qc =
|
||||
{
|
||||
qc_dummy_setup,
|
||||
qc_dummy_process_init,
|
||||
qc_dummy_process_end,
|
||||
qc_dummy_thread_init,
|
||||
qc_dummy_thread_end,
|
||||
qc_dummy_parse,
|
||||
qc_dummy_get_type_mask,
|
||||
qc_dummy_get_operation,
|
||||
qc_dummy_get_created_table_name,
|
||||
qc_dummy_is_drop_table_query,
|
||||
qc_dummy_get_table_names,
|
||||
NULL,
|
||||
qc_dummy_query_has_clause,
|
||||
qc_dummy_get_database_names,
|
||||
qc_dummy_get_prepare_name,
|
||||
qc_dummy_get_field_info,
|
||||
qc_dummy_get_function_info,
|
||||
qc_dummy_get_preparable_stmt,
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_QUERY_CLASSIFIER,
|
||||
MXS_MODULE_IN_DEVELOPMENT,
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
"Dummy Query Classifier",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&qc,
|
||||
qc_dummy_process_init,
|
||||
qc_dummy_process_end,
|
||||
qc_dummy_thread_init,
|
||||
qc_dummy_thread_end,
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
global:
|
||||
mxs_get_module_object;
|
||||
local:
|
||||
*;
|
||||
};
|
@ -94,6 +94,14 @@ typedef struct parsing_info_st
|
||||
#endif
|
||||
} parsing_info_t;
|
||||
|
||||
static thread_local struct
|
||||
{
|
||||
// The version information is not used; the embedded library parses according
|
||||
// to the version of the embedded library it has been linked with. However, we
|
||||
// need to store the information so that qc_[get|set]_server_version will work.
|
||||
uint64_t version;
|
||||
} this_thread;
|
||||
|
||||
#define QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (t<QUERY_TYPE_WRITE ? true : false)
|
||||
|
||||
static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str);
|
||||
@ -1737,6 +1745,10 @@ int32_t qc_mysql_get_operation(GWBUF* querybuf, int32_t* operation)
|
||||
*operation = QUERY_OP_REVOKE;
|
||||
break;
|
||||
|
||||
case SQLCOM_EXECUTE:
|
||||
*operation = QUERY_OP_EXECUTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
*operation = QUERY_OP_UNDEFINED;
|
||||
}
|
||||
@ -2590,6 +2602,16 @@ int32_t qc_mysql_get_function_info(GWBUF* buf,
|
||||
return QC_RESULT_OK;
|
||||
}
|
||||
|
||||
void qc_mysql_set_server_version(uint64_t version)
|
||||
{
|
||||
this_thread.version = version;
|
||||
}
|
||||
|
||||
void qc_mysql_get_server_version(uint64_t* version)
|
||||
{
|
||||
*version = this_thread.version;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -2743,6 +2765,8 @@ extern "C"
|
||||
qc_mysql_get_field_info,
|
||||
qc_mysql_get_function_info,
|
||||
qc_mysql_get_preparable_stmt,
|
||||
qc_mysql_set_server_version,
|
||||
qc_mysql_get_server_version,
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
|
@ -1,5 +1,14 @@
|
||||
/*
|
||||
* @licence@
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "builtin_functions.h"
|
||||
@ -368,6 +377,42 @@ static const char* BUILTIN_FUNCTIONS[] =
|
||||
|
||||
const size_t N_BUILTIN_FUNCTIONS = sizeof(BUILTIN_FUNCTIONS) / sizeof(BUILTIN_FUNCTIONS[0]);
|
||||
|
||||
// The functions have been taken from:
|
||||
// https://mariadb.com/kb/en/mariadb/json-functions
|
||||
|
||||
static const char* BUILTIN_10_2_3_FUNCTIONS[] =
|
||||
{
|
||||
"json_array",
|
||||
"json_array_append",
|
||||
"json_array_insert",
|
||||
"json_compact",
|
||||
"json_contains",
|
||||
"json_contains_path",
|
||||
"json_depth",
|
||||
"json_detailed",
|
||||
"json_exists",
|
||||
"json_extract",
|
||||
"json_insert",
|
||||
"json_keys",
|
||||
"json_length",
|
||||
"json_loose",
|
||||
"json_merge",
|
||||
"json_object",
|
||||
"json_query",
|
||||
"json_quote",
|
||||
"json_remove"
|
||||
"json_replace",
|
||||
"json_search",
|
||||
"json_set",
|
||||
"json_type",
|
||||
"json_unquote",
|
||||
"json_valid",
|
||||
"json_value"
|
||||
};
|
||||
|
||||
const size_t N_BUILTIN_10_2_3_FUNCTIONS =
|
||||
sizeof(BUILTIN_10_2_3_FUNCTIONS) / sizeof(BUILTIN_10_2_3_FUNCTIONS[0]);
|
||||
|
||||
// NOTE: sort_compare and search_compare are not identical, so don't
|
||||
// NOTE: optimize either of them away.
|
||||
static int sort_compare(const void* key, const void* value)
|
||||
@ -389,6 +434,7 @@ void init_builtin_functions()
|
||||
ss_dassert(!unit.inited);
|
||||
|
||||
qsort(BUILTIN_FUNCTIONS, N_BUILTIN_FUNCTIONS, sizeof(char*), sort_compare);
|
||||
qsort(BUILTIN_10_2_3_FUNCTIONS, N_BUILTIN_10_2_3_FUNCTIONS, sizeof(char*), sort_compare);
|
||||
|
||||
unit.inited = true;
|
||||
}
|
||||
@ -399,11 +445,22 @@ void finish_builtin_functions()
|
||||
unit.inited = false;
|
||||
}
|
||||
|
||||
bool is_builtin_readonly_function(const char* key)
|
||||
bool is_builtin_readonly_function(const char* key, uint32_t major, uint32_t minor, uint32_t patch)
|
||||
{
|
||||
ss_dassert(unit.inited);
|
||||
|
||||
char* value = bsearch(key, BUILTIN_FUNCTIONS, N_BUILTIN_FUNCTIONS, sizeof(char*), search_compare);
|
||||
|
||||
if (!value)
|
||||
{
|
||||
if ((major > 10) ||
|
||||
((major == 10) && (minor > 2)) ||
|
||||
((major == 10) && (minor == 2) && (patch >= 3)))
|
||||
{
|
||||
value = bsearch(key, BUILTIN_10_2_3_FUNCTIONS, N_BUILTIN_10_2_3_FUNCTIONS,
|
||||
sizeof(char*), search_compare);
|
||||
}
|
||||
}
|
||||
|
||||
return value ? true : false;
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
#ifndef BUILTIN_FUNCTIONS_H
|
||||
#define BUILTIN_FUNCTIONS_H
|
||||
/**
|
||||
* @LICENCE@
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -14,10 +22,8 @@ extern "C" {
|
||||
void init_builtin_functions();
|
||||
void finish_builtin_functions();
|
||||
|
||||
bool is_builtin_readonly_function(const char* zToken);
|
||||
bool is_builtin_readonly_function(const char* zToken, uint32_t major, uint32_t minor, uint32_t patch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -114,6 +114,9 @@ static thread_local struct
|
||||
bool initialized;
|
||||
sqlite3* db; // Thread specific database handle.
|
||||
QC_SQLITE_INFO* info;
|
||||
uint32_t version_major;
|
||||
uint32_t version_minor;
|
||||
uint32_t version_patch;
|
||||
} this_thread;
|
||||
|
||||
/**
|
||||
@ -1102,7 +1105,10 @@ static void update_field_infos(QC_SQLITE_INFO* info,
|
||||
{
|
||||
info->type_mask |= (QUERY_TYPE_READ | QUERY_TYPE_MASTER_READ);
|
||||
}
|
||||
else if (!is_builtin_readonly_function(zToken))
|
||||
else if (!is_builtin_readonly_function(zToken,
|
||||
this_thread.version_major,
|
||||
this_thread.version_minor,
|
||||
this_thread.version_patch))
|
||||
{
|
||||
info->type_mask |= QUERY_TYPE_WRITE;
|
||||
}
|
||||
@ -2026,6 +2032,7 @@ void maxscaleExecute(Parse* pParse, Token* pName)
|
||||
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->type_mask = QUERY_TYPE_WRITE;
|
||||
info->operation = QUERY_OP_EXECUTE;
|
||||
|
||||
// If information is collected in several passes, then we may
|
||||
// this information already.
|
||||
@ -2907,6 +2914,8 @@ static int32_t qc_sqlite_get_canonical(GWBUF* query, char** canonical);
|
||||
static int32_t qc_sqlite_query_has_clause(GWBUF* query, int32_t* has_clause);
|
||||
static int32_t qc_sqlite_get_database_names(GWBUF* query, char*** names, int* sizep);
|
||||
static int32_t qc_sqlite_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt);
|
||||
static void qc_sqlite_set_server_version(uint64_t version);
|
||||
static void qc_sqlite_get_server_version(uint64_t* version);
|
||||
|
||||
static bool get_key_and_value(char* arg, const char** pkey, const char** pvalue)
|
||||
{
|
||||
@ -3080,6 +3089,9 @@ static int32_t qc_sqlite_thread_init(void)
|
||||
this_thread.info = NULL;
|
||||
|
||||
this_thread.initialized = true;
|
||||
this_thread.version_major = 0;
|
||||
this_thread.version_minor = 0;
|
||||
this_thread.version_patch = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3522,6 +3534,26 @@ int32_t qc_sqlite_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void qc_sqlite_set_server_version(uint64_t version)
|
||||
{
|
||||
QC_TRACE();
|
||||
|
||||
uint32_t major = version / 10000;
|
||||
uint32_t minor = (version - major * 10000) / 100;
|
||||
uint32_t patch = version - major * 10000 - minor * 100;
|
||||
|
||||
this_thread.version_major = major;
|
||||
this_thread.version_minor = minor;
|
||||
this_thread.version_patch = patch;
|
||||
}
|
||||
|
||||
static void qc_sqlite_get_server_version(uint64_t* version)
|
||||
{
|
||||
QC_TRACE();
|
||||
|
||||
*version = this_thread.version_major * 10000 + this_thread.version_minor * 100 + this_thread.version_patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPORTS
|
||||
*/
|
||||
@ -3548,6 +3580,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
qc_sqlite_get_field_info,
|
||||
qc_sqlite_get_function_info,
|
||||
qc_sqlite_get_preparable_stmt,
|
||||
qc_sqlite_set_server_version,
|
||||
qc_sqlite_get_server_version,
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
|
@ -22,6 +22,9 @@ if (BUILD_QC_MYSQLEMBEDDED)
|
||||
add_executable(compare compare.cc testreader.cc)
|
||||
target_link_libraries(compare maxscale-common)
|
||||
|
||||
add_executable(version_sensitivity version_sensitivity.cc)
|
||||
target_link_libraries(version_sensitivity maxscale-common)
|
||||
|
||||
add_executable(crash_qc_sqlite crash_qc_sqlite.c)
|
||||
target_link_libraries(crash_qc_sqlite maxscale-common)
|
||||
|
||||
@ -39,6 +42,8 @@ if (BUILD_QC_MYSQLEMBEDDED)
|
||||
add_test(TestQC_CompareUpdate compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/update.test)
|
||||
add_test(TestQC_CompareMaxScale compare -v 2 ${CMAKE_CURRENT_SOURCE_DIR}/maxscale.test)
|
||||
add_test(TestQC_CompareWhiteSpace compare -v 2 -S -s "select user from mysql.user; ")
|
||||
|
||||
add_test(TestQC_version_sensitivity version_sensitivity)
|
||||
endif()
|
||||
|
||||
add_subdirectory(canonical_tests)
|
||||
|
161
query_classifier/test/version_sensitivity.cc
Normal file
161
query_classifier/test/version_sensitivity.cc
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#define MYSQL_COM_QUIT COM_QUIT
|
||||
#define MYSQL_COM_INIT_DB COM_INIT_DB
|
||||
#define MYSQL_COM_CHANGE_USER COM_CHANGE_USER
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/paths.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
GWBUF* create_gwbuf(const string& s)
|
||||
{
|
||||
size_t len = s.length();
|
||||
size_t payload_len = len + 1;
|
||||
size_t gwbuf_len = MYSQL_HEADER_LEN + payload_len;
|
||||
|
||||
GWBUF* gwbuf = gwbuf_alloc(gwbuf_len);
|
||||
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf))) = payload_len;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 1)) = (payload_len >> 8);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 2)) = (payload_len >> 16);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 3)) = 0x00;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 4)) = 0x03;
|
||||
memcpy((char*)GWBUF_DATA(gwbuf) + 5, s.c_str(), len);
|
||||
|
||||
return gwbuf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool test(const string& s, uint32_t expected)
|
||||
{
|
||||
GWBUF* pBuf = create_gwbuf(s);
|
||||
|
||||
uint32_t type_mask = qc_get_type_mask(pBuf);
|
||||
|
||||
gwbuf_free(pBuf);
|
||||
|
||||
bool success = (type_mask == expected);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
cout << "error: " << s << " classified wrong." << endl;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int test()
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
string valid_json("SELECT Json_Array(56, 3.1416, 'My name is \"Foo\"', NULL)");
|
||||
string invalid_json("SELECT Json_Foo(56, 3.1416, 'My name is \"Foo\"', NULL)");
|
||||
|
||||
SERVER_VERSION sv;
|
||||
|
||||
// pre-Json
|
||||
sv.major = 10;
|
||||
sv.minor = 0;
|
||||
sv.patch = 0;
|
||||
|
||||
cout << "Testing pre-Json server." << endl;
|
||||
|
||||
qc_set_server_version(server_encode_version(&sv));
|
||||
|
||||
if (!test(valid_json, QUERY_TYPE_READ | QUERY_TYPE_WRITE))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!test(invalid_json, QUERY_TYPE_READ | QUERY_TYPE_WRITE))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cout << "Testing post-Json server." << endl;
|
||||
|
||||
// post-Json
|
||||
sv.major = 10;
|
||||
sv.minor = 2;
|
||||
sv.patch = 3;
|
||||
|
||||
qc_set_server_version(server_encode_version(&sv));
|
||||
|
||||
if (!test(valid_json, QUERY_TYPE_READ))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!test(invalid_json, QUERY_TYPE_READ | QUERY_TYPE_WRITE))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int rc = EXIT_FAILURE;
|
||||
|
||||
set_datadir(strdup("/tmp"));
|
||||
set_langdir(strdup("."));
|
||||
set_process_datadir(strdup("/tmp"));
|
||||
|
||||
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
|
||||
{
|
||||
const char QC_LIB[] = "qc_sqlite";
|
||||
const char LIBDIR[] = "../qc_sqlite";
|
||||
|
||||
set_libdir(strdup(LIBDIR));
|
||||
|
||||
if (qc_setup(QC_LIB, NULL))
|
||||
{
|
||||
if (qc_process_init(QC_INIT_BOTH))
|
||||
{
|
||||
rc = test();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "error: Could not perform process initialization for " << QC_LIB << "." << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "error: Could not setup " << QC_LIB << "." << endl;
|
||||
}
|
||||
|
||||
mxs_log_finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "error: Could not initialize log." << endl;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
@ -4,6 +4,7 @@ add_library(maxscale-common SHARED
|
||||
alloc.cc
|
||||
atomic.cc
|
||||
authenticator.cc
|
||||
backend.cc
|
||||
buffer.cc
|
||||
config.cc
|
||||
config_runtime.cc
|
||||
@ -41,6 +42,7 @@ add_library(maxscale-common SHARED
|
||||
server.cc
|
||||
service.cc
|
||||
session.cc
|
||||
session_command.cc
|
||||
skygw_utils.cc
|
||||
spinlock.cc
|
||||
ssl.cc
|
||||
|
@ -53,7 +53,7 @@ uint64_t atomic_add_uint64(uint64_t *variable, int64_t value)
|
||||
#endif
|
||||
}
|
||||
|
||||
int atomic_load_int32(int *variable)
|
||||
int atomic_load_int32(const int *variable)
|
||||
{
|
||||
#ifdef MXS_USE_ATOMIC_BUILTINS
|
||||
return __atomic_load_n(variable, __ATOMIC_SEQ_CST);
|
||||
@ -62,7 +62,7 @@ int atomic_load_int32(int *variable)
|
||||
#endif
|
||||
}
|
||||
|
||||
int64_t atomic_load_int64(int64_t *variable)
|
||||
int64_t atomic_load_int64(const int64_t *variable)
|
||||
{
|
||||
#ifdef MXS_USE_ATOMIC_BUILTINS
|
||||
return __atomic_load_n(variable, __ATOMIC_SEQ_CST);
|
||||
@ -71,7 +71,7 @@ int64_t atomic_load_int64(int64_t *variable)
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t atomic_load_uint64(uint64_t *variable)
|
||||
uint64_t atomic_load_uint64(const uint64_t *variable)
|
||||
{
|
||||
#ifdef MXS_USE_ATOMIC_BUILTINS
|
||||
return __atomic_load_n(variable, __ATOMIC_SEQ_CST);
|
||||
@ -80,7 +80,7 @@ uint64_t atomic_load_uint64(uint64_t *variable)
|
||||
#endif
|
||||
}
|
||||
|
||||
void* atomic_load_ptr(void **variable)
|
||||
void* atomic_load_ptr(void * const *variable)
|
||||
{
|
||||
#ifdef MXS_USE_ATOMIC_BUILTINS
|
||||
return __atomic_load_n(variable, __ATOMIC_SEQ_CST);
|
||||
|
316
server/core/backend.cc
Normal file
316
server/core/backend.cc
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/backend.hh>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <maxscale/debug.h>
|
||||
|
||||
using namespace maxscale;
|
||||
|
||||
Backend::Backend(SERVER_REF *ref):
|
||||
m_closed(false),
|
||||
m_backend(ref),
|
||||
m_dcb(NULL),
|
||||
m_state(0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[" << server()->name << "]:" << server()->port;
|
||||
m_uri = ss.str();
|
||||
}
|
||||
|
||||
Backend::~Backend()
|
||||
{
|
||||
ss_dassert(m_closed || !in_use());
|
||||
|
||||
if (in_use())
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
void Backend::close(close_type type)
|
||||
{
|
||||
if (!m_closed)
|
||||
{
|
||||
m_closed = true;
|
||||
|
||||
if (in_use())
|
||||
{
|
||||
CHK_DCB(m_dcb);
|
||||
|
||||
/** Clean operation counter in bref and in SERVER */
|
||||
if (is_waiting_result())
|
||||
{
|
||||
clear_state(WAITING_RESULT);
|
||||
}
|
||||
clear_state(IN_USE);
|
||||
|
||||
if (type == CLOSE_FATAL)
|
||||
{
|
||||
set_state(FATAL_FAILURE);
|
||||
}
|
||||
|
||||
dcb_close(m_dcb);
|
||||
|
||||
/** decrease server current connection counters */
|
||||
atomic_add(&m_backend->connections, -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Backend::execute_session_command()
|
||||
{
|
||||
if (is_closed() || !session_command_count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CHK_DCB(m_dcb);
|
||||
|
||||
SessionCommandList::iterator iter = m_session_commands.begin();
|
||||
SessionCommand& sescmd = *(*iter);
|
||||
GWBUF *buffer = sescmd.copy_buffer().release();
|
||||
bool rval = false;
|
||||
|
||||
switch (sescmd.get_command())
|
||||
{
|
||||
case MYSQL_COM_QUIT:
|
||||
case MYSQL_COM_STMT_CLOSE:
|
||||
rval = write(buffer, NO_RESPONSE);
|
||||
break;
|
||||
|
||||
case MYSQL_COM_CHANGE_USER:
|
||||
/** This makes it possible to handle replies correctly */
|
||||
gwbuf_set_type(buffer, GWBUF_TYPE_SESCMD);
|
||||
rval = auth(buffer);
|
||||
break;
|
||||
|
||||
case MYSQL_COM_QUERY:
|
||||
default:
|
||||
/**
|
||||
* Mark session command buffer, it triggers writing
|
||||
* MySQL command to protocol
|
||||
*/
|
||||
gwbuf_set_type(buffer, GWBUF_TYPE_SESCMD);
|
||||
rval = write(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Backend::append_session_command(GWBUF* buffer, uint64_t sequence)
|
||||
{
|
||||
m_session_commands.push_back(SSessionCommand(new SessionCommand(buffer, sequence)));
|
||||
}
|
||||
|
||||
void Backend::append_session_command(const SSessionCommand& sescmd)
|
||||
{
|
||||
m_session_commands.push_back(sescmd);
|
||||
}
|
||||
|
||||
void Backend::append_session_command(const SessionCommandList& sescmdlist)
|
||||
{
|
||||
m_session_commands.insert(m_session_commands.end(), sescmdlist.begin(), sescmdlist.end());
|
||||
}
|
||||
|
||||
uint64_t Backend::complete_session_command()
|
||||
{
|
||||
uint64_t rval = m_session_commands.front()->get_position();
|
||||
m_session_commands.pop_front();
|
||||
return rval;
|
||||
}
|
||||
|
||||
size_t Backend::session_command_count() const
|
||||
{
|
||||
return m_session_commands.size();
|
||||
}
|
||||
|
||||
const SSessionCommand& Backend::next_session_command() const
|
||||
{
|
||||
ss_dassert(session_command_count() > 0);
|
||||
return m_session_commands.front();
|
||||
}
|
||||
|
||||
void Backend::clear_state(backend_state state)
|
||||
{
|
||||
if ((state & WAITING_RESULT) && (m_state & WAITING_RESULT))
|
||||
{
|
||||
ss_debug(int prev2 = )atomic_add(&m_backend->server->stats.n_current_ops, -1);
|
||||
ss_dassert(prev2 > 0);
|
||||
}
|
||||
|
||||
m_state &= ~state;
|
||||
}
|
||||
|
||||
void Backend::set_state(backend_state state)
|
||||
{
|
||||
if ((state & WAITING_RESULT) && (m_state & WAITING_RESULT) == 0)
|
||||
{
|
||||
ss_debug(int prev2 = )atomic_add(&m_backend->server->stats.n_current_ops, 1);
|
||||
ss_dassert(prev2 >= 0);
|
||||
}
|
||||
|
||||
m_state |= state;
|
||||
}
|
||||
|
||||
SERVER_REF* Backend::backend() const
|
||||
{
|
||||
ss_dassert(m_backend);
|
||||
return m_backend;
|
||||
}
|
||||
|
||||
SERVER* Backend::server() const
|
||||
{
|
||||
ss_dassert(m_backend);
|
||||
return m_backend->server;
|
||||
}
|
||||
|
||||
bool Backend::can_connect() const
|
||||
{
|
||||
return !has_failed() && SERVER_IS_RUNNING(m_backend->server);
|
||||
}
|
||||
|
||||
bool Backend::connect(MXS_SESSION* session)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if ((m_dcb = dcb_connect(m_backend->server, session, m_backend->server->protocol)))
|
||||
{
|
||||
m_closed = false;
|
||||
m_state = IN_USE;
|
||||
atomic_add(&m_backend->connections, 1);
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = FATAL_FAILURE;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
DCB* Backend::dcb() const
|
||||
{
|
||||
return m_dcb;
|
||||
}
|
||||
|
||||
bool Backend::write(GWBUF* buffer, response_type type)
|
||||
{
|
||||
bool rval = m_dcb->func.write(m_dcb, buffer) != 0;
|
||||
|
||||
if (rval && type == EXPECT_RESPONSE)
|
||||
{
|
||||
set_state(WAITING_RESULT);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool Backend::auth(GWBUF* buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (m_dcb->func.auth(m_dcb, NULL, m_dcb->session, buffer) == 1)
|
||||
{
|
||||
set_state(WAITING_RESULT);
|
||||
rval = true;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Backend::ack_write()
|
||||
{
|
||||
ss_dassert(is_waiting_result());
|
||||
clear_state(WAITING_RESULT);
|
||||
}
|
||||
|
||||
void Backend::store_command(GWBUF* buffer)
|
||||
{
|
||||
m_pending_cmd.reset(buffer);
|
||||
}
|
||||
|
||||
bool Backend::write_stored_command()
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (m_pending_cmd.length())
|
||||
{
|
||||
rval = write(m_pending_cmd.release());
|
||||
|
||||
if (!rval)
|
||||
{
|
||||
MXS_ERROR("Routing of pending query failed.");
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool Backend::in_use() const
|
||||
{
|
||||
return m_state & IN_USE;
|
||||
}
|
||||
|
||||
bool Backend::is_active() const
|
||||
{
|
||||
return SERVER_REF_IS_ACTIVE(m_backend);
|
||||
}
|
||||
|
||||
bool Backend::is_waiting_result() const
|
||||
{
|
||||
return m_state & WAITING_RESULT;
|
||||
}
|
||||
|
||||
bool Backend::is_closed() const
|
||||
{
|
||||
return m_closed;
|
||||
}
|
||||
|
||||
bool Backend::is_master() const
|
||||
{
|
||||
return SERVER_IS_MASTER(m_backend->server);
|
||||
}
|
||||
|
||||
bool Backend::is_slave() const
|
||||
{
|
||||
return SERVER_IS_SLAVE(m_backend->server);
|
||||
}
|
||||
|
||||
bool Backend::is_relay() const
|
||||
{
|
||||
return SERVER_IS_RELAY_SERVER(m_backend->server);
|
||||
}
|
||||
|
||||
bool Backend::has_failed() const
|
||||
{
|
||||
return m_state & FATAL_FAILURE;
|
||||
}
|
||||
|
||||
const char* Backend::name() const
|
||||
{
|
||||
return m_backend->server->unique_name;
|
||||
}
|
||||
|
||||
const char* Backend::uri() const
|
||||
{
|
||||
return m_uri.c_str();
|
||||
}
|
@ -1244,9 +1244,50 @@ pcre2_code* config_get_compiled_regex(const MXS_CONFIG_PARAMETER *params,
|
||||
uint32_t* output_ovec_size)
|
||||
{
|
||||
const char* regex_string = config_get_string(params, key);
|
||||
uint32_t jit_available = 0;
|
||||
pcre2_config(PCRE2_CONFIG_JIT, &jit_available);
|
||||
return compile_regex_string(regex_string, jit_available, options, output_ovec_size);
|
||||
pcre2_code* code = NULL;
|
||||
|
||||
if (*regex_string)
|
||||
{
|
||||
uint32_t jit_available = 0;
|
||||
pcre2_config(PCRE2_CONFIG_JIT, &jit_available);
|
||||
code = compile_regex_string(regex_string, jit_available, options, output_ovec_size);
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
bool config_get_compiled_regexes(const MXS_CONFIG_PARAMETER *params,
|
||||
const char* keys[], int keys_size,
|
||||
uint32_t options, uint32_t* out_ovec_size,
|
||||
pcre2_code** out_codes[])
|
||||
{
|
||||
bool rval = true;
|
||||
uint32_t max_ovec_size = 0;
|
||||
uint32_t ovec_size_temp = 0;
|
||||
for (int i = 0; i < keys_size; i++)
|
||||
{
|
||||
ss_dassert(out_codes[i]);
|
||||
*out_codes[i] = config_get_compiled_regex(params, keys[i], options,
|
||||
&ovec_size_temp);
|
||||
if (*out_codes[i])
|
||||
{
|
||||
if (ovec_size_temp > max_ovec_size)
|
||||
{
|
||||
max_ovec_size = ovec_size_temp;
|
||||
}
|
||||
}
|
||||
/* config_get_compiled_regex() returns null also if the config setting
|
||||
* didn't exist. Check that before setting error state. */
|
||||
else if (*(config_get_value_string(params, keys[i])))
|
||||
{
|
||||
rval = false;
|
||||
}
|
||||
}
|
||||
if (out_ovec_size)
|
||||
{
|
||||
*out_ovec_size = max_ovec_size;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
MXS_CONFIG_PARAMETER* config_clone_param(const MXS_CONFIG_PARAMETER* param)
|
||||
|
@ -3401,7 +3401,7 @@ int poll_remove_dcb(DCB *dcb)
|
||||
* Only positive fds can be removed from epoll set.
|
||||
*/
|
||||
dcbfd = dcb->fd;
|
||||
ss_dassert(dcbfd > 0);
|
||||
ss_dassert(dcbfd > 0 || dcb->dcb_role == DCB_ROLE_INTERNAL);
|
||||
|
||||
if (dcbfd > 0)
|
||||
{
|
||||
|
@ -125,12 +125,15 @@ static struct option long_options[] =
|
||||
{"version-full", no_argument, 0, 'V'},
|
||||
{"help", no_argument, 0, '?'},
|
||||
{"connector_plugindir", required_argument, 0, 'H'},
|
||||
{"debug", required_argument, 0, 'g'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static bool syslog_configured = false;
|
||||
static bool maxlog_configured = false;
|
||||
static bool log_to_shm_configured = false;
|
||||
static volatile sig_atomic_t last_signal = 0;
|
||||
static bool unload_modules_at_exit = true;
|
||||
|
||||
static int cnf_preparser(void* data, const char* section, const char* name, const char* value);
|
||||
static void log_flush_shutdown(void);
|
||||
@ -145,6 +148,7 @@ static int ntfw_cb(const char*, const struct stat*, int, struct FTW*);
|
||||
static bool file_is_readable(const char* absolute_pathname);
|
||||
static bool file_is_writable(const char* absolute_pathname);
|
||||
bool handle_path_arg(char** dest, const char* path, const char* arg, bool rd, bool wr);
|
||||
static bool handle_debug_args(char* args);
|
||||
static void set_log_augmentation(const char* value);
|
||||
static void usage(void);
|
||||
static char* get_expanded_pathname(
|
||||
@ -172,6 +176,32 @@ static bool daemonize();
|
||||
static bool sniff_configuration(const char* filepath);
|
||||
static bool modules_process_init();
|
||||
static void modules_process_finish();
|
||||
static void disable_module_unloading();
|
||||
static void enable_module_unloading();
|
||||
|
||||
struct DEBUG_ARGUMENT
|
||||
{
|
||||
const char* name; /**< The name of the debug argument */
|
||||
void (*action)(); /**< The function implementing the argument */
|
||||
const char* description; /**< Help text */
|
||||
};
|
||||
|
||||
#define SPACER " "
|
||||
|
||||
const DEBUG_ARGUMENT debug_arguments[] =
|
||||
{
|
||||
{
|
||||
"disable-module-unloading", disable_module_unloading,
|
||||
"disable module unloading at exit. Will produce better\n"
|
||||
SPACER "Valgring leak reports if leaked memory was allocated in\n"
|
||||
SPACER "a shared library"
|
||||
},
|
||||
{
|
||||
"enable-module-unloading", enable_module_unloading,
|
||||
"cancels disable-module-unloading"
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/** SSL multi-threading functions and structures */
|
||||
|
||||
@ -916,6 +946,14 @@ static void usage(void)
|
||||
" -S, --maxlog=[yes|no] log messages to MaxScale log (default: yes)\n"
|
||||
" -G, --log_augmentation=0|1 augment messages with the name of the function\n"
|
||||
" where the message was logged (default: 0)\n"
|
||||
" -g, --debug=arg1,arg2,... enable or disable debug features. Supported arguments:\n",
|
||||
progname);
|
||||
for (int i = 0; debug_arguments[i].action != NULL; i++)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" %-24s %s\n", debug_arguments[i].name, debug_arguments[i].description);
|
||||
}
|
||||
fprintf(stderr,
|
||||
" -v, --version print version info and exit\n"
|
||||
" -V, --version-full print full version info and exit\n"
|
||||
" -?, --help show this help\n"
|
||||
@ -940,7 +978,6 @@ static void usage(void)
|
||||
"dir will be '/path/maxscale/var/log/maxscale', the config dir will be\n"
|
||||
"'/path/maxscale/etc' and the default config file will be\n"
|
||||
"'/path/maxscale/etc/maxscale.cnf'.\n",
|
||||
progname,
|
||||
get_configdir(), default_cnf_fname,
|
||||
get_configdir(), get_logdir(), get_cachedir(), get_libdir(),
|
||||
get_datadir(), get_execdir(), get_langdir(), get_piddir(),
|
||||
@ -1299,7 +1336,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "dcf:l:vVs:S:?L:D:C:B:U:A:P:G:N:E:F:M:H:",
|
||||
while ((opt = getopt_long(argc, argv, "dcf:g:l:vVs:S:?L:D:C:B:U:A:P:G:N:E:F:M:H:",
|
||||
long_options, &option_index)) != -1)
|
||||
{
|
||||
bool succp = true;
|
||||
@ -1565,6 +1602,13 @@ int main(int argc, char **argv)
|
||||
config_check = true;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
if (!handle_debug_args(optarg))
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
succp = false;
|
||||
@ -2067,8 +2111,10 @@ int main(int argc, char **argv)
|
||||
utils_end();
|
||||
cleanup_process_datadir();
|
||||
MXS_NOTICE("MaxScale shutdown completed.");
|
||||
|
||||
unload_all_modules();
|
||||
if (unload_modules_at_exit)
|
||||
{
|
||||
unload_all_modules();
|
||||
}
|
||||
/* Remove Pidfile */
|
||||
unlock_pidfile();
|
||||
unlink_pidfile();
|
||||
@ -2899,3 +2945,88 @@ static void modules_process_finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void enable_module_unloading()
|
||||
{
|
||||
unload_modules_at_exit = true;
|
||||
}
|
||||
|
||||
static void disable_module_unloading()
|
||||
{
|
||||
unload_modules_at_exit = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process command line debug arguments
|
||||
*
|
||||
* @param args The debug argument list
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool handle_debug_args(char* args)
|
||||
{
|
||||
bool arg_error = false;
|
||||
int args_found = 0;
|
||||
char *endptr = NULL;
|
||||
char* token = strtok_r(args, ",", &endptr);
|
||||
while (token)
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = 0; debug_arguments[i].action != NULL; i++)
|
||||
{
|
||||
// Debug features are activated by running functions in the struct-array.
|
||||
if (strcmp(token, debug_arguments[i].name) == 0)
|
||||
{
|
||||
found = true;
|
||||
args_found++;
|
||||
debug_arguments[i].action();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
const char UNRECOG_P1[] = "Unrecognized debug setting: '";
|
||||
const char UNRECOG_P2[] = "'.";
|
||||
size_t unrecog_msg_len = sizeof(UNRECOG_P1) + strlen(token) + sizeof(UNRECOG_P2);
|
||||
char unrecog_msg[unrecog_msg_len];
|
||||
snprintf(unrecog_msg, unrecog_msg_len, "%s%s%s", UNRECOG_P1, token, UNRECOG_P2);
|
||||
print_log_n_stderr(true, true, unrecog_msg, unrecog_msg, 0);
|
||||
arg_error = true;
|
||||
}
|
||||
token = strtok_r(NULL, ",", &endptr);
|
||||
}
|
||||
if (args_found == 0)
|
||||
{
|
||||
arg_error = true;
|
||||
}
|
||||
if (arg_error)
|
||||
{
|
||||
// Form a string with all debug argument names listed.
|
||||
size_t total_len = 1;
|
||||
for (int i = 0; debug_arguments[i].action != NULL; i++)
|
||||
{
|
||||
total_len += strlen(debug_arguments[i].name) + 1;
|
||||
}
|
||||
char arglist[total_len];
|
||||
arglist[0] = '\0';
|
||||
for (int i = 0; debug_arguments[i].action != NULL; i++)
|
||||
{
|
||||
strcat(arglist, debug_arguments[i].name);
|
||||
// If not the last element, add a comma
|
||||
if (debug_arguments[i + 1].action != NULL)
|
||||
{
|
||||
strcat(arglist, ", ");
|
||||
}
|
||||
}
|
||||
const char DEBUG_ERROR_P1[] =
|
||||
"Debug argument identifier '-g' or '--debug' was specified "
|
||||
"but no arguments were found or one of them was invalid. Supported "
|
||||
"arguments are: ";
|
||||
const char DEBUG_ERROR_P2[] = ".";
|
||||
size_t arg_error_msg_len = sizeof(DEBUG_ERROR_P1) + total_len + sizeof(DEBUG_ERROR_P2);
|
||||
char arg_error_msg[arg_error_msg_len];
|
||||
snprintf(arg_error_msg, arg_error_msg_len, "%s%s%s", DEBUG_ERROR_P1, arglist,
|
||||
DEBUG_ERROR_P2);
|
||||
print_log_n_stderr(true, true, arg_error_msg, arg_error_msg, 0);
|
||||
}
|
||||
return !arg_error;
|
||||
}
|
||||
|
@ -141,7 +141,6 @@ mxs_pcre2_result_t mxs_pcre2_simple_match(const char* pattern, const char* subje
|
||||
void mxs_pcre2_print_error(int errorcode, const char *module_name, const char *filename,
|
||||
int line_num, const char* func_name)
|
||||
{
|
||||
ss_dassert(module_name);
|
||||
ss_dassert(filename);
|
||||
ss_dassert(func_name);
|
||||
|
||||
@ -158,3 +157,53 @@ void mxs_pcre2_print_error(int errorcode, const char *module_name, const char *f
|
||||
"message.");
|
||||
}
|
||||
}
|
||||
|
||||
bool mxs_pcre2_check_match_exclude(pcre2_code* re_match, pcre2_code* re_exclude,
|
||||
pcre2_match_data* md, const char* subject,
|
||||
int length, const char* calling_module)
|
||||
{
|
||||
ss_dassert((!re_match && !re_exclude) || (md && subject));
|
||||
bool rval = true;
|
||||
int string_len = ((size_t)length == PCRE2_ZERO_TERMINATED) ? strlen(subject) : length;
|
||||
if (re_match)
|
||||
{
|
||||
int result = pcre2_match(re_match, (PCRE2_SPTR)subject, string_len, 0, 0, md, NULL);
|
||||
if (result == PCRE2_ERROR_NOMATCH)
|
||||
{
|
||||
rval = false; // Didn't match the "match"-regex
|
||||
if (mxs_log_priority_is_enabled(LOG_INFO))
|
||||
{
|
||||
mxs_log_message(LOG_INFO, calling_module, __FILE__, __LINE__, __func__,
|
||||
"Subject does not match the 'match' pattern: %.*s",
|
||||
string_len, subject);
|
||||
}
|
||||
}
|
||||
else if (result < 0)
|
||||
{
|
||||
rval = false;
|
||||
/* The __FILE__ etc macros here do not match calling_module, but
|
||||
* the values are only used for throttling messages. */
|
||||
mxs_pcre2_print_error(result, calling_module, __FILE__, __LINE__, __func__);
|
||||
}
|
||||
}
|
||||
if (rval && re_exclude)
|
||||
{
|
||||
int result = pcre2_match(re_exclude, (PCRE2_SPTR)subject, string_len, 0, 0, md, NULL);
|
||||
if (result >= 0)
|
||||
{
|
||||
rval = false; // Matched the "exclude"-regex
|
||||
if (mxs_log_priority_is_enabled(LOG_INFO))
|
||||
{
|
||||
mxs_log_message(LOG_INFO, calling_module, __FILE__, __LINE__, __func__,
|
||||
"Query matches the 'exclude' pattern: %.*s",
|
||||
string_len, subject);
|
||||
}
|
||||
}
|
||||
else if (result != PCRE2_ERROR_NOMATCH)
|
||||
{
|
||||
rval = false;
|
||||
mxs_pcre2_print_error(result, calling_module, __FILE__, __LINE__, __func__);
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
@ -1310,7 +1310,7 @@ bool modutil_ignorable_ping(DCB *dcb)
|
||||
return rval;
|
||||
}
|
||||
|
||||
const char format_str[] = "COM_UNKNOWN(%02x)";
|
||||
const char format_str[] = "COM_UNKNOWN(%02hhx)";
|
||||
|
||||
// The message always fits inside the buffer
|
||||
thread_local char unknow_type[sizeof(format_str)] = "";
|
||||
|
@ -972,7 +972,7 @@ static const char* mon_get_event_name(MXS_MONITOR_SERVERS* node)
|
||||
static void mon_append_node_names(MXS_MONITOR_SERVERS* servers, char* dest, int len, int status)
|
||||
{
|
||||
const char *separator = "";
|
||||
char arr[MAX_SERVER_NAME_LEN + 64]; // Some extra space for port and separator
|
||||
char arr[MAX_SERVER_ADDRESS_LEN + 64]; // Some extra space for port and separator
|
||||
dest[0] = '\0';
|
||||
|
||||
while (servers && len)
|
||||
|
@ -285,3 +285,15 @@ mxs_mysql_name_kind_t mxs_mysql_name_to_pcre(char *pcre,
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void mxs_mysql_set_server_version(MYSQL* mysql, SERVER* server)
|
||||
{
|
||||
const char* version_string = mysql_get_server_info(mysql);
|
||||
|
||||
if (version_string)
|
||||
{
|
||||
unsigned long version = mysql_get_server_version(mysql);
|
||||
|
||||
server_set_version(server, version_string, version);
|
||||
}
|
||||
}
|
||||
|
@ -901,3 +901,23 @@ uint32_t qc_get_trx_type_mask(GWBUF* stmt)
|
||||
{
|
||||
return qc_get_trx_type_mask_using(stmt, qc_trx_parse_using);
|
||||
}
|
||||
|
||||
void qc_set_server_version(uint64_t version)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
classifier->qc_set_server_version(version);
|
||||
}
|
||||
|
||||
uint64_t qc_get_server_version()
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
uint64_t version;
|
||||
|
||||
classifier->qc_get_server_version(&version);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
@ -131,7 +131,6 @@ SERVER* server_alloc(const char *name, const char *address, unsigned short port,
|
||||
server->master_id = -1;
|
||||
server->depth = -1;
|
||||
server->parameters = NULL;
|
||||
server->server_string = NULL;
|
||||
spinlock_init(&server->lock);
|
||||
server->persistent = persistent;
|
||||
server->persistmax = 0;
|
||||
@ -187,7 +186,6 @@ server_free(SERVER *tofreeserver)
|
||||
/* Clean up session and free the memory */
|
||||
MXS_FREE(tofreeserver->protocol);
|
||||
MXS_FREE(tofreeserver->unique_name);
|
||||
MXS_FREE(tofreeserver->server_string);
|
||||
server_parameter_free(tofreeserver->parameters);
|
||||
|
||||
if (tofreeserver->persistent)
|
||||
@ -496,10 +494,7 @@ dprintServer(DCB *dcb, const SERVER *server)
|
||||
MXS_FREE(stat);
|
||||
dcb_printf(dcb, "\tProtocol: %s\n", server->protocol);
|
||||
dcb_printf(dcb, "\tPort: %d\n", server->port);
|
||||
if (server->server_string)
|
||||
{
|
||||
dcb_printf(dcb, "\tServer Version: %s\n", server->server_string);
|
||||
}
|
||||
dcb_printf(dcb, "\tServer Version: %s\n", server->version_string);
|
||||
dcb_printf(dcb, "\tNode Id: %ld\n", server->node_id);
|
||||
dcb_printf(dcb, "\tMaster Id: %ld\n", server->master_id);
|
||||
if (server->slaves)
|
||||
@ -1080,28 +1075,55 @@ server_map_status(const char *str)
|
||||
|
||||
/**
|
||||
* Set the version string of the server.
|
||||
* @param server Server to update
|
||||
* @param string Version string
|
||||
* @return True if the assignment of the version string was successful, false if
|
||||
* memory allocation failed.
|
||||
*
|
||||
* @param server Server to update
|
||||
* @param version_string Version string
|
||||
*/
|
||||
bool server_set_version_string(SERVER* server, const char* string)
|
||||
void server_set_version_string(SERVER* server, const char* version_string)
|
||||
{
|
||||
bool rval = true;
|
||||
string = MXS_STRDUP(string);
|
||||
// There is a race here. The string may be accessed, while we are
|
||||
// updating it. Thus we take some precautions to ensure that the
|
||||
// string cannot be completely garbled at any point.
|
||||
|
||||
if (string)
|
||||
size_t old_len = strlen(server->version_string);
|
||||
size_t new_len = strlen(version_string);
|
||||
|
||||
if (new_len >= MAX_SERVER_VERSION_LEN)
|
||||
{
|
||||
char* old = server->server_string;
|
||||
server->server_string = (char*)string;
|
||||
MXS_FREE(old);
|
||||
}
|
||||
else
|
||||
{
|
||||
rval = false;
|
||||
new_len = MAX_SERVER_VERSION_LEN - 1;
|
||||
}
|
||||
|
||||
return rval;
|
||||
if (new_len < old_len)
|
||||
{
|
||||
// If the new string is shorter, we start by nulling out the
|
||||
// excess data.
|
||||
memset(server->version_string + new_len, 0, old_len - new_len);
|
||||
}
|
||||
|
||||
strncpy(server->version_string, version_string, new_len);
|
||||
// No null-byte needs to be set. The array starts out as all zeros
|
||||
// and the above memset adds the necessary null, should the new string
|
||||
// be shorter than the old.
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version of the server.
|
||||
*
|
||||
* @param server Server to update
|
||||
* @param version_string Human readable version string.
|
||||
* @param version Version encoded as MariaDB encodes the version, i.e.:
|
||||
* version = major * 10000 + minor * 100 + patch
|
||||
*/
|
||||
void server_set_version(SERVER* server, const char* version_string, uint64_t version)
|
||||
{
|
||||
server_set_version_string(server, version_string);
|
||||
|
||||
atomic_store_uint64(&server->version, version);
|
||||
}
|
||||
|
||||
uint64_t server_get_version(const SERVER* server)
|
||||
{
|
||||
return atomic_load_uint64(&server->version);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1396,10 +1418,7 @@ static json_t* server_json_attributes(const SERVER* server)
|
||||
json_object_set_new(attr, CN_STATUS, json_string(stat));
|
||||
MXS_FREE(stat);
|
||||
|
||||
if (server->server_string)
|
||||
{
|
||||
json_object_set_new(attr, CN_VERSION_STRING, json_string(server->server_string));
|
||||
}
|
||||
json_object_set_new(attr, CN_VERSION_STRING, json_string(server->version_string));
|
||||
|
||||
json_object_set_new(attr, "node_id", json_integer(server->node_id));
|
||||
json_object_set_new(attr, "master_id", json_integer(server->master_id));
|
||||
@ -1440,7 +1459,7 @@ static json_t* server_json_attributes(const SERVER* server)
|
||||
json_object_set_new(stats, "total_connections", json_integer(server->stats.n_connections));
|
||||
json_object_set_new(stats, "active_operations", json_integer(server->stats.n_current_ops));
|
||||
|
||||
json_object_set_new(attr, "statictics", stats);
|
||||
json_object_set_new(attr, "statistics", stats);
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
@ -2673,3 +2673,81 @@ json_t* service_relations_to_server(const SERVER* server, const char* host)
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
uint64_t service_get_version(const SERVICE *service, service_version_which_t which)
|
||||
{
|
||||
uint64_t version = 0;
|
||||
|
||||
if (which == SERVICE_VERSION_ANY)
|
||||
{
|
||||
SERVER_REF* sref = service->dbref;
|
||||
|
||||
while (sref && !sref->active)
|
||||
{
|
||||
sref = sref->next;
|
||||
}
|
||||
|
||||
if (sref)
|
||||
{
|
||||
version = server_get_version(sref->server);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
uint64_t v;
|
||||
|
||||
if (which == SERVICE_VERSION_MIN)
|
||||
{
|
||||
v = UINT64_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(which == SERVICE_VERSION_MAX);
|
||||
|
||||
v = 0;
|
||||
}
|
||||
|
||||
SERVER_REF* sref = service->dbref;
|
||||
|
||||
while (sref)
|
||||
{
|
||||
if (sref->active)
|
||||
{
|
||||
++n;
|
||||
|
||||
SERVER* s = sref->server;
|
||||
uint64_t server_version = server_get_version(s);
|
||||
|
||||
if (which == SERVICE_VERSION_MIN)
|
||||
{
|
||||
if (server_version < v)
|
||||
{
|
||||
v = server_version;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(which == SERVICE_VERSION_MAX);
|
||||
|
||||
if (server_version > v)
|
||||
{
|
||||
v = server_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sref = sref->next;
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
v = 0;
|
||||
}
|
||||
|
||||
version = v;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
@ -1156,8 +1156,12 @@ struct SessionListData
|
||||
|
||||
bool seslist_cb(DCB* dcb, void* data)
|
||||
{
|
||||
SessionListData* d = (SessionListData*)data;
|
||||
json_array_append_new(d->json, session_json_data(dcb->session, d->host));
|
||||
if (dcb->dcb_role == DCB_ROLE_CLIENT_HANDLER)
|
||||
{
|
||||
SessionListData* d = (SessionListData*)data;
|
||||
json_array_append_new(d->json, session_json_data(dcb->session, d->host));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,13 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include "session_command.hh"
|
||||
#include <maxscale/session_command.hh>
|
||||
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
using namespace maxscale;
|
||||
|
||||
void SessionCommand::mark_reply_received()
|
||||
{
|
||||
m_reply_sent = true;
|
@ -48,14 +48,12 @@ test1()
|
||||
DCB *dcb;
|
||||
SERV_LISTENER dummy;
|
||||
/* Single buffer tests */
|
||||
ss_dfprintf(stderr,
|
||||
"testdcb : creating buffer with type DCB_ROLE_SERVICE_LISTENER");
|
||||
dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER, &dummy);
|
||||
ss_dfprintf(stderr, "testdcb : creating buffer with type DCB_ROLE_SERVICE_LISTENER");
|
||||
dcb = dcb_alloc(DCB_ROLE_INTERNAL, &dummy);
|
||||
printDCB(dcb);
|
||||
ss_info_dassert(dcb_isvalid(dcb), "New DCB must be valid");
|
||||
ss_dfprintf(stderr, "\t..done\nAllocated dcb.");
|
||||
printAllDCBs();
|
||||
ss_info_dassert(true, "Something is true");
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
dcb->state = DCB_STATE_POLLING;
|
||||
dcb_close(dcb);
|
||||
|
@ -82,7 +82,7 @@ const char* test1_json =
|
||||
" 3002,"
|
||||
" 3003"
|
||||
" ],"
|
||||
" \"statictics\": {"
|
||||
" \"statistics\": {"
|
||||
" \"connections\": 0,"
|
||||
" \"total_connections\": 0,"
|
||||
" \"active_operations\": 0"
|
||||
@ -136,7 +136,7 @@ const char* test1_json =
|
||||
" \"master_id\": 3000,"
|
||||
" \"replication_depth\": 1,"
|
||||
" \"slaves\": [],"
|
||||
" \"statictics\": {"
|
||||
" \"statistics\": {"
|
||||
" \"connections\": 0,"
|
||||
" \"total_connections\": 0,"
|
||||
" \"active_operations\": 0"
|
||||
@ -190,7 +190,7 @@ const char* test1_json =
|
||||
" \"master_id\": 3000,"
|
||||
" \"replication_depth\": 1,"
|
||||
" \"slaves\": [],"
|
||||
" \"statictics\": {"
|
||||
" \"statistics\": {"
|
||||
" \"connections\": 0,"
|
||||
" \"total_connections\": 0,"
|
||||
" \"active_operations\": 0"
|
||||
@ -244,7 +244,7 @@ const char* test1_json =
|
||||
" \"master_id\": 3000,"
|
||||
" \"replication_depth\": 1,"
|
||||
" \"slaves\": [],"
|
||||
" \"statictics\": {"
|
||||
" \"statistics\": {"
|
||||
" \"connections\": 0,"
|
||||
" \"total_connections\": 0,"
|
||||
" \"active_operations\": 0"
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/paths.h>
|
||||
@ -69,6 +70,7 @@ struct test_case
|
||||
uint32_t type_mask;
|
||||
} test_cases[] =
|
||||
{
|
||||
// Keep these all uppercase, lowercase are tested programmatically.
|
||||
{ "BEGIN", QUERY_TYPE_BEGIN_TRX },
|
||||
{ "BEGIN WORK", QUERY_TYPE_BEGIN_TRX },
|
||||
|
||||
@ -299,14 +301,23 @@ bool test(uint32_t (*getter)(GWBUF*), bool dont_bail_out)
|
||||
string base(pTest->zStmt);
|
||||
cout << base << endl;
|
||||
|
||||
string s;
|
||||
|
||||
s = base;
|
||||
if (!test(getter, s.c_str(), pTest->type_mask))
|
||||
if (!test(getter, base.c_str(), pTest->type_mask))
|
||||
{
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (dont_bail_out || rc)
|
||||
{
|
||||
// Test all lowercase.
|
||||
string lc(base);
|
||||
transform(lc.begin(), lc.end(), lc.begin(), ::tolower);
|
||||
|
||||
if (!test(getter, lc.c_str(), pTest->type_mask))
|
||||
{
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dont_bail_out || rc)
|
||||
{
|
||||
if (!test_with_prefixes(getter, base, pTest->type_mask))
|
||||
|
@ -522,14 +522,13 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
mysql_get_character_set_info(mysql, &cs_info);
|
||||
server->charset = cs_info.number;
|
||||
|
||||
if (server->server_string == NULL)
|
||||
if (server->version_string[0] == 0)
|
||||
{
|
||||
const char *server_string = mysql_get_server_info(mysql);
|
||||
server_set_version_string(server, server_string);
|
||||
mxs_mysql_set_server_version(mysql, server);
|
||||
}
|
||||
|
||||
const char *template = "SELECT user, host, %s, Select_priv FROM mysql.user limit 1";
|
||||
const char* query_pw = strstr(server->server_string, "5.7.") ?
|
||||
const char* query_pw = strstr(server->version_string, "5.7.") ?
|
||||
MYSQL57_PASSWORD : MYSQL_PASSWORD;
|
||||
char query[strlen(template) + strlen(query_pw) + 1];
|
||||
bool rval = true;
|
||||
@ -726,18 +725,14 @@ void commit_sqlite_transaction(sqlite3 *handle)
|
||||
}
|
||||
}
|
||||
|
||||
int get_users_from_server(MYSQL *con, SERVER_REF *server, SERVICE *service, SERV_LISTENER *listener)
|
||||
int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service, SERV_LISTENER *listener)
|
||||
{
|
||||
if (server->server->server_string == NULL)
|
||||
if (server_ref->server->version_string[0] == 0)
|
||||
{
|
||||
const char *server_string = mysql_get_server_info(con);
|
||||
if (!server_set_version_string(server->server, server_string))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
mxs_mysql_set_server_version(con, server_ref->server);
|
||||
}
|
||||
|
||||
char *query = get_new_users_query(server->server->server_string, service->enable_root);
|
||||
char *query = get_new_users_query(server_ref->server->version_string, service->enable_root);
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
bool anon_user = false;
|
||||
int users = 0;
|
||||
|
2
server/modules/filter/cache/CMakeLists.txt
vendored
2
server/modules/filter/cache/CMakeLists.txt
vendored
@ -17,7 +17,7 @@ if (JANSSON_FOUND)
|
||||
storagefactory.cc
|
||||
storagereal.cc
|
||||
)
|
||||
target_link_libraries(cache maxscale-common ${JANSSON_LIBRARIES})
|
||||
target_link_libraries(cache maxscale-common ${JANSSON_LIBRARIES} MySQLCommon)
|
||||
set_target_properties(cache PROPERTIES VERSION "1.0.0")
|
||||
set_target_properties(cache PROPERTIES LINK_FLAGS -Wl,-z,defs)
|
||||
install_module(cache core)
|
||||
|
@ -205,15 +205,15 @@ CacheFilterSession* CacheFilterSession::Create(Cache* pCache, MXS_SESSION* pSess
|
||||
ss_dassert(pSession->client_dcb);
|
||||
ss_dassert(pSession->client_dcb->data);
|
||||
|
||||
MYSQL_session *pMysqlSession = (MYSQL_session*)pSession->client_dcb->data;
|
||||
const char* zDb = mxs_mysql_get_current_db(pSession);
|
||||
char* zDefaultDb = NULL;
|
||||
|
||||
if (pMysqlSession->db[0] != 0)
|
||||
if (zDb[0] != 0)
|
||||
{
|
||||
zDefaultDb = MXS_STRDUP(pMysqlSession->db);
|
||||
zDefaultDb = MXS_STRDUP(zDb);
|
||||
}
|
||||
|
||||
if ((pMysqlSession->db[0] == 0) || zDefaultDb)
|
||||
if ((zDb[0] == 0) || zDefaultDb)
|
||||
{
|
||||
pCacheFilterSession = new (std::nothrow) CacheFilterSession(pSession, pCache, zDefaultDb);
|
||||
|
||||
|
@ -13,16 +13,18 @@
|
||||
|
||||
#define MXS_MODULE_NAME "ccrfilter"
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/filter.h>
|
||||
#include <maxscale/hint.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/modinfo.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <string.h>
|
||||
#include <maxscale/hint.h>
|
||||
#include <maxscale/pcre2.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include <regex.h>
|
||||
#include <maxscale/alloc.h>
|
||||
|
||||
/**
|
||||
* @file ccrfilter.c - a very simple filter designed to send queries to the
|
||||
@ -81,8 +83,9 @@ typedef struct
|
||||
int count; /*< Number of hints to add after each operation
|
||||
* that modifies data. */
|
||||
LAGSTATS stats;
|
||||
regex_t re; /* Compiled regex text of match */
|
||||
regex_t nore; /* Compiled regex text of ignore */
|
||||
pcre2_code* re; /* Compiled regex text of match */
|
||||
pcre2_code* nore; /* Compiled regex text of ignore */
|
||||
uint32_t ovector_size; /* PCRE2 match data ovector size */
|
||||
} CCR_INSTANCE;
|
||||
|
||||
/**
|
||||
@ -93,16 +96,20 @@ typedef struct
|
||||
MXS_DOWNSTREAM down; /*< The downstream filter */
|
||||
int hints_left; /*< Number of hints left to add to queries*/
|
||||
time_t last_modification; /*< Time of the last data modifying operation */
|
||||
pcre2_match_data* md; /*< PCRE2 match data */
|
||||
} CCR_SESSION;
|
||||
|
||||
static const MXS_ENUM_VALUE option_values[] =
|
||||
{
|
||||
{"ignorecase", REG_ICASE},
|
||||
{"ignorecase", PCRE2_CASELESS},
|
||||
{"case", 0},
|
||||
{"extended", REG_EXTENDED},
|
||||
{"extended", PCRE2_EXTENDED},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static const char PARAM_MATCH[] = "match";
|
||||
static const char PARAM_IGNORE[] = "ignore";
|
||||
|
||||
typedef enum ccr_hint_value_t
|
||||
{
|
||||
CCR_HINT_NONE,
|
||||
@ -154,8 +161,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
{"count", MXS_MODULE_PARAM_COUNT, "0"},
|
||||
{"time", MXS_MODULE_PARAM_COUNT, CCR_DEFAULT_TIME},
|
||||
{"match", MXS_MODULE_PARAM_STRING},
|
||||
{"ignore", MXS_MODULE_PARAM_STRING},
|
||||
{PARAM_MATCH, MXS_MODULE_PARAM_REGEX},
|
||||
{PARAM_IGNORE, MXS_MODULE_PARAM_REGEX},
|
||||
{
|
||||
"options",
|
||||
MXS_MODULE_PARAM_ENUM,
|
||||
@ -192,23 +199,26 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
my_instance->stats.n_add_count = 0;
|
||||
my_instance->stats.n_add_time = 0;
|
||||
my_instance->stats.n_modified = 0;
|
||||
my_instance->ovector_size = 0;
|
||||
my_instance->re = NULL;
|
||||
my_instance->nore = NULL;
|
||||
|
||||
int cflags = config_get_enum(params, "options", option_values);
|
||||
my_instance->match = config_copy_string(params, PARAM_MATCH);
|
||||
my_instance->nomatch = config_copy_string(params, PARAM_IGNORE);
|
||||
const char* keys[] = {PARAM_MATCH, PARAM_IGNORE};
|
||||
pcre2_code** code_arr[] = {&my_instance->re, &my_instance->nore};
|
||||
|
||||
if ((my_instance->match = config_copy_string(params, "match")))
|
||||
if (!config_get_compiled_regexes(params, keys, sizeof(keys)/sizeof(char*),
|
||||
cflags, &my_instance->ovector_size,
|
||||
code_arr))
|
||||
{
|
||||
if (regcomp(&my_instance->re, my_instance->match, cflags))
|
||||
{
|
||||
MXS_ERROR("Failed to compile regex '%s'.", my_instance->match);
|
||||
}
|
||||
}
|
||||
|
||||
if ((my_instance->nomatch = config_copy_string(params, "ignore")))
|
||||
{
|
||||
if (regcomp(&my_instance->nore, my_instance->nomatch, cflags))
|
||||
{
|
||||
MXS_ERROR("Failed to compile regex '%s'.", my_instance->nomatch);
|
||||
}
|
||||
MXS_FREE(my_instance->match);
|
||||
MXS_FREE(my_instance->nomatch);
|
||||
pcre2_code_free(my_instance->re);
|
||||
pcre2_code_free(my_instance->nore);
|
||||
MXS_FREE(my_instance);
|
||||
my_instance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,12 +236,23 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
static MXS_FILTER_SESSION *
|
||||
newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
{
|
||||
CCR_INSTANCE *my_instance = (CCR_INSTANCE *)instance;
|
||||
CCR_SESSION *my_session = MXS_MALLOC(sizeof(CCR_SESSION));
|
||||
|
||||
if (my_session)
|
||||
{
|
||||
bool error = false;
|
||||
my_session->hints_left = 0;
|
||||
my_session->last_modification = 0;
|
||||
if (my_instance->ovector_size)
|
||||
{
|
||||
my_session->md = pcre2_match_data_create(my_instance->ovector_size, NULL);
|
||||
if (!my_session->md)
|
||||
{
|
||||
MXS_FREE(my_session);
|
||||
my_session = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (MXS_FILTER_SESSION*)my_session;
|
||||
@ -296,6 +317,7 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
CCR_INSTANCE *my_instance = (CCR_INSTANCE *)instance;
|
||||
CCR_SESSION *my_session = (CCR_SESSION *)session;
|
||||
char *sql;
|
||||
int length;
|
||||
time_t now = time(NULL);
|
||||
|
||||
if (modutil_is_SQL(queue))
|
||||
@ -306,7 +328,7 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
*/
|
||||
if (qc_query_is_type(qc_get_type_mask(queue), QUERY_TYPE_WRITE))
|
||||
{
|
||||
if ((sql = modutil_get_SQL(queue)) != NULL)
|
||||
if (modutil_extract_SQL(queue, &sql, &length))
|
||||
{
|
||||
bool trigger_ccr = true;
|
||||
bool decided = false; // Set by hints to take precedence.
|
||||
@ -322,18 +344,10 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
}
|
||||
if (!decided)
|
||||
{
|
||||
if (my_instance->nomatch &&
|
||||
regexec(&my_instance->nore, sql, 0, NULL, 0) == 0)
|
||||
{
|
||||
// Nomatch was present and sql matched it.
|
||||
trigger_ccr = false;
|
||||
}
|
||||
else if (my_instance->match &&
|
||||
(regexec(&my_instance->re, sql, 0, NULL, 0) != 0))
|
||||
{
|
||||
// Match was present but sql did *not* match it.
|
||||
trigger_ccr = false;
|
||||
}
|
||||
trigger_ccr =
|
||||
mxs_pcre2_check_match_exclude(my_instance->re, my_instance->nore,
|
||||
my_session->md, sql, length,
|
||||
MXS_MODULE_NAME);
|
||||
}
|
||||
if (trigger_ccr)
|
||||
{
|
||||
@ -351,7 +365,6 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
|
||||
my_instance->stats.n_modified++;
|
||||
}
|
||||
MXS_FREE(sql);
|
||||
}
|
||||
}
|
||||
else if (my_session->hints_left > 0)
|
||||
@ -435,7 +448,7 @@ static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESS
|
||||
|
||||
if (my_instance->match)
|
||||
{
|
||||
json_object_set_new(rval, "match", json_string(my_instance->match));
|
||||
json_object_set_new(rval, PARAM_MATCH, json_string(my_instance->match));
|
||||
}
|
||||
|
||||
if (my_instance->nomatch)
|
||||
|
@ -7,13 +7,13 @@ if(BISON_FOUND AND FLEX_FOUND)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_library(dbfwfilter SHARED dbfwfilter.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS})
|
||||
target_link_libraries(dbfwfilter maxscale-common)
|
||||
target_link_libraries(dbfwfilter maxscale-common MySQLCommon)
|
||||
set_target_properties(dbfwfilter PROPERTIES VERSION "1.0.0")
|
||||
install_module(dbfwfilter core)
|
||||
|
||||
# The offline rule check utility
|
||||
add_executable(dbfwchk dbfw_rule_check.c ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS})
|
||||
target_link_libraries(dbfwchk maxscale-common)
|
||||
target_link_libraries(dbfwchk maxscale-common MySQLCommon)
|
||||
install_executable(dbfwchk core)
|
||||
|
||||
else()
|
||||
|
@ -1745,7 +1745,7 @@ GWBUF* gen_dummy_error(FW_SESSION* session, char* msg)
|
||||
}
|
||||
|
||||
dcb = session->session->client_dcb;
|
||||
mysql_session = (MYSQL_session*) dcb->data;
|
||||
const char* db = mxs_mysql_get_current_db(session->session);
|
||||
errlen = msg != NULL ? strlen(msg) : 0;
|
||||
errmsg = (char*) MXS_MALLOC((512 + errlen) * sizeof(char));
|
||||
|
||||
@ -1755,14 +1755,14 @@ GWBUF* gen_dummy_error(FW_SESSION* session, char* msg)
|
||||
}
|
||||
|
||||
|
||||
if (mysql_session->db[0] == '\0')
|
||||
if (db[0] == '\0')
|
||||
{
|
||||
sprintf(errmsg, "Access denied for user '%s'@'%s'", dcb->user, dcb->remote);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(errmsg, "Access denied for user '%s'@'%s' to database '%s'",
|
||||
dcb->user, dcb->remote, mysql_session->db);
|
||||
dcb->user, dcb->remote, db);
|
||||
}
|
||||
|
||||
if (msg != NULL)
|
||||
|
@ -96,7 +96,11 @@ static const char* token_get_keyword(
|
||||
{
|
||||
switch (token->token)
|
||||
{
|
||||
case TOK_EOL:
|
||||
case TOK_END:
|
||||
return "End of hint";
|
||||
break;
|
||||
|
||||
case TOK_LINEBRK:
|
||||
return "End of line";
|
||||
break;
|
||||
|
||||
@ -107,14 +111,14 @@ static const char* token_get_keyword(
|
||||
default:
|
||||
{
|
||||
int i = 0;
|
||||
while (i < TOK_EOL && keywords[i].token != token->token)
|
||||
while (i < TOK_LINEBRK && keywords[i].token != token->token)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
ss_dassert(i != TOK_EOL);
|
||||
ss_dassert(i != TOK_LINEBRK);
|
||||
|
||||
if (i == TOK_EOL)
|
||||
if (i == TOK_LINEBRK)
|
||||
{
|
||||
return "Unknown token";
|
||||
}
|
||||
@ -147,7 +151,7 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
GWBUF *buf;
|
||||
HINT_TOKEN *tok;
|
||||
HINT_MODE mode = HM_EXECUTE;
|
||||
|
||||
bool multiline_comment = false;
|
||||
/* First look for any comment in the SQL */
|
||||
modutil_MySQL_Query(request, &ptr, &len, &residual);
|
||||
buf = request;
|
||||
@ -180,7 +184,9 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
squoted = 0;
|
||||
}
|
||||
else if (quoted || squoted)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if (escape)
|
||||
{
|
||||
escape = 0;
|
||||
@ -197,6 +203,7 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
else if (*ptr == '*' && lastch == '/')
|
||||
{
|
||||
found = 1;
|
||||
multiline_comment = true;
|
||||
break;
|
||||
}
|
||||
else if (*ptr == '-' && lastch == '-')
|
||||
@ -271,9 +278,24 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
|
||||
state = HS_INIT;
|
||||
|
||||
while ((tok = hint_next_token(&buf, &ptr)) != NULL
|
||||
&& tok->token != TOK_EOL)
|
||||
while (((tok = hint_next_token(&buf, &ptr)) != NULL) &&
|
||||
(tok->token != TOK_END))
|
||||
{
|
||||
if (tok->token == TOK_LINEBRK)
|
||||
{
|
||||
if (multiline_comment)
|
||||
{
|
||||
// Skip token
|
||||
token_free(tok);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat as TOK_END
|
||||
tok->token = TOK_END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case HS_INIT:
|
||||
@ -365,6 +387,7 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
{
|
||||
case TOK_EQUAL:
|
||||
pname = lvalue;
|
||||
lvalue = NULL;
|
||||
state = HS_PVALUE;
|
||||
break;
|
||||
case TOK_PREPARE:
|
||||
@ -390,6 +413,8 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
case HS_PVALUE:
|
||||
/* Action: pname = tok->value */
|
||||
rval = hint_create_parameter(rval, pname, tok->value);
|
||||
MXS_FREE(pname);
|
||||
pname = NULL;
|
||||
state = HS_INIT;
|
||||
break;
|
||||
case HS_PREPARE:
|
||||
@ -418,7 +443,7 @@ hint_parser(HINT_SESSION *session, GWBUF *request)
|
||||
token_free(tok);
|
||||
} /*< while */
|
||||
|
||||
if (tok && tok->token == TOK_EOL)
|
||||
if (tok && tok->token == TOK_END)
|
||||
{
|
||||
token_free(tok);
|
||||
}
|
||||
@ -536,8 +561,8 @@ hint_next_token(GWBUF **buf, char **ptr)
|
||||
inword = 0;
|
||||
break;
|
||||
}
|
||||
/** found '=', move ptr and return with '=' */
|
||||
else if (!inword && inquote == '\0' && **ptr == '=')
|
||||
/** found '=' or '\n', move ptr and return with the char */
|
||||
else if (!inword && inquote == '\0' && ((**ptr == '=') || (**ptr == '\n')))
|
||||
{
|
||||
*dest = **ptr;
|
||||
dest++;
|
||||
@ -590,7 +615,12 @@ hint_next_token(GWBUF **buf, char **ptr)
|
||||
*/
|
||||
if (word[0] == '\0' || (word[0] == '*' && word[1] == '/'))
|
||||
{
|
||||
tok->token = TOK_EOL;
|
||||
tok->token = TOK_END;
|
||||
return tok;
|
||||
}
|
||||
if (word[0] == '\n')
|
||||
{
|
||||
tok->token = TOK_LINEBRK;
|
||||
return tok;
|
||||
}
|
||||
found = 0;
|
||||
|
@ -40,7 +40,8 @@ typedef enum
|
||||
TOK_MASTER,
|
||||
TOK_SLAVE,
|
||||
TOK_SERVER,
|
||||
TOK_EOL
|
||||
TOK_LINEBRK,
|
||||
TOK_END
|
||||
} TOKEN_VALUE;
|
||||
|
||||
/* The tokenising return type */
|
||||
|
@ -807,21 +807,15 @@ void pushMessage(MQ_INSTANCE *instance, amqp_basic_properties_t* prop, char* msg
|
||||
static MXS_FILTER_SESSION *
|
||||
newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
{
|
||||
MYSQL_session *sessauth = session->client_dcb->data;
|
||||
char *db = sessauth->db;
|
||||
if (db)
|
||||
const char *db = mxs_mysql_get_current_db(session);
|
||||
char* my_db = NULL;
|
||||
|
||||
if (*db)
|
||||
{
|
||||
if (strnlen(db, 128) > 0)
|
||||
my_db = MXS_STRDUP(my_db);
|
||||
if (!my_db)
|
||||
{
|
||||
db = MXS_STRDUP(db);
|
||||
if (!db)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
db = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -832,11 +826,11 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
my_session->was_query = false;
|
||||
my_session->uid = NULL;
|
||||
my_session->session = session;
|
||||
my_session->db = db;
|
||||
my_session->db = my_db;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_FREE(db);
|
||||
MXS_FREE(my_db);
|
||||
}
|
||||
|
||||
return (MXS_FILTER_SESSION*)my_session;
|
||||
|
@ -1,4 +1,4 @@
|
||||
add_library(qlafilter SHARED qlafilter.c)
|
||||
add_library(qlafilter SHARED qlafilter.cc)
|
||||
target_link_libraries(qlafilter maxscale-common)
|
||||
set_target_properties(qlafilter PROPERTIES VERSION "1.1.1")
|
||||
install_module(qlafilter core)
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#define MXS_MODULE_NAME "qlafilter"
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
@ -99,8 +101,6 @@ typedef struct
|
||||
|
||||
/* Avoid repeatedly printing some errors/warnings. */
|
||||
bool write_warning_given;
|
||||
bool match_error_printed;
|
||||
bool exclude_error_printed;
|
||||
} QLA_INSTANCE;
|
||||
|
||||
/* The session structure for this QLA filter. */
|
||||
@ -120,8 +120,6 @@ typedef struct
|
||||
static FILE* open_log_file(uint32_t, QLA_INSTANCE *, const char *);
|
||||
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*, const char*,
|
||||
const char*, size_t);
|
||||
static bool regex_check(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
const char* ptr, int length);
|
||||
|
||||
static const MXS_ENUM_VALUE option_values[] =
|
||||
{
|
||||
@ -159,6 +157,8 @@ static const char PARAM_LOG_DATA[] = "log_data";
|
||||
static const char PARAM_FLUSH[] = "flush";
|
||||
static const char PARAM_APPEND[] = "append";
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* The module entry point routine.
|
||||
*
|
||||
@ -256,6 +256,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
return &info;
|
||||
}
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
/**
|
||||
* Create an instance of the filter for a particular service within MaxScale.
|
||||
*
|
||||
@ -277,8 +279,6 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
my_instance->ovec_size = 0;
|
||||
my_instance->unified_fp = NULL;
|
||||
my_instance->write_warning_given = false;
|
||||
my_instance->match_error_printed = false;
|
||||
my_instance->exclude_error_printed = false;
|
||||
|
||||
my_instance->source = config_copy_string(params, PARAM_SOURCE);
|
||||
my_instance->user_name = config_copy_string(params, PARAM_USER);
|
||||
@ -296,29 +296,13 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
bool error = false;
|
||||
|
||||
int cflags = config_get_enum(params, PARAM_OPTIONS, option_values);
|
||||
if (my_instance->match)
|
||||
{
|
||||
my_instance->re_match =
|
||||
config_get_compiled_regex(params, PARAM_MATCH, cflags, &my_instance->ovec_size);
|
||||
if (!my_instance->re_match)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (my_instance->exclude)
|
||||
const char* keys[] = {PARAM_MATCH, PARAM_EXCLUDE};
|
||||
pcre2_code** code_arr[] = {&my_instance->re_match, &my_instance->re_exclude};
|
||||
if (!config_get_compiled_regexes(params, keys, sizeof(keys) / sizeof(char*),
|
||||
cflags, &my_instance->ovec_size, code_arr))
|
||||
{
|
||||
uint32_t ovec_size_temp = 0;
|
||||
my_instance->re_exclude =
|
||||
config_get_compiled_regex(params, PARAM_EXCLUDE, cflags, &ovec_size_temp);
|
||||
if (ovec_size_temp > my_instance->ovec_size)
|
||||
{
|
||||
my_instance->ovec_size = ovec_size_temp;
|
||||
}
|
||||
if (!my_instance->re_exclude)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
error = true;
|
||||
}
|
||||
|
||||
// Try to open the unified log file
|
||||
@ -328,7 +312,7 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
const char UNIFIED[] = ".unified";
|
||||
int namelen = strlen(my_instance->filebase) + sizeof(UNIFIED);
|
||||
char *filename = NULL;
|
||||
if ((filename = MXS_CALLOC(namelen, sizeof(char))) != NULL)
|
||||
if ((filename = (char*)MXS_CALLOC(namelen, sizeof(char))) != NULL)
|
||||
{
|
||||
snprintf(filename, namelen, "%s.unified", my_instance->filebase);
|
||||
// Open the file. It is only closed at program exit
|
||||
@ -386,7 +370,7 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
QLA_SESSION *my_session;
|
||||
const char *remote, *userName;
|
||||
|
||||
if ((my_session = MXS_CALLOC(1, sizeof(QLA_SESSION))) != NULL)
|
||||
if ((my_session = (QLA_SESSION*)MXS_CALLOC(1, sizeof(QLA_SESSION))) != NULL)
|
||||
{
|
||||
my_session->fp = NULL;
|
||||
my_session->match_data = NULL;
|
||||
@ -525,7 +509,8 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
|
||||
if (my_session->active &&
|
||||
modutil_extract_SQL(queue, &ptr, &length) &&
|
||||
regex_check(my_instance, my_session, ptr, length))
|
||||
mxs_pcre2_check_match_exclude(my_instance->re_match, my_instance->re_exclude,
|
||||
my_session->match_data, ptr, length, MXS_MODULE_NAME))
|
||||
{
|
||||
char buffer[QLA_DATE_BUFFER_SIZE];
|
||||
gettimeofday(&tv, NULL);
|
||||
@ -830,7 +815,7 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
||||
cause garbled printing if several threads write simultaneously, so we
|
||||
have to first print to a string. */
|
||||
char *print_str = NULL;
|
||||
if ((print_str = MXS_CALLOC(print_len, sizeof(char))) == NULL)
|
||||
if ((print_str = (char*)MXS_CALLOC(print_len, sizeof(char))) == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@ -918,46 +903,3 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
|
||||
static bool regex_check(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
const char* ptr, int length)
|
||||
{
|
||||
bool rval = true;
|
||||
if (my_instance->re_match)
|
||||
{
|
||||
int result = pcre2_match(my_instance->re_match, (PCRE2_SPTR)ptr,
|
||||
length, 0, 0, my_session->match_data, NULL);
|
||||
if (result == PCRE2_ERROR_NOMATCH)
|
||||
{
|
||||
rval = false; // Didn't match the "match"-regex
|
||||
}
|
||||
else if (result < 0)
|
||||
{
|
||||
rval = false;
|
||||
if (!my_instance->match_error_printed)
|
||||
{
|
||||
MXS_PCRE2_PRINT_ERROR(result);
|
||||
my_instance->match_error_printed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rval && my_instance->re_exclude)
|
||||
{
|
||||
int result = pcre2_match(my_instance->re_exclude, (PCRE2_SPTR)ptr,
|
||||
length, 0, 0, my_session->match_data, NULL);
|
||||
if (result >= 0)
|
||||
{
|
||||
rval = false; // Matched the "exclude"-regex
|
||||
}
|
||||
else if (result != PCRE2_ERROR_NOMATCH)
|
||||
{
|
||||
rval = false;
|
||||
if (!my_instance->exclude_error_printed)
|
||||
{
|
||||
MXS_PCRE2_PRINT_ERROR(result);
|
||||
my_instance->exclude_error_printed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
@ -29,31 +29,23 @@
|
||||
|
||||
static const MXS_ENUM_VALUE option_values[] =
|
||||
{
|
||||
{"ignorecase", REG_ICASE},
|
||||
{"case", 0},
|
||||
{"extended", REG_EXTENDED},
|
||||
{"ignorecase", PCRE2_CASELESS},
|
||||
{"case", 0},
|
||||
{"extended", PCRE2_EXTENDED},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
Tee::Tee(SERVICE* service, const char* user, const char* remote,
|
||||
const char* match, const char* nomatch, int cflags):
|
||||
Tee::Tee(SERVICE* service, std::string user, std::string remote,
|
||||
pcre2_code* match, std::string match_string,
|
||||
pcre2_code* exclude, std::string exclude_string):
|
||||
m_service(service),
|
||||
m_user(user),
|
||||
m_source(remote),
|
||||
m_match(match),
|
||||
m_nomatch(nomatch)
|
||||
m_match_code(match),
|
||||
m_exclude_code(exclude),
|
||||
m_match(match_string),
|
||||
m_exclude(exclude_string)
|
||||
{
|
||||
if (*match)
|
||||
{
|
||||
ss_debug(int rc = )regcomp(&m_re, match, cflags);
|
||||
ss_dassert(rc == 0);
|
||||
}
|
||||
|
||||
if (*nomatch)
|
||||
{
|
||||
ss_debug(int rc = )regcomp(&m_nore, nomatch, cflags);
|
||||
ss_dassert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,34 +60,22 @@ Tee::Tee(SERVICE* service, const char* user, const char* remote,
|
||||
*/
|
||||
Tee* Tee::create(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
{
|
||||
Tee *my_instance = NULL;
|
||||
|
||||
SERVICE* service = config_get_service(params, "service");
|
||||
const char* source = config_get_string(params, "source");
|
||||
const char* user = config_get_string(params, "user");
|
||||
const char* match = config_get_string(params, "match");
|
||||
const char* nomatch = config_get_string(params, "exclude");
|
||||
uint32_t cflags = config_get_enum(params, "options", option_values);
|
||||
pcre2_code* match = config_get_compiled_regex(params, "match", cflags, NULL);
|
||||
pcre2_code* exclude = config_get_compiled_regex(params, "exclude", cflags, NULL);
|
||||
const char* match_str = config_get_string(params, "match");
|
||||
const char* exclude_str = config_get_string(params, "exclude");
|
||||
|
||||
int cflags = config_get_enum(params, "options", option_values);
|
||||
regex_t re;
|
||||
regex_t nore;
|
||||
Tee* my_instance = new (std::nothrow) Tee(service, source, user, match,
|
||||
match_str, exclude, exclude_str);
|
||||
|
||||
if (*match && regcomp(&re, match, cflags) != 0)
|
||||
if (my_instance == NULL)
|
||||
{
|
||||
MXS_ERROR("Invalid regular expression '%s' for the match parameter.", match);
|
||||
}
|
||||
else if (*nomatch && regcomp(&nore, nomatch, cflags) != 0)
|
||||
{
|
||||
MXS_ERROR("Invalid regular expression '%s' for the nomatch parameter.", nomatch);
|
||||
|
||||
if (*match)
|
||||
{
|
||||
regfree(&re);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my_instance = new (std::nothrow) Tee(service, source, user, match, nomatch, cflags);
|
||||
pcre2_code_free(match);
|
||||
pcre2_code_free(exclude);
|
||||
}
|
||||
|
||||
return my_instance;
|
||||
@ -136,10 +116,10 @@ void Tee::diagnostics(DCB *dcb)
|
||||
dcb_printf(dcb, "\t\tInclude queries that match %s\n",
|
||||
m_match.c_str());
|
||||
}
|
||||
if (m_nomatch.c_str())
|
||||
if (m_exclude.c_str())
|
||||
{
|
||||
dcb_printf(dcb, "\t\tExclude queries that match %s\n",
|
||||
m_nomatch.c_str());
|
||||
m_exclude.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,9 +154,9 @@ json_t* Tee::diagnostics_json() const
|
||||
json_object_set_new(rval, "match", json_string(m_match.c_str()));
|
||||
}
|
||||
|
||||
if (m_nomatch.length())
|
||||
if (m_exclude.length())
|
||||
{
|
||||
json_object_set_new(rval, "exclude", json_string(m_nomatch.c_str()));
|
||||
json_object_set_new(rval, "exclude", json_string(m_exclude.c_str()));
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -210,8 +190,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
NULL, /* Thread finish. */
|
||||
{
|
||||
{"service", MXS_MODULE_PARAM_SERVICE, NULL, MXS_MODULE_OPT_REQUIRED},
|
||||
{"match", MXS_MODULE_PARAM_STRING},
|
||||
{"exclude", MXS_MODULE_PARAM_STRING},
|
||||
{"match", MXS_MODULE_PARAM_REGEX},
|
||||
{"exclude", MXS_MODULE_PARAM_REGEX},
|
||||
{"source", MXS_MODULE_PARAM_STRING},
|
||||
{"user", MXS_MODULE_PARAM_STRING},
|
||||
{
|
||||
|
@ -57,15 +57,26 @@ public:
|
||||
return m_service;
|
||||
}
|
||||
|
||||
pcre2_code* get_match() const
|
||||
{
|
||||
return m_match_code;
|
||||
}
|
||||
|
||||
pcre2_code* get_exclude() const
|
||||
{
|
||||
return m_exclude_code;
|
||||
}
|
||||
|
||||
private:
|
||||
Tee(SERVICE* service, const char* user, const char* remote,
|
||||
const char* match, const char* nomatch, int cflags);
|
||||
Tee(SERVICE* service, std::string user, std::string remote,
|
||||
pcre2_code* match, std::string match_string,
|
||||
pcre2_code* exclude, std::string exclude_string);
|
||||
|
||||
SERVICE* m_service;
|
||||
std::string m_user; /* The user name to filter on */
|
||||
std::string m_source; /* The source of the client connection */
|
||||
std::string m_match; /* Optional text to match against */
|
||||
std::string m_nomatch; /* Optional text to match against for exclusion */
|
||||
regex_t m_re; /* Compiled regex text */
|
||||
regex_t m_nore; /* Compiled regex nomatch text */
|
||||
std::string m_user; /* The user name to filter on */
|
||||
std::string m_source; /* The source of the client connection */
|
||||
pcre2_code* m_match_code; /* Compiled match pattern */
|
||||
pcre2_code* m_exclude_code; /* Compiled exclude pattern*/
|
||||
std::string m_match; /* Pattern for matching queries */
|
||||
std::string m_exclude; /* Pattern for excluding queries */
|
||||
};
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <maxscale/modutil.h>
|
||||
|
||||
/**
|
||||
* Detect loops in the filter chain.
|
||||
*/
|
||||
@ -57,9 +59,15 @@ bool recursive_tee_usage(std::set<std::string>& services, SERVICE* service)
|
||||
return false;
|
||||
}
|
||||
|
||||
TeeSession::TeeSession(MXS_SESSION* session, LocalClient* client):
|
||||
TeeSession::TeeSession(MXS_SESSION* session, LocalClient* client,
|
||||
pcre2_code* match, pcre2_match_data* md_match,
|
||||
pcre2_code* exclude, pcre2_match_data* md_exclude):
|
||||
mxs::FilterSession(session),
|
||||
m_client(client)
|
||||
m_client(client),
|
||||
m_match(match),
|
||||
m_md_match(md_match),
|
||||
m_exclude(exclude),
|
||||
m_md_exclude(md_exclude)
|
||||
{
|
||||
}
|
||||
|
||||
@ -75,17 +83,43 @@ TeeSession* TeeSession::create(Tee* my_instance, MXS_SESSION* session)
|
||||
}
|
||||
|
||||
LocalClient* client = NULL;
|
||||
pcre2_code* match = NULL;
|
||||
pcre2_code* exclude = NULL;
|
||||
pcre2_match_data* md_match = NULL;
|
||||
pcre2_match_data* md_exclude = NULL;
|
||||
|
||||
if (my_instance->user_matches(session_get_user(session)) &&
|
||||
my_instance->remote_matches(session_get_remote(session)))
|
||||
{
|
||||
match = my_instance->get_match();
|
||||
exclude = my_instance->get_exclude();
|
||||
|
||||
if ((match && (md_match = pcre2_match_data_create_from_pattern(match, NULL)) == NULL) ||
|
||||
(exclude && (md_exclude = pcre2_match_data_create_from_pattern(exclude, NULL)) == NULL))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((client = LocalClient::create(session, my_instance->get_service())) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return new (std::nothrow) TeeSession(session, client);
|
||||
TeeSession* tee = new (std::nothrow) TeeSession(session, client, match, md_match, exclude, md_exclude);
|
||||
|
||||
if (!tee)
|
||||
{
|
||||
pcre2_match_data_free(md_match);
|
||||
pcre2_match_data_free(md_exclude);
|
||||
|
||||
if (client)
|
||||
{
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
|
||||
return tee;
|
||||
}
|
||||
|
||||
TeeSession::~TeeSession()
|
||||
@ -99,7 +133,7 @@ void TeeSession::close()
|
||||
|
||||
int TeeSession::routeQuery(GWBUF* queue)
|
||||
{
|
||||
if (m_client)
|
||||
if (m_client && query_matches(queue))
|
||||
{
|
||||
m_client->queue_query(queue);
|
||||
}
|
||||
@ -115,3 +149,32 @@ json_t* TeeSession::diagnostics_json() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool TeeSession::query_matches(GWBUF* buffer)
|
||||
{
|
||||
bool rval = true;
|
||||
|
||||
if (m_match || m_exclude)
|
||||
{
|
||||
char* sql;
|
||||
int len;
|
||||
|
||||
if (modutil_extract_SQL(buffer, &sql, &len))
|
||||
{
|
||||
if (m_match && pcre2_match_8(m_match, (PCRE2_SPTR)sql, len, 0, 0,
|
||||
m_md_match, NULL) < 0)
|
||||
{
|
||||
MXS_INFO("Query does not match the 'match' pattern: %.*s", len, sql);
|
||||
rval = false;
|
||||
}
|
||||
else if (m_exclude && pcre2_match_8(m_exclude, (PCRE2_SPTR)sql, len,
|
||||
0, 0, m_md_exclude, NULL) >= 0)
|
||||
{
|
||||
MXS_INFO("Query matches the 'exclude' pattern: %.*s", len, sql);
|
||||
rval = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
@ -38,6 +38,14 @@ public:
|
||||
json_t* diagnostics_json() const;
|
||||
|
||||
private:
|
||||
TeeSession(MXS_SESSION* session, LocalClient* client);
|
||||
LocalClient* m_client; /**< The client connection to the local service */
|
||||
TeeSession(MXS_SESSION* session, LocalClient* client,
|
||||
pcre2_code* match, pcre2_match_data* md_match,
|
||||
pcre2_code* exclude, pcre2_match_data* md_exclude);
|
||||
bool query_matches(GWBUF* buffer);
|
||||
|
||||
LocalClient* m_client; /**< The client connection to the local service */
|
||||
pcre2_code* m_match;
|
||||
pcre2_match_data* m_md_match;
|
||||
pcre2_code* m_exclude;
|
||||
pcre2_match_data* m_md_exclude;
|
||||
};
|
||||
|
@ -13,31 +13,14 @@
|
||||
|
||||
/**
|
||||
* @file galera_mon.c - A MySQL Galera cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 22/07/13 Mark Riddoch Initial implementation
|
||||
* 21/05/14 Massimiliano Pinto Monitor sets a master server that has the lowest value of wsrep_local_index
|
||||
* 23/05/14 Massimiliano Pinto Added 1 configuration option (setInterval). Interval is printed in diagnostics.
|
||||
* 03/06/14 Mark Riddoch Add support for maintenance mode
|
||||
* 24/06/14 Massimiliano Pinto Added depth level 0 for each node
|
||||
* 30/10/14 Massimiliano Pinto Added disableMasterFailback feature
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
* 20/04/15 Guillaume Lefranc Added availableWhenDonor feature
|
||||
* 22/04/15 Martin Brampton Addition of disableMasterRoleSetting
|
||||
* 08/05/15 Markus Makela Addition of launchable scripts
|
||||
* 17/10/15 Martin Brampton Change DCB callback to hangup
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "galeramon"
|
||||
|
||||
#include "galeramon.h"
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
|
||||
#define DONOR_NODE_NAME_MAX_LEN 60
|
||||
#define DONOR_LIST_SET_VAR "SET GLOBAL wsrep_sst_donor = \""
|
||||
@ -331,11 +314,8 @@ monitorDatabase(MXS_MONITOR *mon, MXS_MONITOR_SERVERS *database)
|
||||
server_set_status_nolock(database->server, SERVER_RUNNING);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *) mysql_get_server_info(database->con);
|
||||
if (server_string)
|
||||
{
|
||||
server_set_version_string(database->server, server_string);
|
||||
}
|
||||
mxs_mysql_set_server_version(database->con, database->server);
|
||||
server_string = database->server->version_string;
|
||||
|
||||
/* Check if the the Galera FSM shows this node is joined to the cluster */
|
||||
char *cluster_member = "SHOW STATUS WHERE Variable_name IN"
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "mmmon.h"
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
@ -261,11 +262,8 @@ monitorDatabase(MXS_MONITOR* mon, MXS_MONITOR_SERVERS *database)
|
||||
server_version = mysql_get_server_version(database->con);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *) mysql_get_server_info(database->con);
|
||||
if (server_string)
|
||||
{
|
||||
server_set_version_string(database->server, server_string);
|
||||
}
|
||||
mxs_mysql_set_server_version(database->con, database->server);
|
||||
server_string = database->server->version_string;
|
||||
|
||||
/* get server_id form current node */
|
||||
if (mysql_query(database->con, "SELECT @@server_id") == 0
|
||||
|
@ -18,10 +18,11 @@
|
||||
#define MXS_MODULE_NAME "mysqlmon"
|
||||
|
||||
#include "../mysqlmon.h"
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
|
||||
#define DEFAULT_JOURNAL_MAX_AGE "28800"
|
||||
|
||||
@ -640,7 +641,7 @@ static MXS_MONITOR_SERVERS *build_mysql51_replication_tree(MXS_MONITOR *mon)
|
||||
(database->server->master_id <= 0 ||
|
||||
database->server->master_id != handle->master->server->node_id))
|
||||
{
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_set_pending_status(database, SERVER_SLAVE);
|
||||
monitor_set_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
}
|
||||
database = database->next;
|
||||
@ -726,11 +727,8 @@ monitorDatabase(MXS_MONITOR *mon, MXS_MONITOR_SERVERS *database)
|
||||
server_version = mysql_get_server_version(database->con);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *) mysql_get_server_info(database->con);
|
||||
if (server_string)
|
||||
{
|
||||
server_set_version_string(database->server, server_string);
|
||||
}
|
||||
mxs_mysql_set_server_version(database->con, database->server);
|
||||
server_string = database->server->version_string;
|
||||
|
||||
MYSQL_SERVER_INFO *serv_info = hashtable_fetch(handle->server_info, database->server->unique_name);
|
||||
ss_dassert(serv_info);
|
||||
@ -1853,7 +1851,7 @@ static MXS_MONITOR_SERVERS *get_replication_tree(MXS_MONITOR *mon, int num_serve
|
||||
/* this server is slave of another server not in MaxScale configuration
|
||||
* we cannot use it as a real slave.
|
||||
*/
|
||||
monitor_clear_pending_status(ptr, SERVER_SLAVE);
|
||||
monitor_set_pending_status(ptr, SERVER_SLAVE);
|
||||
monitor_set_pending_status(ptr, SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
}
|
||||
}
|
||||
|
@ -13,22 +13,13 @@
|
||||
|
||||
/**
|
||||
* @file ndbcluster_mon.c - A MySQL cluster SQL node monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 25/07/14 Massimiliano Pinto Initial implementation
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
* 08/05/15 Markus Makela Addition of launchable scripts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "ndbclustermon"
|
||||
|
||||
#include "../mysqlmon.h"
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
@ -234,11 +225,8 @@ monitorDatabase(MXS_MONITOR_SERVERS *database, char *defaultUser, char *defaultP
|
||||
server_set_status_nolock(database->server, SERVER_RUNNING);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *) mysql_get_server_info(database->con);
|
||||
if (server_string)
|
||||
{
|
||||
server_set_version_string(database->server, server_string);
|
||||
}
|
||||
mxs_mysql_set_server_version(database->con, database->server);
|
||||
server_string = database->server->version_string;
|
||||
|
||||
/* Check if the the SQL node is able to contact one or more data nodes */
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
|
||||
|
@ -808,18 +808,27 @@ gw_read_and_write(DCB *dcb)
|
||||
*/
|
||||
if (protocol_get_srv_command((MySQLProtocol *)dcb->protocol, true) != MYSQL_COM_UNDEFINED)
|
||||
{
|
||||
stmt = process_response_data(dcb, &read_buffer, gwbuf_length(read_buffer));
|
||||
/**
|
||||
* Received incomplete response to session command.
|
||||
* Store it to readqueue and return.
|
||||
*/
|
||||
if (!sescmd_response_complete(dcb))
|
||||
if (result_collected)
|
||||
{
|
||||
stmt = gwbuf_append(stmt, read_buffer);
|
||||
dcb->dcb_readqueue = gwbuf_append(stmt, dcb->dcb_readqueue);
|
||||
return 0;
|
||||
/** The result set or PS response was collected, we know it's complete */
|
||||
stmt = read_buffer;
|
||||
read_buffer = NULL;
|
||||
gwbuf_set_type(stmt, GWBUF_TYPE_RESPONSE_END | GWBUF_TYPE_SESCMD_RESPONSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = process_response_data(dcb, &read_buffer, gwbuf_length(read_buffer));
|
||||
/**
|
||||
* Received incomplete response to session command.
|
||||
* Store it to readqueue and return.
|
||||
*/
|
||||
if (!sescmd_response_complete(dcb))
|
||||
{
|
||||
stmt = gwbuf_append(stmt, read_buffer);
|
||||
dcb->dcb_readqueue = gwbuf_append(stmt, dcb->dcb_readqueue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stmt)
|
||||
{
|
||||
MXS_ERROR("Read buffer unexpectedly null, even though response "
|
||||
|
@ -224,8 +224,7 @@ int MySQLSendHandshake(DCB* dcb)
|
||||
{
|
||||
mysql_server_language = dcb->service->dbref->server->charset;
|
||||
|
||||
if (dcb->service->dbref->server->server_string &&
|
||||
strstr(dcb->service->dbref->server->server_string, "10.2."))
|
||||
if (strstr(dcb->service->dbref->server->version_string, "10.2."))
|
||||
{
|
||||
/** The backend servers support the extended capabilities */
|
||||
is_maria = true;
|
||||
@ -929,6 +928,10 @@ gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
||||
}
|
||||
}
|
||||
|
||||
/** The query classifier classifies according to the service's server that has
|
||||
* the smallest version number. */
|
||||
qc_set_server_version(service_get_version(session->service, SERVICE_VERSION_MIN));
|
||||
|
||||
spec_com_res_t res = process_special_commands(dcb, read_buffer, nbytes_read);
|
||||
int rval = 1;
|
||||
switch (res)
|
||||
|
@ -1587,3 +1587,15 @@ mysql_server_cmd_t mxs_mysql_current_command(MXS_SESSION* session)
|
||||
MySQLProtocol* proto = (MySQLProtocol*)session->client_dcb->protocol;
|
||||
return proto->current_command;
|
||||
}
|
||||
|
||||
const char* mxs_mysql_get_current_db(MXS_SESSION* session)
|
||||
{
|
||||
MYSQL_session* data = (MYSQL_session*)session->client_dcb->data;
|
||||
return data->db;
|
||||
}
|
||||
|
||||
void mxs_mysql_set_current_db(MXS_SESSION* session, const char* db)
|
||||
{
|
||||
MYSQL_session* data = (MYSQL_session*)session->client_dcb->data;
|
||||
snprintf(data->db, sizeof(data->db), "%s", db);
|
||||
}
|
||||
|
@ -20,7 +20,10 @@ import selectors
|
||||
import binascii
|
||||
import os
|
||||
|
||||
schema_read = False
|
||||
|
||||
def read_data():
|
||||
global schema_read
|
||||
sel = selectors.DefaultSelector()
|
||||
sel.register(sock, selectors.EVENT_READ)
|
||||
|
||||
@ -29,8 +32,17 @@ def read_data():
|
||||
events = sel.select(timeout=int(opts.read_timeout) if int(opts.read_timeout) > 0 else None)
|
||||
buf = sock.recv(4096, socket.MSG_DONTWAIT)
|
||||
if len(buf) > 0:
|
||||
# If the request for data is rejected, an error will be sent instead of the table schema
|
||||
if not schema_read:
|
||||
if "err" in buf.decode().lower():
|
||||
print(buf.decode(), file=sys.stderr)
|
||||
exit(1)
|
||||
else:
|
||||
schema_read = True
|
||||
|
||||
os.write(sys.stdout.fileno(), buf)
|
||||
sys.stdout.flush()
|
||||
|
||||
else:
|
||||
raise Exception('Socket was closed')
|
||||
|
||||
@ -40,6 +52,13 @@ def read_data():
|
||||
print(ex, file=sys.stderr)
|
||||
break
|
||||
|
||||
|
||||
def check_for_err(err):
|
||||
if "err" in err.lower().strip():
|
||||
print(err.strip(), file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description = "CDC Binary consumer", conflict_handler="resolve")
|
||||
parser.add_argument("-h", "--host", dest="host", help="Network address where the connection is made", default="localhost")
|
||||
parser.add_argument("-P", "--port", dest="port", help="Port where the connection is made", default="4001")
|
||||
@ -60,13 +79,17 @@ auth_string += bytes(hashlib.sha1(opts.password.encode("utf_8")).hexdigest().enc
|
||||
sock.send(auth_string)
|
||||
|
||||
# Discard the response
|
||||
response = str(sock.recv(1024)).encode('utf_8')
|
||||
response = sock.recv(1024).decode()
|
||||
|
||||
check_for_err(response)
|
||||
|
||||
# Register as a client as request Avro format data
|
||||
sock.send(bytes(("REGISTER UUID=XXX-YYY_YYY, TYPE=" + opts.format).encode()))
|
||||
|
||||
# Discard the response again
|
||||
response = str(sock.recv(1024)).encode('utf_8')
|
||||
response = sock.recv(1024).decode()
|
||||
|
||||
check_for_err(response)
|
||||
|
||||
# Request a data stream
|
||||
sock.send(bytes(("REQUEST-DATA " + opts.FILE + (" " + opts.GTID if opts.GTID else "")).encode()))
|
||||
|
@ -38,7 +38,7 @@ while True:
|
||||
if len(buf) == 0:
|
||||
break
|
||||
|
||||
data = buf.encode().strip()
|
||||
data = buf[:-1]
|
||||
producer.send(topic=opts.kafka_topic, value=data)
|
||||
producer.flush()
|
||||
|
||||
|
@ -575,9 +575,12 @@ int extract_type_length(const char* ptr, char *dest)
|
||||
}
|
||||
|
||||
/** Store type */
|
||||
int typelen = ptr - start;
|
||||
memcpy(dest, start, typelen);
|
||||
dest[typelen] = '\0';
|
||||
for (const char* c = start; c < ptr; c++)
|
||||
{
|
||||
*dest++ = tolower(*c);
|
||||
}
|
||||
|
||||
*dest++ = '\0';
|
||||
|
||||
/** Skip whitespace */
|
||||
while (*ptr && isspace(*ptr))
|
||||
@ -880,7 +883,7 @@ void read_alter_identifier(const char *sql, const char *end, char *dest, int siz
|
||||
|
||||
void make_avro_token(char* dest, const char* src, int length)
|
||||
{
|
||||
while (*src == '(' || *src == ')' || *src == '`' || isspace(*src))
|
||||
while (length > 0 && (*src == '(' || *src == ')' || *src == '`' || isspace(*src)))
|
||||
{
|
||||
src++;
|
||||
length--;
|
||||
@ -902,16 +905,17 @@ void make_avro_token(char* dest, const char* src, int length)
|
||||
fix_reserved_word(dest);
|
||||
}
|
||||
|
||||
int get_column_index(TABLE_CREATE *create, const char *tok)
|
||||
int get_column_index(TABLE_CREATE *create, const char *tok, int len)
|
||||
{
|
||||
int idx = -1;
|
||||
char safe_tok[strlen(tok) + 2];
|
||||
strcpy(safe_tok, tok);
|
||||
char safe_tok[len + 2];
|
||||
memcpy(safe_tok, tok, len);
|
||||
safe_tok[len] = '\0';
|
||||
fix_reserved_word(safe_tok);
|
||||
|
||||
for (int x = 0; x < create->columns; x++)
|
||||
{
|
||||
if (strcasecmp(create->column_names[x], tok) == 0)
|
||||
if (strcasecmp(create->column_names[x], safe_tok) == 0)
|
||||
{
|
||||
idx = x;
|
||||
break;
|
||||
@ -950,18 +954,17 @@ bool table_create_alter(TABLE_CREATE *create, const char *sql, const char *end)
|
||||
{
|
||||
tok = get_tok(tok + len, &len, end);
|
||||
|
||||
char ** tmp = MXS_REALLOC(create->column_names, sizeof(char*) * create->columns + 1);
|
||||
ss_dassert(tmp);
|
||||
create->column_names = MXS_REALLOC(create->column_names, sizeof(char*) * create->columns + 1);
|
||||
create->column_types = MXS_REALLOC(create->column_types, sizeof(char*) * create->columns + 1);
|
||||
create->column_lengths = MXS_REALLOC(create->column_lengths, sizeof(int) * create->columns + 1);
|
||||
|
||||
if (tmp == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
create->column_names = tmp;
|
||||
char avro_token[len + 1];
|
||||
make_avro_token(avro_token, tok, len);
|
||||
char field_type[200] = ""; // Enough to hold all types
|
||||
int field_length = extract_type_length(tok + len, field_type);
|
||||
create->column_names[create->columns] = MXS_STRDUP_A(avro_token);
|
||||
create->column_types[create->columns] = MXS_STRDUP_A(field_type);
|
||||
create->column_lengths[create->columns] = field_length;
|
||||
create->columns++;
|
||||
updates++;
|
||||
tok = get_next_def(tok, end);
|
||||
@ -971,25 +974,22 @@ bool table_create_alter(TABLE_CREATE *create, const char *sql, const char *end)
|
||||
{
|
||||
tok = get_tok(tok + len, &len, end);
|
||||
|
||||
int idx = get_column_index(create, tok);
|
||||
int idx = get_column_index(create, tok, len);
|
||||
|
||||
if (idx != -1)
|
||||
{
|
||||
MXS_FREE(create->column_names[idx]);
|
||||
MXS_FREE(create->column_types[idx]);
|
||||
for (int i = idx; i < (int)create->columns - 1; i++)
|
||||
{
|
||||
create->column_names[i] = create->column_names[i + 1];
|
||||
create->column_types[i] = create->column_types[i + 1];
|
||||
create->column_lengths[i] = create->column_lengths[i + 1];
|
||||
}
|
||||
|
||||
char ** tmp = realloc(create->column_names, sizeof(char*) * create->columns - 1);
|
||||
ss_dassert(tmp);
|
||||
|
||||
if (tmp == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
create->column_names = tmp;
|
||||
create->column_names = MXS_REALLOC(create->column_names, sizeof(char*) * create->columns - 1);
|
||||
create->column_types = MXS_REALLOC(create->column_types, sizeof(char*) * create->columns - 1);
|
||||
create->column_lengths = MXS_REALLOC(create->column_lengths, sizeof(int) * create->columns - 1);
|
||||
create->columns--;
|
||||
updates++;
|
||||
}
|
||||
@ -1001,12 +1001,19 @@ bool table_create_alter(TABLE_CREATE *create, const char *sql, const char *end)
|
||||
{
|
||||
tok = get_tok(tok + len, &len, end);
|
||||
|
||||
int idx = get_column_index(create, tok);
|
||||
int idx = get_column_index(create, tok, len);
|
||||
|
||||
if (idx != -1)
|
||||
if (idx != -1 && (tok = get_tok(tok + len, &len, end)))
|
||||
{
|
||||
MXS_FREE(create->column_names[idx]);
|
||||
create->column_names[idx] = strndup(tok, len);
|
||||
MXS_FREE(create->column_types[idx]);
|
||||
char avro_token[len + 1];
|
||||
make_avro_token(avro_token, tok, len);
|
||||
char field_type[200] = ""; // Enough to hold all types
|
||||
int field_length = extract_type_length(tok + len, field_type);
|
||||
create->column_names[idx] = MXS_STRDUP_A(avro_token);
|
||||
create->column_types[idx] = MXS_STRDUP_A(field_type);
|
||||
create->column_lengths[idx] = field_length;
|
||||
updates++;
|
||||
}
|
||||
|
||||
@ -1021,7 +1028,7 @@ bool table_create_alter(TABLE_CREATE *create, const char *sql, const char *end)
|
||||
}
|
||||
|
||||
/** Only increment the create version if it has an associated .avro
|
||||
* file. The .avro file is only created if it is acutally used. */
|
||||
* file. The .avro file is only created if it is actually used. */
|
||||
if (updates > 0 && create->was_used)
|
||||
{
|
||||
create->version++;
|
||||
|
@ -505,7 +505,7 @@ closeSession(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session)
|
||||
static void log_closed_session(mysql_server_cmd_t mysql_command, bool is_closed,
|
||||
SERVER_REF *ref)
|
||||
{
|
||||
char msg[MAX_SERVER_NAME_LEN + 200] = ""; // Extra space for message
|
||||
char msg[MAX_SERVER_ADDRESS_LEN + 200] = ""; // Extra space for message
|
||||
|
||||
if (is_closed)
|
||||
{
|
||||
|
@ -1,4 +1,11 @@
|
||||
add_library(readwritesplit SHARED readwritesplit.c rwsplit_mysql.c rwsplit_route_stmt.c rwsplit_select_backends.c rwsplit_session_cmd.c rwsplit_tmp_table_multi.c)
|
||||
add_library(readwritesplit SHARED
|
||||
readwritesplit.cc
|
||||
rwsplit_mysql.cc
|
||||
rwsplit_route_stmt.cc
|
||||
rwsplit_select_backends.cc
|
||||
rwsplit_session_cmd.cc
|
||||
rwsplit_tmp_table_multi.cc
|
||||
rwsplit_ps.cc)
|
||||
target_link_libraries(readwritesplit maxscale-common MySQLCommon)
|
||||
set_target_properties(readwritesplit PROPERTIES VERSION "1.0.2")
|
||||
install_module(readwritesplit core)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user