Merge branch 'develop' into MXS-1266

This commit is contained in:
MassimilianoPinto 2017-06-29 11:31:56 +02:00
commit 4993fd683c
121 changed files with 4956 additions and 5528 deletions

View File

@ -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

View File

@ -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})

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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
View 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
View 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}")

View File

@ -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 "")

View File

@ -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
View 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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View 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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -5,7 +5,7 @@
"column": "a"
},
"with": {
"fill": "X"
"fill": "Y"
}
},
{
@ -22,7 +22,7 @@
},
"with": {
"value": "012345-ABCD",
"fill": "X"
"fill": "Y"
}
}
]

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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)
{

View File

@ -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");

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -1,6 +1,5 @@
add_subdirectory(qc_mysqlembedded)
add_subdirectory(qc_sqlite)
add_subdirectory(qc_dummy)
if(BUILD_TESTS)
add_subdirectory(test)

View File

@ -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)

View File

@ -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;
}
}

View File

@ -1,6 +0,0 @@
{
global:
mxs_get_module_object;
local:
*;
};

View File

@ -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 =

View File

@ -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;
}

View File

@ -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

View File

@ -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 =

View File

@ -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)

View 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;
}

View File

@ -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

View File

@ -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
View 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();
}

View File

@ -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)

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)] = "";

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -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))

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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;

View File

@ -40,7 +40,8 @@ typedef enum
TOK_MASTER,
TOK_SLAVE,
TOK_SERVER,
TOK_EOL
TOK_LINEBRK,
TOK_END
} TOKEN_VALUE;
/* The tokenising return type */

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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},
{

View File

@ -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 */
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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"

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 "

View File

@ -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)

View File

@ -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);
}

View File

@ -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()))

View File

@ -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()

View File

@ -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++;

View File

@ -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)
{

View File

@ -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