Merge branch 'develop' into MXS-1075
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
## About MariaDB MaxScale
|
||||
|
||||
- [About MariaDB MaxScale](About/About-MaxScale.md)
|
||||
- [Release Notes](Release-Notes/MaxScale-2.0.0-Release-Notes.md)
|
||||
- [Release Notes](Release-Notes/MaxScale-2.1.1-Release-Notes.md)
|
||||
- [Changelog](Changelog.md)
|
||||
- [Limitations](About/Limitations.md)
|
||||
|
||||
@ -19,10 +19,6 @@
|
||||
## Upgrading MariaDB MaxScale
|
||||
|
||||
- [Upgrading MariaDB MaxScale from 1.4 to 2.0](Upgrading/Upgrading-To-MaxScale-2.0.md)
|
||||
- [Upgrading MariaDB MaxScale from 1.3 to 1.4](Upgrading/Upgrading-To-MaxScale-1.4.md)
|
||||
- [Upgrading MariaDB MaxScale from 1.2 to 1.3](Upgrading/Upgrading-To-MaxScale-1.3.md)
|
||||
- [Upgrading MariaDB MaxScale from 1.1.1 to 1.2](Upgrading/Upgrading-To-MaxScale-1.2.md)
|
||||
- [Upgrading MariaDB MaxScale from 1.0.5 to 1.1.0](Upgrading/Upgrading-To-MaxScale-1.1.0.md)
|
||||
|
||||
## Reference
|
||||
|
||||
@ -129,20 +125,3 @@ document.
|
||||
- [DCB States (to be replaced in StarUML)](Design-Documents/DCB-States.pdf)
|
||||
- [Schema Sharding Router Technical Documentation](Design-Documents/SchemaRouter-technical.md)
|
||||
- [Plugin development guide](Design-Documents/Plugin-development-guide.md)
|
||||
|
||||
## Earlier Release Notes
|
||||
|
||||
- [MariaDB MaxScale 1.4.3 Release Notes](Release-Notes/MaxScale-1.4.3-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.4.2 Release Notes](Release-Notes/MaxScale-1.4.2-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.4.1 Release Notes](Release-Notes/MaxScale-1.4.1-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.4.0 Release Notes](Release-Notes/MaxScale-1.4.0-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.3.0 Release Notes](Release-Notes/MaxScale-1.3.0-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.2.0 Release Notes](Release-Notes/MaxScale-1.2.0-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.1.1 Release Notes](Release-Notes/MaxScale-1.1.1-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.1.0 Release Notes](Release-Notes/MaxScale-1.1-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.0.3 Release Notes](Release-Notes/MaxScale-1.0.3-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.0.1 Release Notes](Release-Notes/MaxScale-1.0.1-Release-Notes.md)
|
||||
- [MariaDB MaxScale 1.0 Release Notes](Release-Notes/MaxScale-1.0-Release-Notes.md)
|
||||
- [MariaDB MaxScale 0.7 Release Notes](Release-Notes/MaxScale-0.7-Release-Notes.md)
|
||||
- [MariaDB MaxScale 0.6 Release Notes](Release-Notes/MaxScale-0.6-Release-Notes.md)
|
||||
- [MariaDB MaxScale 0.5 Release Notes](Release-Notes/MaxScale-0.5-Release-Notes.md)
|
||||
|
@ -64,6 +64,10 @@ An optional parameter that can be used to control which statements trigger the
|
||||
statement re-routing. The parameter value is a regular expression that is used to
|
||||
match against the SQL text. Only non-SELECT statements are inspected.
|
||||
|
||||
```
|
||||
match=.*INSERT.*
|
||||
```
|
||||
|
||||
### `ignore`
|
||||
|
||||
An optional parameter that can be used to control which statements don't trigger
|
||||
@ -71,6 +75,10 @@ the statement re-routing. This does the opposite of the _match_ parameter. The
|
||||
parameter value is a regular expression that is used to match against the SQL
|
||||
text. Only non-SELECT statements are inspected.
|
||||
|
||||
```
|
||||
ignore=.*UPDATE.*
|
||||
```
|
||||
|
||||
## Example Configuration
|
||||
|
||||
Here is a minimal filter configuration for the CCRFilter which should solve most
|
||||
|
@ -10,17 +10,36 @@ The cache filter is a simple cache that is capable of caching the result of
|
||||
SELECTs, so that subsequent identical SELECTs are served directly by MaxScale,
|
||||
without the queries being routed to any server.
|
||||
|
||||
SELECTs using the following functions will not be cached: `BENCHMARK`,
|
||||
`CONNECTION_ID`, `CONVERT_TZ`, `CURDATE`, `CURRENT_DATE`, `CURRENT_TIMESTAMP`,
|
||||
`CURTIME`, `DATABASE`, `ENCRYPT`, `FOUND_ROWS`, `GET_LOCK`, `IS_FREE_LOCK`,
|
||||
`IS_USED_LOCK`, `LAST_INSERT_ID`, `LOAD_FILE`, `LOCALTIME`, `LOCALTIMESTAMP`,
|
||||
`MASTER_POS_WAIT`, `NOW`, `RAND`, `RELEASE_LOCK`, `SESSION_USER`, `SLEEP`,
|
||||
`SYSDATE`, `SYSTEM_USER`, `UNIX_TIMESTAMP`, `USER`, `UUID`, `UUID_SHORT`.
|
||||
The cache will be used and populated in the following circumstances:
|
||||
* There is _no_ explicit transaction active, that is, _autocommit_ is used,
|
||||
* there is an _explicitly_ read-only transaction (that is,`START TRANSACTION
|
||||
READ ONLY`) active, or
|
||||
* there is a transaction active and _no_ statement that modifies the database
|
||||
has been performed.
|
||||
|
||||
Note that installing the cache causes all statements to be parsed. The
|
||||
implication of that is that unless statements _already_ need to be parsed,
|
||||
e.g. due to the presence of another filter or the chosen router, then adding
|
||||
the cache will not necessarily improve the performance, but may decrease it.
|
||||
In practice, the last bullet point basically means that if a transaction has
|
||||
been started with `BEGIN`, `START TRANSACTION` or `START TRANSACTION READ
|
||||
WRITE`, then the cache will be used and populated until the first `UPDATE`,
|
||||
`INSERT` or `DELETE` statement is encountered.
|
||||
|
||||
By default, it is *ensured* that the cache is **not** used in the following
|
||||
circumstances:
|
||||
|
||||
* The `SELECT` uses any of the following functions: `BENCHMARK`,
|
||||
`CONNECTION_ID`, `CONVERT_TZ`, `CURDATE`, `CURRENT_DATE`, `CURRENT_TIMESTAMP`,
|
||||
`CURTIME`, `DATABASE`, `ENCRYPT`, `FOUND_ROWS`, `GET_LOCK`, `IS_FREE_LOCK`,
|
||||
`IS_USED_LOCK`, `LAST_INSERT_ID`, `LOAD_FILE`, `LOCALTIME`, `LOCALTIMESTAMP`,
|
||||
`MASTER_POS_WAIT`, `NOW`, `RAND`, `RELEASE_LOCK`, `SESSION_USER`, `SLEEP`,
|
||||
`SYSDATE`, `SYSTEM_USER`, `UNIX_TIMESTAMP`, `USER`, `UUID`, `UUID_SHORT`.
|
||||
* The `SELECT` accesses any of the following fields: `CURRENT_DATE`,
|
||||
`CURRENT_TIMESTAMP`, `LOCALTIME`, `LOCALTIMESTAMP`
|
||||
* The `SELECT` uses system or user variables.
|
||||
|
||||
In order to ensure that, all `SELECT` statements have to be parsed, which
|
||||
carries a _significant_ performance cost. If it is known that there are no
|
||||
such statements or that it does not matter even if they are cached, that
|
||||
safety measure can be turned off. Please read [performance](#performance)
|
||||
for more details.
|
||||
|
||||
## Limitations
|
||||
|
||||
@ -32,24 +51,6 @@ Currently there is **no** cache invalidation, apart from _time-to-live_.
|
||||
### Prepared Statements
|
||||
Resultsets of prepared statements are **not** cached.
|
||||
|
||||
### Transactions
|
||||
The cache will be used and populated in the following circumstances:
|
||||
|
||||
* There is _no_ explicit transaction active, that is, _autocommit_ is used,
|
||||
* there is an _explicitly_ read-only transaction (that is,`START TRANSACTION
|
||||
READ ONLY`) active, or
|
||||
* there is a transaction active and _no_ statement that modify the database
|
||||
has been performed.
|
||||
|
||||
In practice, the last bullet point basically means that if a transaction has
|
||||
been started with `BEGIN` or `START TRANSACTION READ WRITE`, then the cache
|
||||
will be used and populated until the first `UPDATE`, `INSERT` or `DELETE`
|
||||
statement is encountered.
|
||||
|
||||
### Variables
|
||||
If user or system variables are used in the _SELECT_ statement, the result
|
||||
will not be cached.
|
||||
|
||||
### Security
|
||||
The cache is **not** aware of grants.
|
||||
|
||||
@ -71,8 +72,6 @@ type=filter
|
||||
module=cache
|
||||
hard_ttl=30
|
||||
soft_ttl=20
|
||||
storage=...
|
||||
storage_options=...
|
||||
rules=...
|
||||
...
|
||||
|
||||
@ -95,10 +94,10 @@ sharing.
|
||||
|
||||
### Filter Parameters
|
||||
|
||||
The cache filter has one mandatory parameter - `storage` - and a few
|
||||
optional ones. Note that it is advisable to specify `max_size` to prevent
|
||||
the cache from using up all memory there is, in case there is very litte
|
||||
overlap among the queries.
|
||||
The cache filter has no mandatory parameters but a range of optional ones.
|
||||
Note that it is advisable to specify `max_size` to prevent the cache from
|
||||
using up all memory there is, in case there is very litte overlap among the
|
||||
queries.
|
||||
|
||||
#### `storage`
|
||||
|
||||
@ -108,6 +107,8 @@ argument. For instance:
|
||||
```
|
||||
storage=storage_inmemory
|
||||
```
|
||||
The default is `storage_inmemory`.
|
||||
|
||||
See [Storage](#storage-1) for what storage modules are available.
|
||||
|
||||
#### `storage_options`
|
||||
@ -227,6 +228,29 @@ cached_data=thread_specific
|
||||
Default is `shared`. See `max_count` and `max_size` what implication changing
|
||||
this setting to `thread_specific` has.
|
||||
|
||||
#### `selects`
|
||||
|
||||
An enumeration option specifying what approach the cache should take with
|
||||
respect to `SELECT` statements. The allowed values are:
|
||||
|
||||
* `assume_cacheable`: The cache can assume that all `SELECT` statements,
|
||||
without exceptions, are cacheable.
|
||||
* `verify_cacheable`: The cache can *not* assume that all `SELECT`
|
||||
statements are cacheable, but must verify that.
|
||||
|
||||
```
|
||||
select=assume_cacheable
|
||||
```
|
||||
|
||||
Default is `verify_cacheable`. In this case, the `SELECT` statements will be
|
||||
parsed and only those that are safe for caching - e.g. do *not* call any
|
||||
non-cacheable functions or access any non-cacheable variables - will be
|
||||
subject to caching.
|
||||
|
||||
If `assume_cacheable` is specified, then all `SELECT` statements are
|
||||
assumed to be cacheable and will be parsed *only* if some specific rule
|
||||
requires that.
|
||||
|
||||
#### `debug`
|
||||
|
||||
An integer value, using which the level of debug logging made by the cache
|
||||
@ -678,3 +702,99 @@ The rules specify that the data of the table `sbtest` should be cached.
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Performance
|
||||
|
||||
Perhaps the most significant factor affecting the performance of the cache is
|
||||
whether the statements need to be parsed or not. By default, all statements are
|
||||
parsed in order to exclude `SELECT` statements that use non-cacheable functions,
|
||||
access non-cacheable variables or refer to system or user variables.
|
||||
|
||||
If it is known that no such statements are used or if it does not matter if the
|
||||
results are cached, that safety measure can be turned off. To do that, add the
|
||||
following line to the cache configuration:
|
||||
```
|
||||
[MyCache]
|
||||
...
|
||||
selects=assume_cacheable
|
||||
```
|
||||
|
||||
With that configuration, the cache itself will not cause the statements to be
|
||||
parsed.
|
||||
|
||||
But note that even with `assume_cacheable` configured, a rule referring
|
||||
specifically to a _database_, _table_ or _column_ will still cause the
|
||||
statement to be parsed.
|
||||
|
||||
For instance, a simple rule like
|
||||
```
|
||||
{
|
||||
"store": [
|
||||
{
|
||||
"attribute": "database",
|
||||
"op": "=",
|
||||
"value": "db1"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
cannot be fulfilled without parsing the statement.
|
||||
|
||||
If the rule is instead expressed using a regular expression
|
||||
```
|
||||
{
|
||||
"store": [
|
||||
{
|
||||
"attribute": "query",
|
||||
"op": "like",
|
||||
"value": "FROM db1\\..*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
then the statement will again not be parsed.
|
||||
|
||||
However, even if regular expression matching performance wise is cheaper
|
||||
than parsing, it still carries a cost. In the following is a table with numbers
|
||||
giving a rough picture of the relative cost of different approaches.
|
||||
|
||||
In the table, _regexp match_ means that the cacheable statements
|
||||
were picked out using a rule like
|
||||
```
|
||||
{
|
||||
"attribute": "query",
|
||||
"op": "like",
|
||||
"value": "FROM dbname"
|
||||
}
|
||||
```
|
||||
while _exact match_ means that the cacheable statements were picked out using a
|
||||
rule like
|
||||
```
|
||||
{
|
||||
"attribute": "database",
|
||||
"op": "=",
|
||||
"value": "dbname"
|
||||
}
|
||||
```
|
||||
The exact match rule requires all statements to be parsed.
|
||||
|
||||
Note that the qps figures are only indicative.
|
||||
|
||||
| `selects` | Rule | qps |
|
||||
| -------------------| ---------------|-----|
|
||||
| `assume_cacheable` | none | 100 |
|
||||
| `assume_cacheable` | _regexp match_ | 98 |
|
||||
| `assume_cacheable` | _exact match_ | 60 |
|
||||
| `verify_cacheable` | none | 60 |
|
||||
| `verify_cacheable` | _regexp match_ | 58 |
|
||||
| `verify_cacheable` | _exact match_ | 58 |
|
||||
|
||||
## Summary
|
||||
|
||||
For maximum performance:
|
||||
* Arrange the situation so that `selects=assume_cacheable` can be
|
||||
configured, and use _no_ rules.
|
||||
* If `selects=assume_cacheable` has been configured, use _only_
|
||||
regexp based rules.
|
||||
* If `selects=verify_cacheable` has been configured non-regex based
|
||||
matching can be used.
|
||||
|
@ -5,7 +5,7 @@ requirements are as follows:
|
||||
|
||||
* CMake version 2.8 or later (Packaging requires version 2.8.12 or later)
|
||||
* GCC version 4.4.7 or later
|
||||
* libaio
|
||||
* SQLite3 version 3.3 or later
|
||||
* libcurl
|
||||
* OpenSSL
|
||||
* Bison 2.7 or later
|
||||
@ -20,33 +20,35 @@ The following packages are required on CentOS/RHEL 7. Older releases may require
|
||||
other packages in addition to these.
|
||||
|
||||
```
|
||||
git gcc gcc-c++ ncurses-devel bison flex glibc-devel cmake libgcc perl make libtool \
|
||||
openssl-devel libaio libaio-devel libcurl-devel pcre-devel tcl tcl-devel systemtap-sdt-devel libuuid libuuid-devel
|
||||
git gcc gcc-c++ ncurses-devel bison flex glibc-devel cmake libgcc perl make \
|
||||
libtool openssl openssl-devel libcurl-devel pcre-devel tcl tcl-devel \
|
||||
systemtap-sdt-devel libuuid libuuid-devel sqlite sqlite-devel
|
||||
```
|
||||
|
||||
You can install the packages with the following commands.
|
||||
|
||||
```
|
||||
sudo yum install git gcc gcc-c++ ncurses-devel bison flex glibc-devel cmake libgcc perl \
|
||||
make libtool openssl-devel libaio libaio-devel librabbitmq-devel \
|
||||
libcurl-devel pcre-devel tcl tcl-devel systemtap-sdt-devel libuuid libuuid-devel
|
||||
sudo yum install git gcc gcc-c++ ncurses-devel bison flex glibc-devel cmake \
|
||||
libgcc perl make libtool openssl openssl-devel libcurl-devel pcre-devel \
|
||||
tcl tcl-devel systemtap-sdt-devel libuuid libuuid-devel sqlite3 sqlite3-devel
|
||||
```
|
||||
|
||||
### Required packages on Ubuntu and Debian systems
|
||||
|
||||
The following packages are required on Ubuntu 14.04. Different releases may require
|
||||
other packages in addition to these.
|
||||
The following packages are required on Ubuntu 16.04. Different releases may
|
||||
require other packages in addition to these.
|
||||
|
||||
```
|
||||
git build-essential libssl-dev libaio-dev ncurses-dev bison flex \
|
||||
cmake perl libtool libcurl4-openssl-dev libpcre3-dev tlc tcl-dev uuid uuid-dev
|
||||
git build-essential libssl-dev ncurses-dev bison flex cmake perl libtool \
|
||||
libcurl4-openssl-dev libpcre3-dev tlc tcl-dev uuid uuid-dev sqlite3-dev
|
||||
```
|
||||
|
||||
You can install the packages with the following command.
|
||||
|
||||
```
|
||||
sudo apt-get install git build-essential libssl-dev libaio-dev ncurses-dev \
|
||||
bison flex cmake perl libtool libcurl4-openssl-dev libpcre3-dev tcl tcl-dev uuid uuid-dev
|
||||
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
|
||||
```
|
||||
|
||||
## Preparing the MariaDB MaxScale build
|
||||
@ -106,6 +108,11 @@ sudo make install
|
||||
|
||||
Other useful targets for Make are `documentation`, which generates the Doxygen documentation, and `uninstall` which uninstall MariaDB MaxScale binaries after an install.
|
||||
|
||||
**Note**: If you configure CMake multiple times, it's possible that you will run
|
||||
into problems when building MaxScale. Most of the time this manifests as a
|
||||
missing _pcre2.h_ header file. When this happens, delete everything in the
|
||||
build directory and run the CMake command again.
|
||||
|
||||
# Building MariaDB MaxScale packages
|
||||
|
||||
In addition to the packages needed to build MariaDB MaxScale, you will need the
|
||||
|
@ -442,6 +442,17 @@ files.
|
||||
execdir=/usr/local/bin/
|
||||
```
|
||||
|
||||
#### `connector_plugindir`
|
||||
|
||||
Location of the MariaDB Connector-C plugin directory. The MariaDB Connector-C
|
||||
used in MaxScale can use this directory to load authentication plugins. The
|
||||
versions of the plugins must be binary compatible with the connector version
|
||||
that MaxScale was built with.
|
||||
|
||||
```
|
||||
connector_plugindir=/usr/lib/plugin/
|
||||
```
|
||||
|
||||
#### `persistdir`
|
||||
|
||||
Configure the directory where persisted configurations are stored. When a new
|
||||
@ -875,6 +886,18 @@ Example:
|
||||
max_connections=100
|
||||
```
|
||||
|
||||
#### `max_retry_interval`
|
||||
|
||||
Configure the maximum interval between consecutive attempts to bind to an
|
||||
interface. The default value for this parameter is 3600 seconds. This
|
||||
parameter was introduced in MaxScale 2.2.0.
|
||||
|
||||
When a listener fails to bind to the interface it is assigned to, it will
|
||||
attempt to bind to the interface again after 10 seconds. If the attempt fails,
|
||||
the interval is incremented by 10 seconds and the next attempt will be in 20
|
||||
seconds. The interval is incremented until the value of `max_retry_interval` is
|
||||
reached at which point the listener attempts to bind to the interface every
|
||||
`max_retry_interval` seconds.
|
||||
|
||||
### Server
|
||||
|
||||
|
@ -200,6 +200,32 @@ external agent that automatically reintegrates failed servers into the
|
||||
cluster. One of these agents is the _replication-manager_ which automatically
|
||||
configures the failed servers as new slaves of the current master.
|
||||
|
||||
### `journal_max_age`
|
||||
|
||||
The maximum journal file age in seconds. The default value is 28800 seconds.
|
||||
|
||||
When the MySQL monitor starts, it reads any stored journal files. If the journal
|
||||
file is older than the value of _journal_max_age_, it will be removed and the
|
||||
monitor starts with no prior knowledge of the servers.
|
||||
|
||||
## MySQL Monitor Crash Safety
|
||||
|
||||
Starting with MaxScale 2.2.0, the mysqlmon module keeps an on-disk journal of
|
||||
the latest server states. This change makes the monitor crash-safe when options
|
||||
that introduce states are used. It also allows the monitor to retain stateful
|
||||
information when MaxScale is restarted.
|
||||
|
||||
Options that introduce states into the monitoring process are the
|
||||
`detect_stale_master` and `detect_stale_slave` options, both of which are
|
||||
enabled by default.
|
||||
|
||||
The default location for the server state journal is in
|
||||
`/var/lib/maxscale/<monitor name>/mysqlmon.dat` where `<monitor name>` is the
|
||||
name of the monitor section in the configuration file. If MaxScale crashes or is
|
||||
shut down in an uncontrolled fashion, the journal will be read when MaxScale is
|
||||
started. To skip the recovery process, manually delete the journal file before
|
||||
starting MaxScale.
|
||||
|
||||
## Example 1 - Monitor script
|
||||
|
||||
Here is an example shell script which sends an email to an admin when a server goes down.
|
||||
|
@ -1,336 +0,0 @@
|
||||
# MariaDB MaxScale 0.5 Alpha Release Notes
|
||||
|
||||
0.5 Alpha
|
||||
|
||||
This document details the changes in version 0.5 since the release of the 0.4 alpha of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
|
||||
## Read/Write Splitter Routing Module
|
||||
|
||||
In previous versions the read/write splitter routing module has had a number of limitations on it use, in the alpha release the router now removes the most important restrictions.
|
||||
|
||||
### Session Commands
|
||||
|
||||
Session commands are those statements that make some change to the user’s login session that may cause different effects from subsequent statements executed. Since the read/write splitter executes statements on either a master server or a slave server, depending upon the statement to execute, it is important that these session modifications are executed on all connections to both slave and master servers. This is resolved in release 0.5 such that session modification commands are executed on all active connections and a single return is forward back to the client that made the request.
|
||||
|
||||
### Transaction Support
|
||||
|
||||
Transaction support has been added into this version of the read/write splitter, there is one known outstanding limitation. If autocommit is enabled inside an active transaction it is not considered as commit in read/write splitter. Once a transaction has started all statements are routed to a master until the transaction is committed or rolled back.
|
||||
|
||||
## Authentication
|
||||
|
||||
A number of issues and shortcomings in the authentication performed by MaxScale have been resolved by this release.
|
||||
|
||||
### Host Considered in Authentication
|
||||
|
||||
Previously MaxScale did not follow the same rules as MySQL when authenticating a login request, it would always use the wildcard password entries and would not check the incoming host was allowed to connect. MaxScale now checks the incoming IP address for a connection request and verifies this against the authentication data loaded from the backend servers. The same rules are applied when choosing the password entry to authenticate with. Note however that authentication from MaxScale to the backend database will fail if the MaxScale host is not allowed to login using the matching password for the user.
|
||||
|
||||
### Stale Authentication Data
|
||||
|
||||
In previous releases of MaxScale the authentication data would be read at startup time only and would not be refreshed. Therefore if a user was added or modified in the backend server this will not be picked up by MaxScale and that user would be unable to connect via MaxScale. MaxScale now reloads user authentication data when a failure occurs and will refresh its internal tables if the data has changed in the backend. Please note that this reload process is rate limited to prevent incorrect logins to MaxScale being used for a denial of service attack on the backend servers.
|
||||
|
||||
### Enable Use Of "root" User
|
||||
|
||||
Previously MaxScale would prevent the use of the root user to login to the backend servers via MaxScale. This may be enabled on a per service basis by adding an "enable_root_user" options in the service entry to enable it in the MaxScale configuration file. This allows the use of root to be controlled on a per service basis.
|
||||
|
||||
## Network Support
|
||||
|
||||
### Unix Domain Sockets
|
||||
|
||||
MaxScale now supports Unix domain sockets for connecting to a local MaxScale server. The use of a Unix domain socket is controlled by adding a "socket" entry in the listener configuration entry for a service.
|
||||
|
||||
### Network Interface Binding
|
||||
|
||||
MaxScale has added the ability to bind a listener for a service to a network address via an "address" entry in the configuration file.
|
||||
|
||||
# Server Version
|
||||
|
||||
The server version reported when connected to a database via MaxScale has now been altered. This now shows the MaxScale name and version together with the backend server name. An example of this can be seen below for the 0.5 release.
|
||||
|
||||
-bash-4.1$ mysql -h 127.0.0.1 -P 4006 -uxxxx -pxxxx
|
||||
Welcome to the MariaDB monitor. Commands end with ; or \\g.
|
||||
Your MySQL connection id is 22320
|
||||
Server version: MaxScale 0.5.0 MariaDB Server
|
||||
|
||||
Copyright (c) 2000, 2012, Oracle, Monty Program Ab and others.
|
||||
|
||||
Type 'help;' or '\\h' for help. Type '\\c' to clear the current input statement.
|
||||
|
||||
MySQL [(none)]> \\ys
|
||||
--------------
|
||||
mysql Ver 15.1 Distrib 5.5.28a-MariaDB, for Linux (i686) using readline 5.1
|
||||
|
||||
...
|
||||
Server: MySQL
|
||||
Server version: MaxScale 0.5.0 MariaDB Server
|
||||
...
|
||||
--------------
|
||||
|
||||
MySQL [(none)]>
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.4 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.skysql.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>141</td>
|
||||
<td>No "delete user" command in debugcli</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>175</td>
|
||||
<td>Buffer leak in dcb_read from Coverity run</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>178</td>
|
||||
<td>Uninitialised variables from Coverity run</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>179</td>
|
||||
<td>open with O_CREAT in second argument needs 3 arguments</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>363</td>
|
||||
<td>simple_mutex "name" memory handling ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>126</td>
|
||||
<td>"reload config" in debug interface causes maxscale server to segfault</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>149</td>
|
||||
<td>It is possible to delete all maxscale users</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>218</td>
|
||||
<td>there is no way to understand what is going on if MAXSCALE_HOME is incorrect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>137</td>
|
||||
<td>"show users" and "reload users" refer to very different things in debugcli</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>154</td>
|
||||
<td>readwritesplit does not use router_options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>160</td>
|
||||
<td>telnetd leaks memory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>169</td>
|
||||
<td>Galera monitor is actually never compiled ....</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>172</td>
|
||||
<td>Several compile errors in galera_mon.c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>174</td>
|
||||
<td>Resource leak in server.c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>176</td>
|
||||
<td>Resource leak in gw_utils.c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>362</td>
|
||||
<td>possible datadir_cleanup() problems ...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>124</td>
|
||||
<td>readconnroute does not validate router_options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>153</td>
|
||||
<td>MaxScale fails when max connections are exceeded</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>133</td>
|
||||
<td>MaxScale leaves lots of "data<pid>" directories sitting around $MAXSCALE_HOME</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>166</td>
|
||||
<td>readwritesplit causes MaxScale segfault when starting up</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>207</td>
|
||||
<td>Quitting telnet session causes maxscale to fail</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>161</td>
|
||||
<td>Memory leak in load_mysql_users.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>177</td>
|
||||
<td>Resource leak in secrets.c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>182</td>
|
||||
<td>On Startup logfiles are empty</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>135</td>
|
||||
<td>MaxScale unsafely handles empty passwords in getUsers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>145</td>
|
||||
<td>.secret file for encrypted passwords cyclicly searched</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>171</td>
|
||||
<td>ifndef logic in build_gateway.inc doesn't work, MARIADB_SRC_PATH from env not picked up</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>173</td>
|
||||
<td>Resource leak in adminusers.c found by Coverity</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>376</td>
|
||||
<td>Confusing Server Version</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>370</td>
|
||||
<td>maxscale binary returns zero exit status on failures</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>150</td>
|
||||
<td>telnetd listener should bind to 127.0.0.1 by default</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>152</td>
|
||||
<td>listener configuration should support bind address</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>373</td>
|
||||
<td>Documentation: it's not clear what privileges the maxscale user needs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>128</td>
|
||||
<td>Maxscale prints debug information to terminal session when run in background</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>129</td>
|
||||
<td>MaxScale refuses to connect to server and reports nonsense error as a result</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>147</td>
|
||||
<td>Maxscale's hashtable fails to handle deletion of entries.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>148</td>
|
||||
<td>users data structure's stats have incorrect values.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>384</td>
|
||||
<td>MaxScale crashes if backend authentication fails</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>210</td>
|
||||
<td>Bad timing in freeing readconnrouter's dcbs cause maxscale crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>403</td>
|
||||
<td>gwbuf_free doesn't protect freeing shared buffer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>371</td>
|
||||
<td>If router module load fails, MaxScale goes to inifinite loop</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>385</td>
|
||||
<td>MaxScale (DEBUG-version) dasserts if backend dcb is closed in the middle of client dcb performing close_dcb</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>386</td>
|
||||
<td>Starting MaxScale with -c pointing at existing file causes erroneous behavior</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>209</td>
|
||||
<td>Error in backend hangs client connection</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>194</td>
|
||||
<td>maxscale crashes at start if module load fails</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>369</td>
|
||||
<td>typo in "QUERY_TYPE_UNKNWON"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>163</td>
|
||||
<td>MaxScale crashes with multiple threads</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>162</td>
|
||||
<td>threads parameter in configuration file is not effective</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>400</td>
|
||||
<td>hastable_get_stats returns value of uninitialized value in 'nelems'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>212</td>
|
||||
<td>Failing write causes maxscale to fail</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>222</td>
|
||||
<td>Double freeing mutex corrupts log</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>208</td>
|
||||
<td>current_connection_count is decreased multiple times per session, thus breaking load balancing logic</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>378</td>
|
||||
<td>Misspelling maxscale section name in config file crashes maxscale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>399</td>
|
||||
<td>Every row in log starts with 0x0A00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>205</td>
|
||||
<td>MaxScale crashes due SEGFAULT because return value of dcb_read is not checked</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>220</td>
|
||||
<td>Maxscale crash if socket listening fails in startup</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>372</td>
|
||||
<td>Log manager hangs MaxScale if log string (mostly query length) exceeds block size</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>397</td>
|
||||
<td>Free of uninitialised pointer if MAXSCALE_HOME is not set</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>402</td>
|
||||
<td>gw_decode_mysql_server_handshake asserts with mysql 5.1 backend</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>345</td>
|
||||
<td>MaxScale don't find backend servers if they are started after MaxScale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>406</td>
|
||||
<td>Memory leak in dcb_alloc()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>360</td>
|
||||
<td>MaxScale passwd option</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>151</td>
|
||||
<td>Get parse_sql failed on array INSERT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>216</td>
|
||||
<td>Backend error handling doesn't update server's connection counter</td>
|
||||
</tr>
|
@ -1,31 +0,0 @@
|
||||
# MariaDB MaxScale 0.6 Alpha Release Notes
|
||||
|
||||
0.6 Alpha
|
||||
|
||||
This document details the changes in version 0.6 since the release of the 0.5 alpha of the MaxScale product. The 0.6 version is merely a set of bug fixes based on the previous 0.5 version.
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.5 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.skysql.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>423</td>
|
||||
<td>The new "version_string" parameter has been added to service section.
|
||||
This allows a specific version string to be set for each service, this version string is used in the MySQL handshake from MaxScale to clients and is reported as the server version to clients.
|
||||
|
||||
The version_string is optional, the default value will be taken from the embedded MariaDB library which supplies the parser to MaxScale.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>418</td>
|
||||
<td>Statements are not routed to master if a transaction is started implicitly by setting autocommit=0. In such cases statements were previously routed as if they were not part of a transaction.
|
||||
|
||||
This fix changes the behavior so that is autocommit is disabled, all statements are routed to the master and in case of session variable updates, to both master and slave.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@ -1,158 +0,0 @@
|
||||
# MariaDB MaxScale 0.7 Alpha Release Notes
|
||||
|
||||
0.7 Alpha
|
||||
|
||||
This document details the changes in version 0.7 since the release of the 0.6 alpha of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
|
||||
## Galera Support
|
||||
|
||||
Enhanced support for Galera cluster to allow Galera to be used as a High Available Cluster with no write contention between the nodes..
|
||||
|
||||
MaxScale will control access to a Galera Cluster such that one node is designated as the master node to which all write operations will be sent. Read operations will be sent to any of the remaining nodes that are part of the cluster. Should the currently elected master node fail MaxScale will automatically promote one of the remaining nodes to become the new master node.
|
||||
|
||||
## Multiple Slave Connections
|
||||
|
||||
The Read/Write Split query router has been enhanced to allow multiple slaves connections to be created. The number of slave connections is configurable via a parameter in the MaxScale configuration file.
|
||||
|
||||
Adding multiple connections allows for better load balancing between the slaves and in a pre-requisite for providing improved fault tolerance within the Read/Write Splitter. The selection of which slave to use for a particular read operation can be controlled via options in the router configuration.
|
||||
|
||||
## Debug Interface Enhancements
|
||||
|
||||
A number of new list commands have been added to the debug interface to allow more concise tabular output of certain object types within the interface.
|
||||
|
||||
**MaxScale>** help list
|
||||
|
||||
Available options to the list command:
|
||||
|
||||
filters List all the filters defined within MaxScale
|
||||
|
||||
listeners List all the listeners defined within MaxScale
|
||||
|
||||
modules Show all currently loaded modules
|
||||
|
||||
services List all the services defined within MaxScale
|
||||
|
||||
servers List all the servers defined within MaxScale
|
||||
|
||||
sessions List all the active sessions within MaxScale
|
||||
|
||||
**MaxScale>**
|
||||
|
||||
Those objects that are defined in the configuration file can now be referenced by the names used in the configuration file rather than by using memory addresses. This means that services, servers, monitors and filters can all now be referenced using meaningful names provided by the user. Internal objects such as DCB’s and sessions, which are not named in the configuration file still require the use of memory addresses.
|
||||
|
||||
Two modes of operation of the interface are now available, user mode and developer mode. The user mode restricts access to the feature that allow arbitrary structures to be examined and checks all memory address for validity before allowing access.
|
||||
|
||||
## Maintenance Mode for Servers
|
||||
|
||||
MaxScale now provides a maintenance mode for servers, this mode allows servers to be set such that no new connections will be opened to that server. Also, servers in maintenance mode are not monitored by MaxScale. This allows an administrator to set a server into maintenance mode when it is required to be taken out of use. The connections will then diminish over time and since no new connections are created, the administrator can remove the node from use to perform some maintenance activities.
|
||||
|
||||
Nodes are placed into maintenance mode via the debug interface using the set server command.
|
||||
|
||||
**MaxScale>** set server datanode3 maintenance
|
||||
|
||||
Nodes are taken out of maintenance using the clear server command.
|
||||
|
||||
**MaxScale>** clear server datanode3 maintenance
|
||||
|
||||
## Configurable Monitoring Interval
|
||||
|
||||
All monitor plugins now provide a configuration parameter that can be set to control how frequently the MaxScale monitoring is performed.
|
||||
|
||||
## Replication Lag Heartbeat Monitor
|
||||
|
||||
The mysqlmon monitor module now implements a replication heartbeat protocol that is used to determine the lag between updates to the master and those updates being applied to the slave. This information is then made available to routing modules and may be used to determine if a particular slave node may be used or which slave node is most up to date.
|
||||
|
||||
## Filters API
|
||||
|
||||
The first phase of the filter API is available as part of this release. This provides filtering for the statements from the client application to the router. Filtering for the returned results has not yet been implemented and will be available in a future version.
|
||||
|
||||
Three example filters are including in the release
|
||||
|
||||
1. Statement counting Filter - a simple filter that counts the number of SQL statements executed within a session. Results may be viewed via the debug interface.
|
||||
|
||||
2. Query Logging Filter - a simple query logging filter that write all statements for a session into a log file for that session.
|
||||
|
||||
3. Query Rewrite Filter - an example of how filters can alter the query contents. This filter allows a regular expression to be defined, along with replacement text that should be substituted for every match of that regular expression.
|
||||
|
||||
## MariaDB 10 Replication Support
|
||||
|
||||
The myqlmon monitor module has been updated to support the new syntax for show all slaves status in MariaDB in order to correctly determine the master and slave state of each server being monitor. Determination of MariaDB 10 is automatically performed by the monitor and no configuration is required.
|
||||
|
||||
## API Versioning
|
||||
|
||||
The module interface has been enhanced to allow the API version in use to be reported, along with the status of the module and a short description of the module. The status allows for differentiation of the release status of a plugin to be identified independently of the core of MaxScale. plugins may be designated as "in development", “alpha”, “beta” or “GA”.
|
||||
|
||||
**MaxScale>** list modules
|
||||
|
||||
Module Name | Module Type | Version | API | Status
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
regexfilter | Filter | V1.0.0 | 1.0.0 | Alpha
|
||||
|
||||
MySQLBackend | Protocol | V2.0.0 | 1.0.0 | Alpha
|
||||
|
||||
telnetd | Protocol | V1.0.1 | 1.0.0 | Alpha
|
||||
|
||||
MySQLClient | Protocol | V1.0.0 | 1.0.0 | Alpha
|
||||
|
||||
mysqlmon | Monitor | V1.2.0 | 1.0.0 | Alpha
|
||||
|
||||
readwritesplit | Router | V1.0.2 | 1.0.0 | Alpha
|
||||
|
||||
readconnroute | Router | V1.0.2 | 1.0.0 | Alpha
|
||||
|
||||
debugcli | Router | V1.1.1 | 1.0.0 | Alpha
|
||||
|
||||
**MaxScale>**
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.6 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.skysql.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>443</td>
|
||||
<td>mysql/galera monitors hang when backend fails</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>424</td>
|
||||
<td>Read/Write Splitter closes connection without sending COM_QUIT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>438</td>
|
||||
<td>Internal thread deadlock</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>436</td>
|
||||
<td>Sessions in invalid state</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>359</td>
|
||||
<td>Router options for Read/Write Split module</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>435</td>
|
||||
<td>Some automated tests have invalid SQL syntax</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>431</td>
|
||||
<td>rwsplit.sh test script has incorrect bash syntax</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>425</td>
|
||||
<td>MaxScale crashes after prolonged use</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Linking
|
||||
|
||||
Following reported issues with incompatibilities between MaxScale and the shared library used by MySQL this version of MaxScale will be statically linked with the MariaDB 5.5 embedded library that it requires. This library is used for internal purposes only and does not result in MaxScale support for other versions of MySQL or MariaDB being affected.
|
||||
|
@ -1,124 +0,0 @@
|
||||
# MariaDB MaxScale 1.0 Beta Release Notes
|
||||
|
||||
1.0 Beta
|
||||
|
||||
This document details the changes in version 1.0 since the release of the 0.7 alpha of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
|
||||
## Complex Replication Structures
|
||||
|
||||
The MaxScale monitor module for Master/Slave replication is now able to correctly identify tree structured replication environments and route write statements to the master server at the root level of the tree. Isolated database instances and now also correctly identified as external to the replication tree.
|
||||
|
||||
## Read/Write Splitter Enhancements
|
||||
|
||||
### Support For Prepared Statements
|
||||
|
||||
Prepared statements are now correctly recognized by MaxScale, with the prepare stage being sent to all the eligible servers that could eventually run the statement. Statements are then execute on a single server.
|
||||
|
||||
### Slave Failure Resilience
|
||||
|
||||
The Read/Write splitter can not be used to establish multiple connections to different slave servers. The read load will be distributed across these slaves and slave failure will be masked from the application as MaxScale will automatically failover to another slave when one fails.
|
||||
|
||||
### Configurable Load Balancing Options
|
||||
|
||||
It is now possible to configure the criteria that the Read/Write Splitter uses for load balancing, the options are:
|
||||
|
||||
* The total number of connections to the servers, from this MaxScale instance
|
||||
|
||||
* The number of connections to the server for this particular MaxScale service
|
||||
|
||||
* The number of statements currently being executed on the server on behalf of this MaxScale instance
|
||||
|
||||
* Route statements to the slave that has the least replication lag
|
||||
|
||||
### Replication Consistency
|
||||
|
||||
The Read/Write splitter may now be configured to exclude nodes that are currently showing a replication lag greater than a configurable threshold. The replication lag is measured using the MySQL Monitor module of MaxScale.
|
||||
|
||||
Alternatively it is possible to define that read operations should be routed to the slave that has the least measured replication lag.
|
||||
|
||||
## Weighted Routing Options
|
||||
|
||||
The distribution of connections and statement across the set of nodes can be controlled by attaching arbitrary parameters to the servers and then configuring the router to use that parameter value as a weighting factor when deciding which of the valid servers to which to connect or route queries.
|
||||
|
||||
Several parameters may be used on each host and different routers may choose to use different parameters as the weighting parameter for that router. The use of weighting is optional, if no weighting parameter is given in the service definition then all eligible servers will have an equal distribution applied.
|
||||
|
||||
Server weighting is supported by both the Read/Write Splitter and the connection router.
|
||||
|
||||
## MaxAdmin Client
|
||||
|
||||
A new administrative interface has been added that uses a MaxScale specific client application to interact with MaxScale to control and monitor the MaxScale activities. This client application may be used interactively or within scripts, passing commands to MaxScale via command line arguments. Command scripts are available, allowing command sets of commands to be stored in script files.
|
||||
|
||||
MaxAdmin also supports command history via libedit on those distributions that support the libedit library. This allows for the use of the up and down cursor keys or selection of previous commands and editing of lines using vi or emacs style editing commands.
|
||||
|
||||
## Pacemaker Support
|
||||
|
||||
MaxScale now ships with an init.d script that is compatible with the use of Pacemaker and Heartbeat to provide for a highly available implementation of MaxScale. A tutorial on setting up MaxScale under Pacemaker control is included in the Documentation directory.
|
||||
|
||||
## Filter API Enhancements
|
||||
|
||||
The filter API has now been enhanced to operate not just on downstream query filtering but also upstream result set filtering.
|
||||
|
||||
## Enhanced and New Filters
|
||||
|
||||
Addition of new filters and enhancements to those existing filters that appeared in 0.7 of MaxScale.
|
||||
|
||||
### Top Filter
|
||||
|
||||
A new filter to capture and log the longest running queries within a client session. The filter can be configured to capture a specific number of queries that take the longest time between the query being submitted to the database server and the first result being returned.
|
||||
|
||||
The queries captured can be defined using regular expressions to include and exclude queries that match these expressions. In addition the inclusion of a session may be based on the user name used to connect to the database or the source address of the client session.
|
||||
|
||||
### Tee Filter
|
||||
|
||||
A filter to optionally duplicate requests received from the client and send them to other services within MaxScale. This allows a single statement sent by a client to be routed to multiple storage backends via MaxScale.
|
||||
|
||||
The queries duplicated can be defined using regular expressions to include and exclude queries that match these expressions. In addition the inclusion of a session may be based on the user name used to connect to the database or the source client session.
|
||||
|
||||
### QLA and Regex Filter Improvements
|
||||
|
||||
These filters have been enhanced to provide for the inclusion of sessions by specifying the username used to connect to the database or the source of the client connection as a criteria to trigger the use of these filters for particular sessions connected to the MaxScale service.
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.6 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.skysql.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>441</td>
|
||||
<td>Possible failure to return a value in setipaddress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>396</td>
|
||||
<td>Build instruction suggest forcing install of RPM’s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>452</td>
|
||||
<td>Make install copies the modules to an incorrect directory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>450</td>
|
||||
<td>Read/Write splitter does not balance load between multiple slaves</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>449</td>
|
||||
<td>The router clientReply function does not handle GWBUF structures correctly</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5 RPM
|
||||
|
||||
* CentOS/RedHat 6 RPM
|
||||
|
||||
* Ubuntu 14.04 package
|
||||
|
@ -1,332 +0,0 @@
|
||||
# MariaDB MaxScale 1.0.1 Beta Release Notes
|
||||
|
||||
1.0.1 Beta
|
||||
|
||||
This document details the changes in version 1.0.1 since the release of the 1.0 beta of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
|
||||
## CMake build system
|
||||
|
||||
Building MaxScale is now easier than ever thanks to the introduction of CMake into the build process. Building with CMake removes the need to edit files, specify directory locations or change build flags, in all but the rarest of the cases, and building with non-standard configurations is a lot easier thanks to the easy configuration of all the build parameters.
|
||||
|
||||
Here’s a short list of the most common build parameters,their functions and default values.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Variable</td>
|
||||
<td>Purpose</td>
|
||||
<td>Default value</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INSTALL_DIR</td>
|
||||
<td>Root location of the MaxScale install</td>
|
||||
<td>/usr/local/skysql/maxscale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>STATIC_EMBEDDED</td>
|
||||
<td>Whether to use the static or the dynamic version of the embedded library</td>
|
||||
<td>No</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OLEVEL</td>
|
||||
<td>Level of optimization used when compiling</td>
|
||||
<td>No optimization</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INSTALL_SYSTEM_FILES</td>
|
||||
<td>If startup scripts should be installed into /etc/init.d and ldconfig configuration files to /etc/ld.so.conf.d</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>BUILD_TYPE</td>
|
||||
<td>The type of the build. ‘None’ for normal, ‘Debug’ for debugging and ‘Optimized’ for an optimized build.</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
Details on all the configurable parameters and instructions on how to use CMake can be found in the README file.
|
||||
|
||||
## Enhancements
|
||||
|
||||
The polling mechanism in MaxScale has been modified to overcome a flaw which mean that connections with a heavy I/O load could starve other connections within MaxScale and prevent query execution. This has been resolved with a more fairer event scheduling mechanism within the MaxScale polling subsystem. This has led to improve overall performance in high load situations.
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 1.0 beta release and this release candidate. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.skysql.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>462</td>
|
||||
<td>Testall target fails in server/test to invalid MAXSCALE_HOME path specification</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>467</td>
|
||||
<td>max_slave_replication lag is not effective after session creation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>468</td>
|
||||
<td>query_classifier : if parsing fails, parse tree and thread context are freed but used</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>469</td>
|
||||
<td>rwsplit counts every connection twice in master - connection counts leak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>466</td>
|
||||
<td>hint_next_token doesn't detect <param>=<value> pair if there are no spaces around '='</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>470</td>
|
||||
<td>Maxscale crashes after a normal query if a query with named hint was used before</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>473</td>
|
||||
<td>Entering a hint with route server target as '=(' causes a crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>472</td>
|
||||
<td>Using a named hint after its initial use causes a crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>471</td>
|
||||
<td>Routing Hints route to server sometimes doesn't work</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>463</td>
|
||||
<td>MaxScale hangs receiving more than 16K in input</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>476</td>
|
||||
<td>mysql_common.c:protocol_archive_srv_command leaks memory and accesses freed memory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>479</td>
|
||||
<td>Undefined filter reference in maxscale.cnf causes a crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>410</td>
|
||||
<td>maxscale.cnf server option is not parsed for spaces</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>417</td>
|
||||
<td>Galera monitor freezes on network failure of a server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>488</td>
|
||||
<td>SHOW VARIABLES randomly failing with "Lost connection to MySQL server"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>484</td>
|
||||
<td>Hashtable does not always release write lock during add</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>485</td>
|
||||
<td>Hashtable not locked soon enough in iterator get next item</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>493</td>
|
||||
<td>Can have same section name multiple times without warning</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>510</td>
|
||||
<td>Embedded library crashes on a call to free_embedded_thd</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>511</td>
|
||||
<td>Format strings in log_manager.cc should be const char*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>509</td>
|
||||
<td>rw-split sensitive to order of terms in field list of SELECT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>507</td>
|
||||
<td>rw-split router does not send last_insert_id() to master</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>490</td>
|
||||
<td>session handling for non-determinstic user variables broken</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>489</td>
|
||||
<td>@@hostname and @@server_id treated differently from @@wsrep_node_address</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>528</td>
|
||||
<td>Wrong service name in tee filter crashes maxscale on connect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>530</td>
|
||||
<td>MaxScale socket permission</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>536</td>
|
||||
<td>log_manager doesn't write buffers to disk in the order they are written</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>447</td>
|
||||
<td>Error log is flooded with same warning if there are no slaves present</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>475</td>
|
||||
<td>The end comment tag in hints isn't properly detected.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>181</td>
|
||||
<td>Missing log entry if server not reachable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>486</td>
|
||||
<td>Hashtable problems when created with size less than one</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>516</td>
|
||||
<td>maxadmin CLI client sessions are not closed?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>495</td>
|
||||
<td>Referring to a nonexisting server in servers=... doesn't even raise a warning</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>538</td>
|
||||
<td>maxscale should expose details of "Down" server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>539</td>
|
||||
<td>MaxScale crashes in session_setup_filters</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>494</td>
|
||||
<td>The service 'CLI' is missing a definition of the servers that provide the service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>180</td>
|
||||
<td>Documentation: No information found in the documentation about firewall settings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>524</td>
|
||||
<td>Connecting to MaxScale from localhost tries matching @127.0.0.1 grant</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>481</td>
|
||||
<td>MySQL monitor doesn't set master server if the replication is broken</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>437</td>
|
||||
<td>Failure to detect MHA master switch</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>541</td>
|
||||
<td>Long queries cause MaxScale to block</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>492</td>
|
||||
<td>In dcb.c switch fallthrough appears to be used without comment</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>439</td>
|
||||
<td>Memory leak in getUsers</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>545</td>
|
||||
<td>RWSplit: session modification commands weren't routed to all if executed inside open transaction</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>543</td>
|
||||
<td>RWSplit router statistics counters are not updated correctly</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>544</td>
|
||||
<td>server with weight=0 gets one connection</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>525</td>
|
||||
<td>Crash when saving post in Wordpress</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>533</td>
|
||||
<td>Drupal installer hangs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>497</td>
|
||||
<td>Can’t enable debug/trace logs in configuration file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>430</td>
|
||||
<td>Temporary tables not working in MaxScale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>527</td>
|
||||
<td>No signal handler for segfault etc</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>546</td>
|
||||
<td>Use of weightby router parameter causes error log write</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>506</td>
|
||||
<td>Don’t write shm/tmpfs by default without telling the user or giving a way to override it</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>552</td>
|
||||
<td>Long argument options to maxadmin and maxscale broke maxadmin commands</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>521</td>
|
||||
<td>Many commands in maxadmin client simply hang</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>478</td>
|
||||
<td>Parallel session command processing fails</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>499</td>
|
||||
<td>make clean leavessoem .o files behind</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>500</td>
|
||||
<td>"depend: no such file warnings during make</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>501</td>
|
||||
<td>log_manager, query classifier rebuilds unconditionally</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>502</td>
|
||||
<td>log_manager and query_classifier builds always rebuild utils</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>504</td>
|
||||
<td>clean rule for Documentation directory in wrong makefile</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>505</td>
|
||||
<td>utils/makefile builds stuff unconditionally, misses "depend" target</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>548</td>
|
||||
<td>MaxScale accesses freed client DCB and crashes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>550</td>
|
||||
<td>modutil functions process length incorrectly</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5 RPM
|
||||
|
||||
* CentOS/RedHat 6 RPM
|
||||
|
||||
* Ubuntu 14.04 package
|
||||
|
@ -1,136 +0,0 @@
|
||||
# MariaDB MaxScale 1.0.3 Release Notes
|
||||
|
||||
1.0.3 GA
|
||||
|
||||
This document details the changes in version 1.0.3 since the release of the 1.0.2 Release Candidate of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
|
||||
No new features have been introduced since the released candidate was released.
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.6 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.mariadb.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>644</td>
|
||||
<td>Buffered that were cloned using the gwbuf_clone routine failed to initialise the buffer lock structure correctly.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>643</td>
|
||||
<td>Recursive filter definitions in the configuration file could cause MaxScale to loop</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>665</td>
|
||||
<td>An access to memory that had already been freed could be made within the MaxScale core</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>664</td>
|
||||
<td>MySQL Authentication code could access memory that had already been freed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>673</td>
|
||||
<td>MaxScale could crash if it had an empty user table and the MaxAdmin show dbusers command was run</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>670</td>
|
||||
<td>The tee filter could lose statement on the branch service if the branch service was significantly slower at executing statements compared with the main service.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>653</td>
|
||||
<td>Memory corruption could occur with extremely long hostnames in the mysql.user table.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>657</td>
|
||||
<td>If the branch service of a tee filter shutdown unexpectedly then MaxScale could fail</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>654</td>
|
||||
<td>Missing quotes in MaxAdmin show dbusers command could cause MaxAdmin to crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>677</td>
|
||||
<td>A race condition existed in the tee filter client reply handling</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>658</td>
|
||||
<td>The readconnroute router did not correctly close sessions when a backend database failed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>662</td>
|
||||
<td>MaxScale startup hangs if no backend servers respond</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>676</td>
|
||||
<td>MaxScale writes a log entry, "Write to backend failed. Session closed." when changing default database via readwritesplit with max_slave_connections != 100%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>650</td>
|
||||
<td>Tee filter does not correctly detect missing branch service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>645</td>
|
||||
<td>Tee filter can hang MaxScale if the read/write splitter is used</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>678</td>
|
||||
<td>Tee filter does not always send full query to branch service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>679</td>
|
||||
<td>A shared pointer in the service was leading to misleading service states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>680</td>
|
||||
<td>The Read/Write Splitter can not load users if there are no databases available at startup</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>681</td>
|
||||
<td>The Read/Write Splitter could crash is the value of max_slave_connections was set to a low percentage and only a small number of backend servers are available</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# Known Issues
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below.
|
||||
|
||||
* The SQL construct "LOAD DATA LOCAL INFILE" is not fully supported.
|
||||
|
||||
* The Read/Write Splitter is a little too strict when it receives errors from slave servers during execution of session commands. This can result in sessions being terminated in situation in which MaxScale could recover without terminating the sessions.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
# Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 13.10
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* Fedora 19
|
||||
|
||||
* Fedora 20
|
||||
|
||||
* OpenSuSE 13
|
||||
|
@ -1,140 +0,0 @@
|
||||
# MariaDB MaxScale 1.0.4 Release Notes
|
||||
|
||||
1.0.4 GA
|
||||
|
||||
This document details the changes in version 1.0.4 since the release of the 1.0.2 Release Candidate of the MaxScale product.
|
||||
|
||||
## New Features
|
||||
|
||||
No new features have been introduced since the released candidate was released.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 0.6 alpha and this alpha release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.mariadb.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>644</td>
|
||||
<td>Buffered that were cloned using the gwbuf_clone routine failed to initialise the buffer lock structure correctly.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>643</td>
|
||||
<td>Recursive filter definitions in the configuration file could cause MaxScale to loop</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>665</td>
|
||||
<td>An access to memory that had already been freed could be made within the MaxScale core</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>664</td>
|
||||
<td>MySQL Authentication code could access memory that had already been freed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>673</td>
|
||||
<td>MaxScale could crash if it had an empty user table and the MaxAdmin show dbusers command was run</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>670</td>
|
||||
<td>The tee filter could lose statement on the branch service if the branch service was significantly slower at executing statements compared with the main service.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>653</td>
|
||||
<td>Memory corruption could occur with extremely long hostnames in the mysql.user table.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>657</td>
|
||||
<td>If the branch service of a tee filter shutdown unexpectedly then MaxScale could fail</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>654</td>
|
||||
<td>Missing quotes in MaxAdmin show dbusers command could cause MaxAdmin to crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>677</td>
|
||||
<td>A race condition existed in the tee filter client reply handling</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>658</td>
|
||||
<td>The readconnroute router did not correctly close sessions when a backend database failed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>662</td>
|
||||
<td>MaxScale startup hangs if no backend servers respond</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>676</td>
|
||||
<td>MaxScale writes a log entry, "Write to backend failed. Session closed." when changing default database via readwritesplit with max_slave_connections != 100%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>650</td>
|
||||
<td>Tee filter does not correctly detect missing branch service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>645</td>
|
||||
<td>Tee filter can hang MaxScale if the read/write splitter is used</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>678</td>
|
||||
<td>Tee filter does not always send full query to branch service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>679</td>
|
||||
<td>A shared pointer in the service was leading to misleading service states</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>680</td>
|
||||
<td>The Read/Write Splitter can not load users if there are no databases available at startup</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>681</td>
|
||||
<td>The Read/Write Splitter could crash is the value of max_slave_connections was set to a low percentage and only a small number of backend servers are available</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## Known Issues
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below.
|
||||
|
||||
* The SQL construct "LOAD DATA LOCAL INFILE" is not fully supported.
|
||||
|
||||
* The Read/Write Splitter is a little too strict when it receives errors from slave servers during execution of session commands. This can result in sessions being terminated in situation in which MaxScale could recover without terminating the sessions.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
# Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 13.10
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* Fedora 19
|
||||
|
||||
* Fedora 20
|
||||
|
||||
* OpenSuSE 13
|
||||
|
||||
# MaxScale Home Default Value
|
||||
|
||||
The installation assumes that the default value for the environment variable MAXSCALE_HOME is set to /usr/local/mariadb/maxscale. This is hard coded in the service startup file that is placed in /etc/init.d/maxscale by the installation process.
|
||||
|
@ -1,113 +0,0 @@
|
||||
# MariaDB MaxScale 1.0.5 Release Notes
|
||||
|
||||
This document details the changes in version 1.0.5 since the release of the 1.0.4 GA of the MaxScale product.
|
||||
|
||||
# New Features
|
||||
No new features have been introduced since the GA version was released. SuSE Enterprise 11 and 12 packages are now also supplied.
|
||||
|
||||
# Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 1.0.4 initial GA release and this GA release. The table below lists the bugs that have been resolved. The details for each of these may be found in bugs.mariadb.com.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>519</td>
|
||||
<td>LOAD DATA LOCAL INFILE not handled?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>714</td>
|
||||
<td>Error log flooded when too many connect errors causes the MaxScale host to be blocked</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>711</td>
|
||||
<td>Some MySQL Workbench Management actions hang with R/W split router</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>710</td>
|
||||
<td>make package install files in /etc/init.d</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>683</td>
|
||||
<td>Check for unsupported version of MariaDB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>684</td>
|
||||
<td>Use mysql_config to determine include/lib directory paths and compiler options</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>689</td>
|
||||
<td>cmake DCMAKE_INSTALL_PREFIX has no effect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>701</td>
|
||||
<td>set server <svr> maint fails on the command line</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>705</td>
|
||||
<td>Authentication fails when the user connects to a database with the SQL mode including ANSI_QUOTES</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>507</td>
|
||||
<td>R/W split does not send last_insert_id() to the master</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>700</td>
|
||||
<td>maxscale version has no output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>694</td>
|
||||
<td>RWSplit SELECT @a:=@a+1 as a, test.b from test breaks client session</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>685</td>
|
||||
<td>SELECT against readconnrouter fails when large volumes of data are returned and the tee filter is used</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Known Issues
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below.
|
||||
|
||||
* The Read/Write Splitter is a little too strict when it receives errors from slave servers during execution of session commands. This can result in sessions being terminated in situation in which MaxScale could recover without terminating the sessions.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
# Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 13.10
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* Fedora 19
|
||||
|
||||
* Fedora 20
|
||||
|
||||
* OpenSuSE 13
|
||||
|
||||
* SuSE Enterprise 11
|
||||
|
||||
* SuSE Enterprise 12
|
||||
|
||||
# MaxScale Home Default Value
|
||||
|
||||
The installation assumes that the default value for the environment variable MAXSCALE_HOME is set to /usr/local/skysql/maxscale. This is hard coded in the service startup file that is placed in /etc/init.d/maxscale by the installation process.
|
@ -1,284 +0,0 @@
|
||||
# MariaDB MaxScale 1.1 Release Notes
|
||||
|
||||
## 1.1 GA
|
||||
|
||||
This document details the changes in version 1.1 since the release of the 1.0.5 GA Release of the MaxScale product.
|
||||
|
||||
## New Features
|
||||
|
||||
### High Performance Binlog Relay
|
||||
Replicate Binlog from the master to slave through MaxScale as simplified relay server for reduced network load and disaster recovery
|
||||
|
||||
### Database Firewall Filter
|
||||
Block queries based on columns in the query, where condition, query type(select, insert, delete, update), presence of wildcard in column selection, regular expression match and time of the query
|
||||
|
||||
### Schema Sharding Router
|
||||
Route to databases sharded by schema without application level knowledge of shard configuration
|
||||
|
||||
### Hint based routing
|
||||
Pass hints in the SQL statement to influence the routing decision based on replication lag or time out
|
||||
|
||||
### Named Server Routing
|
||||
Routing to a named server if incoming query matches a regular expression
|
||||
|
||||
### Canonical Query logging
|
||||
Convert incoming queries to canonical form and push the query and response into RabbitMQ Broker for a RabbitMQ Client to later retrieve from
|
||||
|
||||
### Nagios Plugin
|
||||
Plugin scripts for monitoring MaxScale status and performance from a Nagios Server
|
||||
|
||||
### Notification Service
|
||||
Receive notification of security update and patches tailored to your MaxScale configuration
|
||||
|
||||
### MySQL NDB cluster support
|
||||
Connection based routing to MySQL NDB clusters
|
||||
|
||||
### Updated installation path
|
||||
MaxScale is now installed into `/usr/local/mariadb-maxscale`
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
A number of bug fixes have been applied between the 1.0.5 GA and this GA release. The table below lists the bugs that have been resolved. The details for each of these may be found in https://jira.mariadb.org/projects/MXS or in the former http://bugs.mariadb.com Bug database
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>Summary</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-80</td>
|
||||
<td>"show sessions" can crash MaxScale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-79</td>
|
||||
<td>schemarouter hangs if client connects with empty database</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-78</td>
|
||||
<td>"USE" statement gives unpredictable/unexpected results</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-76</td>
|
||||
<td>core/dbusers.c needs better error messages</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-74</td>
|
||||
<td>Crash when no arguments given to on_queries clause</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-72</td>
|
||||
<td>dbfwfilter on_queries clause appears to be ignored</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-71</td>
|
||||
<td>dbfwfilter at_times clause seems to erroneously block user</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-68</td>
|
||||
<td>Wrong rule name in dbfwfilter leads to MaxScale crash</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-65</td>
|
||||
<td>Omitting <any|all|strict_all> in users directive causes crash in libdbfwfilter.so(link_rules)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-63</td>
|
||||
<td>Maxkeys and Maxpasswd log to /tpm</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-57</td>
|
||||
<td>MaxScale should write a message to the error log when config is not found</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-54</td>
|
||||
<td>Write failed auth attempt to trace log</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-50</td>
|
||||
<td>Removing 1.0.5 RPM gives error about /etc/ld.so.conf.d/maxscale.conf</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-47</td>
|
||||
<td>Session freeze when small tail packet</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>MXS-5</td>
|
||||
<td>Possible memory leak in readwritesplit router</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>736</td>
|
||||
<td>Memory leak while doing read/write splitting</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>733</td>
|
||||
<td>Init-script deletes bin/maxscale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>732</td>
|
||||
<td>Build is broken: CentOS/RHEL 5 and SLES 11</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>730</td>
|
||||
<td>Regex filter and shorter than original replacement queries MaxScale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>729</td>
|
||||
<td>PDO prepared statements bug introduced in Maxscale 1.0.5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>721</td>
|
||||
<td>Documentation suggests SIGTERM to re-read config file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>716</td>
|
||||
<td>$this->getReadConnection()->query('SET @id = 0;');</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>709</td>
|
||||
<td>"COPYRIGHT LICENSE README SETUP" files go to /usr/local/mariadb-maxscale/ after 'make package'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>704</td>
|
||||
<td>"make testall" returns success status (exit code 0) even on failures</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>698</td>
|
||||
<td>Using invalid parameter in many maxadmin commands causes MaxScale to fail</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>693</td>
|
||||
<td>Freeing tee filter's orphaned sessions causes a segfault when embedded server closes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>690</td>
|
||||
<td>CPU/architecture is hardcoded into debian/rules</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>686</td>
|
||||
<td>TestService fails because of the modules used in it aren't meant for actual use</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>677</td>
|
||||
<td>Race condition in tee filter clientReply</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>676</td>
|
||||
<td>"Write to backend failed. Session closed." when changing default database via readwritesplit with max_slave_connections != 100%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>673</td>
|
||||
<td>MaxScale crashes if "Users table data" is empty and "show dbusers" is executed in maxadmin</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>670</td>
|
||||
<td>Tee filter: statement router loses statements when other router gets enough ahead</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>665</td>
|
||||
<td>Core: accessing freed memory when session is closed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>659</td>
|
||||
<td>MaxScale doesn't shutdown if none of the configured services start</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>648</td>
|
||||
<td>use database is sent forever with tee filter to a readwrite split service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>620</td>
|
||||
<td>enable_root_user=true generates errors to error log</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>612</td>
|
||||
<td>Service was started although no users could be loaded from database</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>600</td>
|
||||
<td>RWSplit: if session command fails in some backend, it is not dropped from routing session</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>587</td>
|
||||
<td>Hint filter don't work if listed before regex filter in configuration file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>579</td>
|
||||
<td>serviceStartProtocol test crashes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>506</td>
|
||||
<td>Don't write to shm/tmpfs by default without telling and without a way to override it</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>503</td>
|
||||
<td>TOC in the bundled PDFs doesn't link to actual sections</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>457</td>
|
||||
<td>Please provide a list of build dependencies for building MaxScale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>361</td>
|
||||
<td>file_exists() *modifies* the file it checks for???</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>338</td>
|
||||
<td>Log manager spread down feature is disabled</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>159</td>
|
||||
<td>Memory leak. Dbusers are loaded into memory but not unloaded</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## Known Issues
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below.
|
||||
|
||||
* The Read/Write Splitter is a little too strict when it receives errors from slave servers during execution of session commands. This can result in sessions being terminated in situation in which MaxScale could recover without terminating the sessions.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
* Service init script is missing after upgrade from 1.0 in RPM-based system. Can be fixed by reinstalling the package ('yum reinstall maxscale' or 'rpm -i --force /maxscale-1.1.rpm')
|
||||
|
||||
* Binlog Router Plugin is compatible with MySQL 5.6
|
||||
Binlog Router Plugin currently does not work for MariaDB 5.5 and MariaDB 10.0
|
||||
|
||||
* LONGBLOG are currently not supported.
|
||||
|
||||
* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser.
|
||||
|
||||
* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client.
|
||||
|
||||
## Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 13.10
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* Fedora 19
|
||||
|
||||
* Fedora 20
|
||||
|
||||
* OpenSuSE 13
|
||||
|
||||
* SuSE Linux Enterprise 11
|
||||
|
||||
* SuSE Linux Enterprise 12
|
@ -1,94 +0,0 @@
|
||||
# MariaDB MaxScale 1.1.1 Release Notes
|
||||
|
||||
## 1.1.1 GA
|
||||
|
||||
MaxScale 1.1 is the current stable (GA) release of MaxScale. Version 1.1.1 is mainly a bug fix release introducing fixes, but also introduces some improvements to existing functionality.
|
||||
|
||||
## New Features
|
||||
|
||||
### Improved memory management options
|
||||
|
||||
Readwritesplit and schemarouter now both support upper limits to session state modifying commands. They both also allow the complete disabling of the history, making the sessions consume the smallest amount of memory while still making sure all slaves keep identical session states.
|
||||
|
||||
### Improved trace logging
|
||||
|
||||
The process of the user authentication data retrieval is logged into the trace log and the readconnroute router also outputs more information into the trace log. This allows for easier problem detection and configuration tuning.
|
||||
|
||||
### More informative output from maxkeys and maxpasswd
|
||||
|
||||
Using the password functionality in MaxScale is now a lot easier. Both programs now produce verbose and exact error messages.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
Here is a list of bugs fixed since the release of the 1.1.0 version of MaxScale. The bug IDs are from the **[MariaDB Jira](https://jira.mariadb.org/)**.
|
||||
|
||||
* [MXS-99](https://jira.mariadb.org/browse/MXS-99): /etc/init.d/maxscale reload doesn't do anything
|
||||
* [MXS-83](https://jira.mariadb.org/browse/MXS-83): linkage fails when system pcre library is recent
|
||||
* [MXS-112](https://jira.mariadb.org/browse/MXS-112): Disable saving of session commands in the readwritesplit and schemarouter modules
|
||||
* [MXS-114](https://jira.mariadb.org/browse/MXS-114): Disable recovery of disconnected slaves
|
||||
* [MXS-73](https://jira.mariadb.org/browse/MXS-73): MaxScale uses nearly 100% CPU
|
||||
* [MXS-36](https://jira.mariadb.org/browse/MXS-36): bugzillaId-671: wrong message if SHOW DATABASES privilege is missing
|
||||
* [MXS-39](https://jira.mariadb.org/browse/MXS-39): bugzillaId-731:Boolean configuration parameters accept inconsistent parameters
|
||||
* [MXS-64](https://jira.mariadb.org/browse/MXS-64): maxkeys and Maxpasswd do not produce informative error output
|
||||
* [MXS-25](https://jira.mariadb.org/browse/MXS-25): bugzillaId-656: MySQL Monitor: claims that Master is available after master failure
|
||||
* [MXS-82](https://jira.mariadb.org/browse/MXS-82): cmake warns when mariadb is compiled without mysql_release
|
||||
* [MXS-69](https://jira.mariadb.org/browse/MXS-69): dbfwfilter should be pessimistic about rule syntax errors
|
||||
* [MXS-98](https://jira.mariadb.org/browse/MXS-98): regexfilter log
|
||||
* [MXS-28](https://jira.mariadb.org/browse/MXS-28): bugzillaId-433: Logging don't include assert information
|
||||
* [MXS-75](https://jira.mariadb.org/browse/MXS-75): "wildcard" rule also blocks COUNT(*)
|
||||
* [MXS-118](https://jira.mariadb.org/browse/MXS-118): Two monitors loaded at the same time result into not working installation
|
||||
* [MXS-33](https://jira.mariadb.org/browse/MXS-33): bugzillaId-702: CLI: list services command shows negative values for the number of users of a service (Read Service).
|
||||
* [MXS-17](https://jira.mariadb.org/browse/MXS-17): bugzillaId-736: Memory leak while doing read/write splitting
|
||||
* [MXS-30](https://jira.mariadb.org/browse/MXS-30): bugzillaId-487: Buffer manager should not use pointer arithmetic on void*
|
||||
* [MXS-81](https://jira.mariadb.org/browse/MXS-81): cmake fails when init scripts are missing
|
||||
* [MXS-127](https://jira.mariadb.org/browse/MXS-127): disable_sescmd_history causes MaxScale to crash under load
|
||||
|
||||
## Known Issues
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of these are listed below.
|
||||
|
||||
* The Read/Write Splitter is a little too strict when it receives errors from slave servers during execution of session commands. This can result in sessions being terminated in situations from which MaxScale could recover without terminating the sessions.
|
||||
|
||||
* MaxScale cannot manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
* Binlog Router Plugin is compatible with MySQL 5.6
|
||||
Binlog Router Plugin currently does not work for MariaDB 5.5 and MariaDB 10.0
|
||||
|
||||
|
||||
* LONGBLOB are currently not supported.
|
||||
|
||||
* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser.
|
||||
|
||||
* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client.
|
||||
|
||||
## Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases. Packages are now provided for:
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* Fedora 19
|
||||
|
||||
* Fedora 20
|
||||
|
||||
* Fedora 21
|
||||
|
||||
* OpenSuSE 13
|
||||
|
||||
* SuSE Linux Enterprise 11
|
||||
|
||||
* SuSE Linux Enterprise 12
|
@ -1,99 +0,0 @@
|
||||
# MariaDB MaxScale 1.2 Release Notes
|
||||
|
||||
## 1.2 GA
|
||||
|
||||
This document details the changes in version 1.2 since the release of the 1.1.1 GA Release of the MaxScale product.
|
||||
|
||||
###***PLEASE NOTICE: MaxScale installation directories have changed in this version***
|
||||
The 1.2 version of MaxScale differs from previous versions in its installation layout. Please take great care when upgrading MaxScale from previous versions to version 1.2. An automatic upgrade will not work due to the severe changes in the installation layout.
|
||||
|
||||
## New Features
|
||||
|
||||
### Non-root MaxScale
|
||||
You can now run MaxScale as any user. The standard installation of a MaxScale package now creates the maxscale user and the maxscale group.
|
||||
|
||||
### FHS-compliant installation
|
||||
The 1.2 version of MaxScale now complies to the Filesystem Hierarchy Standard. This means that MAXSCALE_HOME is no longer necessary and directories can be moved to different locations.
|
||||
|
||||
A quick list of changes in installation directories and file names:
|
||||
|
||||
* Binaries go into `/usr/bin`
|
||||
* Configuration files to `/etc` and the configuration file is now lower case: `maxscale.cnf`
|
||||
* Logs to `/var/log/maxscale`
|
||||
* The module and library directory have been combined into a single directory in `/usr/lib64/maxscale`. If you have custom modules please make sure they are located there.
|
||||
* Data directory is `/var/lib/maxscale`. This is the default location for MaxScale-specific data.
|
||||
* PID file can be found at `/var/run/maxscale`
|
||||
|
||||
### Client side SSL encryption
|
||||
MaxScale now supports SSL/TLS encrypted connections to MaxScale.
|
||||
|
||||
### Launchable scripts
|
||||
Now you can configure MaxScale monitor module to automatically launch a script when it detects change in the state of a backend server. The script can be any customer script defined by you to take diagnostic or reporting action. With this you can easily customize MaxScale's behavior.
|
||||
|
||||
### Lsyncd configuration guide
|
||||
A new tutorial has been added which helps you keep MaxScale's configuration files in sync across multiple hosts. This allows for easier HA setups with MaxScale and guarantees up-to-date configuration files on all nodes. The tutorial can be found [here](../Tutorials/MaxScale-HA-with-lsyncd.md).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
Here is a list of bugs fixed since the release of MaxScale 1.1.1.
|
||||
|
||||
* [MXS-24](https://jira.mariadb.org/browse/MXS-24): bugzillaId-604: Module load path documentation issues ...
|
||||
* [MXS-40](https://jira.mariadb.org/browse/MXS-40): Display logged in users
|
||||
* [MXS-113](https://jira.mariadb.org/browse/MXS-113): MaxScale seems to fail if built against MariaDB 10.0 libraries
|
||||
* [MXS-116](https://jira.mariadb.org/browse/MXS-116): Do not run maxscale as root.
|
||||
* [MXS-117](https://jira.mariadb.org/browse/MXS-117): Allow configuration of the log file directory
|
||||
* [MXS-125](https://jira.mariadb.org/browse/MXS-125): inconsistency in maxkeys/maxpassword output and parameters
|
||||
* [MXS-128](https://jira.mariadb.org/browse/MXS-128): cyclic dependency utils -> log_manager -> utils
|
||||
* [MXS-136](https://jira.mariadb.org/browse/MXS-136): Check for MaxScale replication heartbeat table existence before creating
|
||||
* [MXS-137](https://jira.mariadb.org/browse/MXS-137): cannot get sql for queries with length >= 0x80
|
||||
* [MXS-139](https://jira.mariadb.org/browse/MXS-139): Schemarouter authentication for wildcard grants fails without optimize_wildcard
|
||||
* [MXS-140](https://jira.mariadb.org/browse/MXS-140): strip_db_esc does not work without auth_all_servers
|
||||
* [MXS-162](https://jira.mariadb.org/browse/MXS-162): Fix Incorrect info in Configuration Guide
|
||||
* [MXS-165](https://jira.mariadb.org/browse/MXS-165): Concurrency issue while incrementing sessions in qlafilter
|
||||
* [MXS-166](https://jira.mariadb.org/browse/MXS-166): Memory leak when creating a new event
|
||||
* [MXS-171](https://jira.mariadb.org/browse/MXS-171): Allow reads on master for readwritesplit
|
||||
* [MXS-176](https://jira.mariadb.org/browse/MXS-176): Missing dependencies in documentation
|
||||
* [MXS-179](https://jira.mariadb.org/browse/MXS-179): Keep configuration changes in synch across MaxScale Mate Nodes
|
||||
* [MXS-180](https://jira.mariadb.org/browse/MXS-180): MariaDB10 binlog router compatibilty
|
||||
* [MXS-181](https://jira.mariadb.org/browse/MXS-181): Poor performance on TCP connection due to Nagle's algoritm
|
||||
* [MXS-182](https://jira.mariadb.org/browse/MXS-182): SHOW SLAVE STATUS and maxadmin "show services" for binlog router needs updated when used with MariaDB 10 Master
|
||||
* [MXS-212](https://jira.mariadb.org/browse/MXS-212): Stopped services accept connections
|
||||
* [MXS-225](https://jira.mariadb.org/browse/MXS-225): RPM Debug build packages have no debugging symbols
|
||||
* [MXS-227](https://jira.mariadb.org/browse/MXS-227): Memory leak in Galera Monitor
|
||||
* [MXS-244](https://jira.mariadb.org/browse/MXS-244): Memory leak when using prepared statements without arguments
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale, the most serious of this are listed below.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
* LONGBLOB are currently not supported.
|
||||
|
||||
* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser.
|
||||
|
||||
* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client.
|
||||
|
||||
## Packaging
|
||||
|
||||
Both RPM and Debian packages are available for MaxScale in addition to the tar based releases previously distributed we now provide
|
||||
|
||||
* CentOS/RedHat 5
|
||||
|
||||
* CentOS/RedHat 6
|
||||
|
||||
* CentOS/RedHat 7
|
||||
|
||||
* Debian 6
|
||||
|
||||
* Debian 7
|
||||
|
||||
* Ubuntu 12.04 LTS
|
||||
|
||||
* Ubuntu 14.04 LTS
|
||||
|
||||
* SuSE Linux Enterprise 11
|
||||
|
||||
* SuSE Linux Enterprise 12
|
@ -1,267 +0,0 @@
|
||||
# MariaDB MaxScale 1.3 Release Notes
|
||||
|
||||
This document describes the changes in release 1.3, when compared to
|
||||
release 1.2.1.
|
||||
|
||||
## 1.3.0
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## New Features
|
||||
|
||||
### Persistent Connections
|
||||
|
||||
MaxScale 1.3.0 introduces the concept of *Persistent Connections*. With
|
||||
that is meant that the connection from MaxScale to the backend server is
|
||||
not terminated even if the connection from the client to MaxScale is.
|
||||
If a client makes frequent short connections, there may be a benefit from
|
||||
using the *Persistent Connection* feature as it may reduce the time it
|
||||
takes from establishing a connection from the client through MaxScale to
|
||||
the backend server.
|
||||
|
||||
**NOTE**: The persistent connections do not track session state. This means
|
||||
that changing the default database or modifying the session state will cause
|
||||
those changes to be active even for new connections. If you use queries with
|
||||
implicit databases or use connections with different client settings, you
|
||||
should take great care when using persistent connections.
|
||||
|
||||
Additional information is available in the following document:
|
||||
* [Administration Tutorial](../Tutorials/Administration-Tutorial.md#persistent-connections)
|
||||
|
||||
### Binlog Server
|
||||
|
||||
There are new administrative commands: STOP SLAVE, START SLAVE, RESET SLAVE
|
||||
and CHANGE MASTER TO. The master server details are now provided by a
|
||||
master.ini file located in binlog directory and could be changed via
|
||||
CHANGE MASTER TO command issued via MySQL connection to MaxScale.
|
||||
|
||||
Before migrating to 1.3.0 it is necessary to put a writable master.ini file
|
||||
into binlog directory, containing these parameters:
|
||||
|
||||
```
|
||||
[binlog_configuration]
|
||||
master_host=127.0.0.1
|
||||
master_port=3308
|
||||
master_user=repl
|
||||
master_password=somepass
|
||||
filestem=repl-bin
|
||||
```
|
||||
|
||||
Users may change parameters according to their configuration.
|
||||
|
||||
**Note**: the "servers" parameter is no longer required in the service
|
||||
definition.
|
||||
|
||||
Additional information is available in the following documents:
|
||||
* [Binlogrouter Tutorial](../Tutorials/Replication-Proxy-Binlog-Router-Tutorial.md)
|
||||
* [Upgrading Binlogrouter to 1.3](../Upgrading/Upgrading-BinlogRouter-To-Maxscale-1.3.md)
|
||||
* [Binlogrouter Documentation](../Routers/Binlogrouter.md)
|
||||
|
||||
### Logging Changes
|
||||
|
||||
Before 1.3, MaxScale logged data to four different log files; *error*,
|
||||
*message*, *trace* and *debug*. Complementary and/or alternatively, MaxScale
|
||||
could also log to syslog, in which case messages intended for the error and
|
||||
message file were logged there. What files were enabled and written to was
|
||||
controlled by entries in the MaxScale configuration file.
|
||||
|
||||
This has now been changed so that MaxScale logs to a single
|
||||
file - *maxscale.log* - and each logged entry is prepended with *error*,
|
||||
*warning*, *notice*, *info* or *debug*, depending on the seriousness or
|
||||
priority of the message. The levels are the same as those of syslog.
|
||||
MaxScale is still capable of complementary or alternatively logging to syslog.
|
||||
|
||||
What used to be logged to the *message* file is now logged as a *notice*
|
||||
message and what used to be written to the *trace* file, is logged as an
|
||||
*info* message.
|
||||
|
||||
By default, *notice*, *warning* and *error* messages are logged, while
|
||||
*info* and *debug* messages are not. Exactly what kind of messages are
|
||||
logged can be controlled via the MaxScale configuration file, but enabling
|
||||
and disabling different kinds of messages can also be performed at runtime
|
||||
from maxadmin.
|
||||
|
||||
Earlier, the *error* and *message* files were written to the filesystem,
|
||||
while the *trace* and *debug* files were written to shared memory. The
|
||||
one and only log file of MaxScale is now by default written to the filesystem.
|
||||
This will have performance implications if *info* and *debug* messages are
|
||||
enabled.
|
||||
|
||||
If you want to retain the possibility of turning on *info* and *debug*
|
||||
messages, without it impacting the performance too much, the recommended
|
||||
approach is to add the following entries to the MaxScale configuration file:
|
||||
|
||||
```
|
||||
[maxscale]
|
||||
syslog=1
|
||||
maxlog=0
|
||||
log_to_shm=1
|
||||
```
|
||||
|
||||
This will have the effect of MaxScale creating the *maxscale.log* into
|
||||
shared memory, but not logging anything to it. However, all *notice*,
|
||||
*warning* and *error* messages will be logged to syslog.
|
||||
|
||||
Then, if there is a need to turn on *info* messages that can be done via
|
||||
the maxadmin interface:
|
||||
|
||||
```
|
||||
MaxScale> enable log-priority info
|
||||
MaxScale> enable maxlog
|
||||
```
|
||||
|
||||
Note that *info* and *debug* messages are never logged to syslog.
|
||||
|
||||
### PCRE2 integration
|
||||
|
||||
MaxScale now uses the PCRE2 library for regular expressions. This has been
|
||||
integrated into the core configuration processing and most of the modules.
|
||||
The main module which uses this is the regexfilter which now fully supports
|
||||
the PCRE2 syntax with proper substitutions. For a closer look at how this
|
||||
differs from the POSIX regular expression syntax take a look at the
|
||||
[PCRE2 documentation](http://www.pcre.org/current/doc/html/pcre2syntax.html).
|
||||
|
||||
**Please note**, that the substitution string follows different rules than
|
||||
the traditional substitution strings. The usual way of referring to capture
|
||||
groups in the substitution string is with the backslash character followed
|
||||
by the capture group reference e.g. `\1` but the PCRE2 library uses the dollar
|
||||
character followed by the group reference. To quote the PCRE2 native API manual:
|
||||
|
||||
```
|
||||
In the replacement string, which is interpreted as a UTF string in UTF mode, and is checked for UTF validity unless the PCRE2_NO_UTF_CHECK option is set, a dollar character is an escape character that can specify the insertion of characters from capturing groups in the pattern. The following forms are recognized:
|
||||
|
||||
$$ insert a dollar character
|
||||
$<n> insert the contents of group <n>
|
||||
${<n>} insert the contents of group <n>
|
||||
```
|
||||
|
||||
### Improved launchable scripts
|
||||
|
||||
The launchable scripts were modified to allow usage without wrapper scripts.
|
||||
The scripts are now executed as they are in the configuration files with certain
|
||||
keywords being replaced with the initiator, event and node list. For more
|
||||
details, please read the [Monitor Common](../Monitors/Monitor-Common.md) document.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.2.1.](https://jira.mariadb.org/browse/MXS-550?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%201.3.0)
|
||||
|
||||
* [MXS-559](https://jira.mariadb.org/browse/MXS-559): Crash due to debug assertion in readwritesplit
|
||||
* [MXS-551](https://jira.mariadb.org/browse/MXS-551): Maxscale BETA 1.3.0 running as root
|
||||
* [MXS-548](https://jira.mariadb.org/browse/MXS-548): Maxscale 1.2.1 crash on Ubuntu 4.04.3 x86_64
|
||||
* [MXS-508](https://jira.mariadb.org/browse/MXS-508): regex filter ignores username
|
||||
* [MXS-505](https://jira.mariadb.org/browse/MXS-505): if Maxscale fails to start it goes to infinite "try-to-start and fail" loop
|
||||
* [MXS-501](https://jira.mariadb.org/browse/MXS-501): USE <db> hangs when Tee filter uses matching
|
||||
* [MXS-500](https://jira.mariadb.org/browse/MXS-500): Tee filter hangs when statements aren't duplicated.
|
||||
* [MXS-499](https://jira.mariadb.org/browse/MXS-499): Init script error on Debian Wheezy
|
||||
* [MXS-494](https://jira.mariadb.org/browse/MXS-494): Weight calculation favors servers without connections
|
||||
* [MXS-493](https://jira.mariadb.org/browse/MXS-493): SIGFPE when weightby parameter is 0 and using LEAST_GLOBAL_CONNECTIONS
|
||||
* [MXS-492](https://jira.mariadb.org/browse/MXS-492): Segfault if server is missing weighting parameter
|
||||
* [MXS-491](https://jira.mariadb.org/browse/MXS-491): MaxScale can time out systemd if startup of services takes too long
|
||||
* [MXS-480](https://jira.mariadb.org/browse/MXS-480): Readwritesplit defaults cause connection pileup
|
||||
* [MXS-479](https://jira.mariadb.org/browse/MXS-479): localtime must not be used in the multi-threaded program.
|
||||
* [MXS-472](https://jira.mariadb.org/browse/MXS-472): Monitors update status in multiple steps
|
||||
* [MXS-464](https://jira.mariadb.org/browse/MXS-464): Upgrade 1.2.0 to 1.2.1 blocking start of `maxscale` service
|
||||
* [MXS-450](https://jira.mariadb.org/browse/MXS-450): Syslog default prefix is MaxScale not maxscale
|
||||
* [MXS-447](https://jira.mariadb.org/browse/MXS-447): Monitors are started before they have been fully configured
|
||||
* [MXS-436](https://jira.mariadb.org/browse/MXS-436): Invalid threads argument is ignored and MaxScale starts with one thread
|
||||
* [MXS-431](https://jira.mariadb.org/browse/MXS-431): Backend authentication fails with schemarouter
|
||||
* [MXS-429](https://jira.mariadb.org/browse/MXS-429): Binlog Router crashes due to segmentation fault with no meaningful error if no listener is configured
|
||||
* [MXS-428](https://jira.mariadb.org/browse/MXS-428): Maxscale crashes at startup.
|
||||
* [MXS-427](https://jira.mariadb.org/browse/MXS-427): Logging a large string causes a segmentation fault
|
||||
* [MXS-417](https://jira.mariadb.org/browse/MXS-417): Single character wildcard doesn't work in MaxScale
|
||||
* [MXS-416](https://jira.mariadb.org/browse/MXS-416): Orphan sessions appear after many network errors
|
||||
* [MXS-415](https://jira.mariadb.org/browse/MXS-415): MaxScale 1.2.1 crashed with Signal 6 and 11
|
||||
* [MXS-414](https://jira.mariadb.org/browse/MXS-414): Maxscale crashed every day!
|
||||
* [MXS-413](https://jira.mariadb.org/browse/MXS-413): MaxAdmin hangs with show session
|
||||
* [MXS-412](https://jira.mariadb.org/browse/MXS-412): show dbusers segmentation fault
|
||||
* [MXS-409](https://jira.mariadb.org/browse/MXS-409): prepare should not hit all servers
|
||||
* [MXS-408](https://jira.mariadb.org/browse/MXS-408): Connections to backend databases do not clear promptly
|
||||
* [MXS-407](https://jira.mariadb.org/browse/MXS-407): Maxscale binlogrouter binlog names are unncessarily length-limited
|
||||
* [MXS-405](https://jira.mariadb.org/browse/MXS-405): Maxscale bin router crash
|
||||
* [MXS-403](https://jira.mariadb.org/browse/MXS-403): Monitor callback to DCBs evades thread control causing crashes
|
||||
* [MXS-394](https://jira.mariadb.org/browse/MXS-394): Faults in regex_replace function of regexfilter.c
|
||||
* [MXS-392](https://jira.mariadb.org/browse/MXS-392): Update to "Rabbit MQ setup and MaxScale Integration" document
|
||||
* [MXS-386](https://jira.mariadb.org/browse/MXS-386): max_sescmd_history should not close connections
|
||||
* [MXS-385](https://jira.mariadb.org/browse/MXS-385): disable_sescmd_history can cause false data to be read.
|
||||
* [MXS-379](https://jira.mariadb.org/browse/MXS-379): Incorrect handing of a GWBUF may cause SIGABRT.
|
||||
* [MXS-376](https://jira.mariadb.org/browse/MXS-376): MaxScale terminates with SIGABRT.
|
||||
* [MXS-373](https://jira.mariadb.org/browse/MXS-373): If config file is non-existent, maxscale crashes.
|
||||
* [MXS-366](https://jira.mariadb.org/browse/MXS-366): Multi-source slave servers are not detected.
|
||||
* [MXS-365](https://jira.mariadb.org/browse/MXS-365): Load data local infile connection abort when loading certain files
|
||||
* [MXS-363](https://jira.mariadb.org/browse/MXS-363): rpm building seems to do something wrong with maxscale libraries
|
||||
* [MXS-361](https://jira.mariadb.org/browse/MXS-361): crash on backend restart if persistent connections are in use
|
||||
* [MXS-360](https://jira.mariadb.org/browse/MXS-360): Persistent connections: maxadmin reports 0 all the time even if connections are created
|
||||
* [MXS-358](https://jira.mariadb.org/browse/MXS-358): Crash, Error in `/usr/bin/maxscale': free(): invalid next size (fast)
|
||||
* [MXS-352](https://jira.mariadb.org/browse/MXS-352): With no backend connection, services aren't started
|
||||
* [MXS-351](https://jira.mariadb.org/browse/MXS-351): Router error handling can cause crash by leaving dangling DCB pointer
|
||||
* [MXS-345](https://jira.mariadb.org/browse/MXS-345): maxscale.conf in /etc/init.d prevents puppet from starting maxscale
|
||||
* [MXS-342](https://jira.mariadb.org/browse/MXS-342): When ini_parse fails to parse config file, no log messages are printed.
|
||||
* [MXS-333](https://jira.mariadb.org/browse/MXS-333): use_sql_variables_in=master doesn't work
|
||||
* [MXS-329](https://jira.mariadb.org/browse/MXS-329): The session pointer in a DCB can be null unexpectedly
|
||||
* [MXS-323](https://jira.mariadb.org/browse/MXS-323): mysql_client readwritesplit handleError seems using wrong dcb and cause wrong behavior
|
||||
* [MXS-321](https://jira.mariadb.org/browse/MXS-321): Incorrect number of connections in maxadmin list view
|
||||
* [MXS-310](https://jira.mariadb.org/browse/MXS-310): MaxScale 1.2 does not completely cleanly change to the maxscale user
|
||||
* [MXS-297](https://jira.mariadb.org/browse/MXS-297): postinstall on debian copies wrong file in /etc/init.d
|
||||
* [MXS-293](https://jira.mariadb.org/browse/MXS-293): Bug in init script, and maxscale --user=maxscale does run as root
|
||||
* [MXS-291](https://jira.mariadb.org/browse/MXS-291): Random number generation has flaws
|
||||
* [MXS-289](https://jira.mariadb.org/browse/MXS-289): Corrupted memory or empty value are in Master_host field of SHOW SLAVE STATUS when master connection is broken
|
||||
* [MXS-286](https://jira.mariadb.org/browse/MXS-286): Fix the content and format of MaxScale-HA-with-Corosync-Pacemaker document
|
||||
* [MXS-283](https://jira.mariadb.org/browse/MXS-283): SSL connections leak memory
|
||||
* [MXS-282](https://jira.mariadb.org/browse/MXS-282): Add example to "Routing Hints" document
|
||||
* [MXS-281](https://jira.mariadb.org/browse/MXS-281): SELECT INTO OUTFILE query goes several times to one slave
|
||||
* [MXS-280](https://jira.mariadb.org/browse/MXS-280): SELECT INTO OUTFILE query succeeds even if backed fails
|
||||
* [MXS-276](https://jira.mariadb.org/browse/MXS-276): Memory leak of buffer in connection router readQuery
|
||||
* [MXS-274](https://jira.mariadb.org/browse/MXS-274): Memory Leak
|
||||
* [MXS-271](https://jira.mariadb.org/browse/MXS-271): Schemarouter and unknown databases
|
||||
* [MXS-269](https://jira.mariadb.org/browse/MXS-269): Crash in MySQL backend protocol
|
||||
* [MXS-260](https://jira.mariadb.org/browse/MXS-260): Multiple MaxScale processes
|
||||
* [MXS-258](https://jira.mariadb.org/browse/MXS-258): ERR_error_string could overflow in future
|
||||
* [MXS-254](https://jira.mariadb.org/browse/MXS-254): Failure to read configuration file results in no error log messages
|
||||
* [MXS-251](https://jira.mariadb.org/browse/MXS-251): Non-thread safe strerror
|
||||
* [MXS-220](https://jira.mariadb.org/browse/MXS-220): LAST_INSERT_ID() query is redirect to slave if function call is in where clause
|
||||
* [MXS-210](https://jira.mariadb.org/browse/MXS-210): Check MaxScale user privileges
|
||||
* [MXS-202](https://jira.mariadb.org/browse/MXS-202): User password not handled correctly
|
||||
* [MXS-197](https://jira.mariadb.org/browse/MXS-197): Incorrect sequence of operations with DCB
|
||||
* [MXS-196](https://jira.mariadb.org/browse/MXS-196): DCB state is changed prior to polling operation
|
||||
* [MXS-195](https://jira.mariadb.org/browse/MXS-195): maxscaled.c ineffective DCB disposal
|
||||
* [MXS-184](https://jira.mariadb.org/browse/MXS-184): init script issues in CentOS 7
|
||||
* [MXS-183](https://jira.mariadb.org/browse/MXS-183): MaxScale crash after 'reload config'
|
||||
* [MXS-111](https://jira.mariadb.org/browse/MXS-111): maxscale binlog events shown in show services seems to be double-counted for the master connection
|
||||
* [MXS-54](https://jira.mariadb.org/browse/MXS-54): Write failed auth attempt to trace log
|
||||
* [MXS-35](https://jira.mariadb.org/browse/MXS-35): bugzillaId-451: maxscale main() exit code is always 0 after it daemonizes
|
||||
* [MXS-29](https://jira.mariadb.org/browse/MXS-29): bugzillaId-589: detect if MAXSCALE_SCHEMA.HEARTBEAT table is not replicated
|
||||
* [MXS-3](https://jira.mariadb.org/browse/MXS-3): Remove code for atomic_add in skygw_utils.cc
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are a number bugs and known limitations within this version of MaxScale,
|
||||
the most serious of this are listed below.
|
||||
|
||||
* MaxScale can not manage authentication that uses wildcard matching in hostnames in the mysql.user table of the backend database. The only wildcards that can be used are in IP address entries.
|
||||
|
||||
* When users have different passwords based on the host from which they connect MaxScale is unable to determine which password it should use to connect to the backend database. This results in failed connections and unusable usernames in MaxScale.
|
||||
|
||||
* The readconnroute module does not support sending of LONGBLOB data.
|
||||
|
||||
* Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser.
|
||||
|
||||
* The Database Firewall filter does not support multi-statements. Using them will result in an error being sent to the client.
|
||||
|
||||
* The SSL support is known to be unstable.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version 1.2.1 of MaxScale
|
||||
is 1.2.1. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,112 +0,0 @@
|
||||
|
||||
# MariaDB MaxScale 1.4.0 (Beta) Release Notes
|
||||
|
||||
Release 1.4.0 is a beta release.
|
||||
|
||||
This document describes the changes in release 1.4.0, when compared to
|
||||
release 1.3.0.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## New Features
|
||||
|
||||
### Firewall Filter
|
||||
|
||||
The firewall filter now supports different actions when a rule is matched.
|
||||
Currently possible actions are to either block the query, allow it or
|
||||
ignore the match and allow it.
|
||||
|
||||
Matching and non-matching queries can now be logged and by combining this new
|
||||
logging functionality with the _ignore_ action, you can set up the filter in
|
||||
a dry-run mode. For more information about the firewall filter, please refer to
|
||||
[Database Firewall Filter](../Filters/Database-Firewall-Filter.md).
|
||||
|
||||
### SSL
|
||||
|
||||
Client-side SSL support has been in MaxScale for some time, but has
|
||||
been known to have been unstable. In 1.4.0, client side SSL is now
|
||||
believed to be stable and fully usable.
|
||||
|
||||
The SSL configuration is now done on a per listener basis which
|
||||
allows both SSL and non-SSL connections to a service. For more details
|
||||
on how to configure this, please refer to the
|
||||
[MaxScale Configuration Guide](../Getting-Started/Configuration-Guide.md#listener-and-ssl).
|
||||
|
||||
### POSIX Extended Regular Expression Syntax
|
||||
|
||||
The _qlafilter_, the _topfilter_ and the _namedserverfilter_ now
|
||||
accept _extended_ as a filter option, which enables the POSIX Extended
|
||||
Regular Expression syntax.
|
||||
|
||||
|
||||
### Improved user grant detection
|
||||
|
||||
MaxScale now allows users with only table level access to connect with
|
||||
a default database. The service users will require SELECT privileges on
|
||||
the `mysql.tables_priv` table:
|
||||
|
||||
```
|
||||
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'maxscalehost'
|
||||
```
|
||||
For more information, refer to the configuration guide:
|
||||
[MaxScale Configuration Guide](../Getting-Started/Configuration-Guide.md#service).
|
||||
|
||||
### Improved password encryption
|
||||
|
||||
MaxScale 1.4.0 uses the MD5 version of the crypt function which is more secure
|
||||
than the non-MD5 version. This means that a new password file needs to be
|
||||
created with `maxkeys`. The configuration file should be updated to use the new
|
||||
passwords. This can be done with the help of the `maxpasswd` utility. For more
|
||||
details about how to do this, please refer to the installation guide:
|
||||
[MariaDB MaxScale Installation Guide](../Getting-Started/MariaDB-MaxScale-Installation-Guide.md)
|
||||
|
||||
## Removed Features
|
||||
|
||||
* MaxScale no longer supports SSLv3.
|
||||
|
||||
* The `enabled` mode, which allows both SSL and non-SSL connections on the same port, has been removed.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.3.0.](https://jira.mariadb.org/browse/MXS-600?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%201.4.0)
|
||||
|
||||
* [MXS-400](https://jira.mariadb.org/browse/MXS-400): readwritesplit router doesn't allow connect when the only remaining server is master and slave
|
||||
* [MXS-497](https://jira.mariadb.org/browse/MXS-497): MaxScale does not contemplate client multiple statements (CLIENT_MULTI_STATEMENTS)
|
||||
* [MXS-504](https://jira.mariadb.org/browse/MXS-504): SSL connection handling needs work
|
||||
* [MXS-511](https://jira.mariadb.org/browse/MXS-511): ReadWriteSplit router won't choose node as master and logs confusing "RUNNING MASTER" error message
|
||||
* [MXS-563](https://jira.mariadb.org/browse/MXS-563): Maxscale fails to start
|
||||
* [MXS-565](https://jira.mariadb.org/browse/MXS-565): Binlog Router doesn't handle 16MB larger transmissions
|
||||
* [MXS-573](https://jira.mariadb.org/browse/MXS-573): Write permission to systemd service file
|
||||
* [MXS-574](https://jira.mariadb.org/browse/MXS-574): Wrong parameter name in systemd service file
|
||||
* [MXS-575](https://jira.mariadb.org/browse/MXS-575): Nagios scripts lack execute permissions
|
||||
* [MXS-577](https://jira.mariadb.org/browse/MXS-577): Don't install systemd files and init.d scipts at the same time
|
||||
* [MXS-581](https://jira.mariadb.org/browse/MXS-581): Only the first 8 characters of passwords are used
|
||||
* [MXS-582](https://jira.mariadb.org/browse/MXS-582): crypt is not thread safe
|
||||
* [MXS-585](https://jira.mariadb.org/browse/MXS-585): Intermittent connection failure with MaxScale 1.2/1.3 using MariaDB/J 1.3
|
||||
* [MXS-589](https://jira.mariadb.org/browse/MXS-589): Password encryption looks for the file in the wrong directory
|
||||
* [MXS-592](https://jira.mariadb.org/browse/MXS-592): Build failure with MariaDB 10.1 when doing a debug build
|
||||
* [MXS-594](https://jira.mariadb.org/browse/MXS-594): Binlog name gets trunkated
|
||||
* [MXS-600](https://jira.mariadb.org/browse/MXS-600): Threads=auto parameter configuration fails
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,41 +0,0 @@
|
||||
|
||||
# MariaDB MaxScale 1.4.1 Release Notes
|
||||
|
||||
Release 1.4.1 is a GA release.
|
||||
|
||||
This document describes the changes in release 1.4.1, when compared to
|
||||
release [1.4.0](MaxScale-1.4.0-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.4.0.](https://jira.mariadb.org/browse/MXS-646?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%201.4.1)
|
||||
|
||||
* [MXS-646](https://jira.mariadb.org/browse/MXS-646): Namedserverfilter ignores user and source parameters
|
||||
* [MXS-632](https://jira.mariadb.org/browse/MXS-632): Replace or update VERSION
|
||||
* [MXS-630](https://jira.mariadb.org/browse/MXS-630): Requirement of tables_priv access not documented in "Upgrading" guide
|
||||
* [MXS-629](https://jira.mariadb.org/browse/MXS-629): Lack of tables_priv privilege causes confusing error message
|
||||
* [MXS-627](https://jira.mariadb.org/browse/MXS-627): Failure to connect to MaxScale with MariaDB Connector/J
|
||||
* [MXS-585](https://jira.mariadb.org/browse/MXS-585): Intermittent connection failure with MaxScale 1.2/1.3 using MariaDB/J 1.3
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,44 +0,0 @@
|
||||
|
||||
# MariaDB MaxScale 1.4.2 Release Notes
|
||||
|
||||
Release 1.4.2 is a GA release.
|
||||
|
||||
This document describes the changes in release 1.4.2, when compared to
|
||||
release 1.4.1.
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.4.1.](https://jira.mariadb.org/browse/MXS-683?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%201.4.2)
|
||||
|
||||
* [MXS-684](https://jira.mariadb.org/browse/MXS-684): Password field still used with MySQL 5.7
|
||||
* [MXS-683](https://jira.mariadb.org/browse/MXS-683): qc_mysqlembedded reports as-name instead of original-name.
|
||||
* [MXS-681](https://jira.mariadb.org/browse/MXS-681): Loading service users error
|
||||
* [MXS-680](https://jira.mariadb.org/browse/MXS-680): qc_mysqlembedded fails to look into function when reporting affected fields
|
||||
* [MXS-679](https://jira.mariadb.org/browse/MXS-679): qc_mysqlembedded excludes some fields, when reporting affected fields
|
||||
* [MXS-662](https://jira.mariadb.org/browse/MXS-662): No Listener on different IPs but same port since 1.4.0
|
||||
* [MXS-661](https://jira.mariadb.org/browse/MXS-661): Log fills with 'Length (0) is 0 or query string allocation failed'
|
||||
* [MXS-656](https://jira.mariadb.org/browse/MXS-656): after upgrade from 1.3 to 1.4, selecting master isn't working as expected
|
||||
* [MXS-616](https://jira.mariadb.org/browse/MXS-616): Duplicated binlog event under heavy load.
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,37 +0,0 @@
|
||||
|
||||
# MariaDB MaxScale 1.4.3 Release Notes
|
||||
|
||||
Release 1.4.3 is a GA release.
|
||||
|
||||
This document describes the changes in release 1.4.3, when compared to
|
||||
release 1.4.2.
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.4.2.](https://jira.mariadb.org/browse/MXS-700?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%201.4.3)
|
||||
|
||||
* [MXS-700](https://jira.mariadb.org/browse/MXS-700): Segfault on startup
|
||||
* [MXS-699](https://jira.mariadb.org/browse/MXS-699): qc_mysqlembedded fails to return fields in comma expression
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,129 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.0 Release Notes
|
||||
|
||||
Release 2.0.0 is a Beta release.
|
||||
|
||||
This document describes the changes in release 2.0.0, when compared to
|
||||
release 1.4.3.
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## License
|
||||
|
||||
The license of MaxScale has been changed from GPLv2 to MariaDB BSL.
|
||||
|
||||
For more information about MariaDB BSL, please refer to
|
||||
[MariaDB BSL](https://www.mariadb.com/bsl).
|
||||
|
||||
## New Features
|
||||
|
||||
### Binlog-to-Avro Translator
|
||||
|
||||
The 2.0 release of MaxScale contains the beta release of the binlog-to-Avro
|
||||
conversion and distribution modules. These modules allow MaxScale to connect to
|
||||
a MariaDB 10.0 master server and convert the binary log events to Avro format
|
||||
change records. These records can then be queries as a continuous JSON or raw Avro
|
||||
stream using the new CDC protocol.
|
||||
|
||||
The [Avrorouter Tutorial](../Tutorials/Avrorouter-Tutorial.md) contains
|
||||
information on how to get started with the binlog-to-Avro translation.
|
||||
|
||||
The [Avrorouter](../Routers/Avrorouter.md) documentation has more information
|
||||
on the details of this conversion process and how to configure the module.
|
||||
|
||||
The [CDC Protocol](../Protocols/CDC.md) documentation contains the details of
|
||||
the new protocol.
|
||||
|
||||
### Read Continuation upon Master Down
|
||||
|
||||
The _readwritesplit_ routing module now supports a high availability read mode
|
||||
where read queries are allowed even if the master server goes down. The new
|
||||
functionality supports three modes: disconnection on master failure, disconnection
|
||||
on first write after master failure and error on write after master failure.
|
||||
|
||||
The MySQL monitor module, _mysqlmon_, now supports stale states for both the master
|
||||
and slave servers. This means that when a slave loses its master, it will retain
|
||||
the slave state as long as it is running.
|
||||
|
||||
For more details about these new modes, please read the [ReadWriteSplit](../Routers/ReadWriteSplit.md)
|
||||
and [MySQL Monitor](../Monitors/MySQL-Monitor.md) documentation.
|
||||
|
||||
### Backend SSL
|
||||
|
||||
The configuration for a backend server can now be set for SSL connections from MaxScale. Although loosely referred to as SSL, this is nowadays the TLS security protocol. If, in MaxScale, a server is configured with SSL parameters then MaxScale will only connect to it using a secure protocol. MaxScale supports TLS versions 1.0, 1.1 and 1.2; which can be used will depend on the capability of the backend server. Once configured, if a secure connection cannot be made, attempts to connect to MaxScale that require that server will fail. An alternative that should be considered is the use of SSH tunnels.
|
||||
|
||||
For more information about backend SSL, please refer to
|
||||
[Server and SSL](../Getting-Started/Configuration-Guide.md#server-and-ssl)
|
||||
|
||||
### Connection Throttling
|
||||
|
||||
The option now exists to set [max_connections](../Getting-Started/Configuration-Guide.md#max_connections) for a service. If a non-zero number is specified, then MaxScale will accept connection requests only up to the specified limit. Further connections will receive the error message "Too many connections" with error number 1040. .
|
||||
|
||||
### MaxAdmin Security Improvements
|
||||
|
||||
The way a user of MaxAdmin is authenticated has been completely changed.
|
||||
In 2.0, MaxAdmin can only connect to MaxScale using a domain socket, thus
|
||||
_only when run on the same host_, and authorization is based upon the UNIX
|
||||
identity. Remote access is no longer supported.
|
||||
|
||||
When 2.0 has been installed, MaxAdmin can only be used by `root` and
|
||||
other users must be added anew. Please consult
|
||||
[MaxAdmin documentation](../Reference/MaxAdmin.md) for more details.
|
||||
|
||||
### Query Classifier
|
||||
|
||||
The query classifier component that MaxScale uses when deciding what
|
||||
to do with a particular query has been changed. It used to be based
|
||||
upon the MariaDB embedded library, but is now based upon sqlite3.
|
||||
This change should not cause any changes in the behaviour of MaxScale.
|
||||
|
||||
For more information, please refer to
|
||||
[Configuration Guide](../Getting-Started/Configuration-Guide.md#query_classifier).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 1.4.3.](https://jira.mariadb.org/browse/MXS-739?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%202.0.0)
|
||||
|
||||
* [MXS-821](https://jira.mariadb.org/browse/MXS-821): filestem router option for binlog router is not documented
|
||||
* [MXS-814](https://jira.mariadb.org/browse/MXS-814): Service and monitor permission checks only use the last available server
|
||||
* [MXS-813](https://jira.mariadb.org/browse/MXS-813): binlogrouter, mariadb10.0, signal 11, crash
|
||||
* [MXS-801](https://jira.mariadb.org/browse/MXS-801): strip_db_esc should default to True
|
||||
* [MXS-790](https://jira.mariadb.org/browse/MXS-790): replication_heartbeat table privilege is not checked / fails silently / is not documented
|
||||
* [MXS-776](https://jira.mariadb.org/browse/MXS-776): Documentation about limitations of reload config is not clear
|
||||
* [MXS-772](https://jira.mariadb.org/browse/MXS-772): RPM installation produces errors
|
||||
* [MXS-766](https://jira.mariadb.org/browse/MXS-766): R/W router sends DEALLOCATE PREPARE to ALL instead of MASTER
|
||||
* [MXS-739](https://jira.mariadb.org/browse/MXS-739): Maxinfo issuing invalid null's in JSON response
|
||||
* [MXS-733](https://jira.mariadb.org/browse/MXS-733): MaxScale `list sessions` can report "Invalid State" for some sessions.
|
||||
* [MXS-720](https://jira.mariadb.org/browse/MXS-720): MaxScale fails to start and doesn't log any useful message when there are spurious characters in the config file
|
||||
* [MXS-718](https://jira.mariadb.org/browse/MXS-718): qc_mysqlembedded does not report fields for INSERT
|
||||
* [MXS-704](https://jira.mariadb.org/browse/MXS-704): start/stop scripts use which in a non-silent manner
|
||||
* [MXS-695](https://jira.mariadb.org/browse/MXS-695): MaxScale does not build on Debian 8 following build from source instructions
|
||||
* [MXS-685](https://jira.mariadb.org/browse/MXS-685): 1.4.1: ReadWrite Split on Master-Master setup doesn't chose master, logs "RUNNING MASTER" error message instead (related to MXS-511?)
|
||||
* [MXS-675](https://jira.mariadb.org/browse/MXS-675): QLA Filter Output Log Improvements
|
||||
* [MXS-658](https://jira.mariadb.org/browse/MXS-658): Crash in embedded library when MariaDB 10.0 is used
|
||||
* [MXS-653](https://jira.mariadb.org/browse/MXS-653): maxpasswd writes notice message to stdout
|
||||
* [MXS-652](https://jira.mariadb.org/browse/MXS-652): ssl is configured in a wrong way, but Maxscale can be started and works
|
||||
* [MXS-633](https://jira.mariadb.org/browse/MXS-633): Galera Monitor should not require the REPLICATION CLIENT privilege
|
||||
* [MXS-631](https://jira.mariadb.org/browse/MXS-631): Rename and clean up macros.cmake
|
||||
* [MXS-477](https://jira.mariadb.org/browse/MXS-477): readconnroute misinterprets data as COM_CHANGE_USER
|
||||
* [MXS-419](https://jira.mariadb.org/browse/MXS-419): Socket creation failed due 24, Too many open files.
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,146 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.1 Release Notes
|
||||
|
||||
Release 2.0.1 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.1, when compared to
|
||||
[release 2.0.0](MaxScale-2.0.0-Release-Notes.md).
|
||||
|
||||
If you are upgrading from 1.4.3, please also read the release notes
|
||||
of [2.0.0](./MaxScale-2.0.0-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please consider submitting a bug
|
||||
report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Changed default values
|
||||
|
||||
### `strip_db_esc`
|
||||
|
||||
The service parameter [_strip_db_esc_](../Getting-Started/Configuration-Guide.md#strip_db_esc)
|
||||
now defaults to true.
|
||||
|
||||
### `detect_stale_master`
|
||||
|
||||
The [stale master detection](../Monitors/MySQL-Monitor.md#detect_stale_master)
|
||||
feature is now enabled by default.
|
||||
|
||||
## Updated Features
|
||||
|
||||
### Starting MariaDB MaxScale
|
||||
|
||||
There is now a new command line parameter `--basedir=PATH` that will
|
||||
cause all directory paths and the location of the configuration file
|
||||
to be defined relative to that path.
|
||||
|
||||
For instance, invoking MariaDB MaxScale like
|
||||
|
||||
$ maxscale --basedir=/path/maxscale
|
||||
|
||||
has the same effect as invoking MariaDB MaxScale like
|
||||
|
||||
$ maxscale --config=/path/maxscale/etc/maxscale.cnf
|
||||
--configdir=/path/maxscale/etc
|
||||
--logdir=/path/maxscale/var/log/maxscale
|
||||
--cachhedir=/path/maxscale/var/cache/maxscale
|
||||
--libdir=/path/maxscale/lib/maxscale
|
||||
--datadir=/path/maxscale/var/lib/maxscale
|
||||
--execdir=/path/maxscale/bin
|
||||
--language=/path/maxscale/var/lib/maxscale
|
||||
--piddir=/path/maxscale/var/run/maxscale
|
||||
|
||||
### Password parameter
|
||||
|
||||
In the configuration entry for a _service_ or _monitor_, the value of
|
||||
the password to be used can now be specified using `password` in addition
|
||||
to `passwd`. The use of the latter will be deprecated and removed in later
|
||||
releases of MaxScale.
|
||||
|
||||
[SomeService]
|
||||
...
|
||||
password=mypasswd
|
||||
|
||||
### Routing hint priority change
|
||||
|
||||
Routing hints now have the highest priority when a routing decision is made. If
|
||||
there is a conflict between the original routing decision made by the
|
||||
readwritesplit and the routing hint attached to the query, the routing hint
|
||||
takes higher priority.
|
||||
|
||||
What this change means is that, if a query would normally be routed to the
|
||||
master but the routing hint instructs the router to route it to the slave, it
|
||||
would be routed to the slave.
|
||||
|
||||
**WARNING**: This change can alter the way some statements are routed and could
|
||||
possibly cause data loss, corruption or inconsisteny. Please consult the [Hint
|
||||
Syntax](../Reference/Hint-Syntax.md) and
|
||||
[ReadWriteSplit](../Routers/ReadWriteSplit.md) documentation before using
|
||||
routing hints.
|
||||
|
||||
### MaxAdmin Usage
|
||||
|
||||
In 2.0.0 (Beta), the authentication mechanism of MaxAdmin was completely
|
||||
changed, so that MaxAdmin could only connect to MaxScale using a Unix domain
|
||||
socket, thus _only when run on the same host_, and authorization was based
|
||||
on the Unix identity. Remote access was no longer supported.
|
||||
|
||||
To the user this was visible so that while you in 1.4.3 had to provide
|
||||
a password when starting _maxadmin_ and when adding a user
|
||||
```
|
||||
user@host $ maxadmin -p password
|
||||
MaxAdmin> add user john johns-password
|
||||
```
|
||||
in 2.0.0 (Beta), where only Unix domain sockets could be used, you did not
|
||||
have to provide a password neither when starting _maxadmin_, nor when adding
|
||||
users
|
||||
```
|
||||
user@host $ maxadmin
|
||||
MaxAdmin> add user john
|
||||
```
|
||||
as the MaxScale user corresponded to a Unix user, provided the Linux user
|
||||
had been added as a user of MaxScale.
|
||||
|
||||
In 2.0.1 (GA) this has been changed so that the 1.4.3 behaviour is intact
|
||||
but _deprecated_, and the 2.0.0 (Beta) behaviour is exposed using a new set
|
||||
of commands:
|
||||
```
|
||||
MaxAdmin> enable account alice
|
||||
MaxAdmin> disable account alice
|
||||
```
|
||||
Note that the way you need to invoke _maxadmin_ depends upon how MariaDB
|
||||
MaxScale has been configued.
|
||||
|
||||
Please consult
|
||||
[MaxAdmin documentation](../Reference/MaxAdmin.md) for more details.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 2.0.0.](https://jira.mariadb.org/browse/MXS-860?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20in%20(2.0.1)%20AND%20resolved%20%3E%3D%20-21d%20AND%20(resolution%20%3D%20Done%20OR%20resolution%20%3D%20Fixed)%20ORDER%20BY%20priority%20DESC)
|
||||
|
||||
* [MXS-860](https://jira.mariadb.org/browse/MXS-860): I want to access the web site if master server is down
|
||||
* [MXS-870](https://jira.mariadb.org/browse/MXS-870): Assertion of Buffer Overflow
|
||||
* [MXS-845](https://jira.mariadb.org/browse/MXS-845): "Server down" event is re-triggered after maintenance mode is repeated
|
||||
* [MXS-836](https://jira.mariadb.org/browse/MXS-836): "Failed to start all MaxScale services" without retrying
|
||||
* [MXS-835](https://jira.mariadb.org/browse/MXS-835): Please reinstate remote access to maxscaled protocol
|
||||
* [MXS-773](https://jira.mariadb.org/browse/MXS-773): 100% CPU on idle MaxScale with MaxInfo
|
||||
* [MXS-812](https://jira.mariadb.org/browse/MXS-812): Number of conns not matching number of operations
|
||||
* [MXS-856](https://jira.mariadb.org/browse/MXS-856): If config file cannot be accessed and creation of log file fails, MaxScale crashes with SIGSEGV
|
||||
* [MXS-829](https://jira.mariadb.org/browse/MXS-829): When the config file isn't readable or doesn't exist, maxscale silently ends
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,64 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.2 Release Notes
|
||||
|
||||
Release 2.0.2 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.2, when compared to
|
||||
release [2.0.1](MaxScale-2.0.1-Release-Notes.md).
|
||||
|
||||
If you are upgrading from release 1.4.4, please also read the release
|
||||
notes of release [2.0.0](./MaxScale-2.0.0-Release-Notes.md) and
|
||||
release [2.0.1](./MaxScale-2.0.1-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please submit a bug report at
|
||||
[Jira](https://jira.mariadb.org).
|
||||
|
||||
## Updated Features
|
||||
|
||||
### [MXS-978] (https://jira.mariadb.org/browse/MXS-978) Support for stale master in case of restart
|
||||
|
||||
In case where replication monitor gets a stale master status (slaves are down)
|
||||
and maxscale gets restarted, master loses the stale master status and no writes
|
||||
can happen.
|
||||
|
||||
To cater for this situation there is now a `set server <name> stale` command.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 2.0.1.](https://jira.mariadb.org/browse/MXS-976?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.2)
|
||||
|
||||
* [MXS-1018](https://jira.mariadb.org/browse/MXS-1018): Internal connections don't use TLS
|
||||
* [MXS-1008](https://jira.mariadb.org/browse/MXS-1008): MySQL Monitor with scripts leaks memory
|
||||
* [MXS-976](https://jira.mariadb.org/browse/MXS-976): Crash in libqc_sqlite
|
||||
* [MXS-975](https://jira.mariadb.org/browse/MXS-975): TCP backlog is capped at 1280
|
||||
* [MXS-970](https://jira.mariadb.org/browse/MXS-970): A fatal problem with maxscale automatically shut down
|
||||
* [MXS-969](https://jira.mariadb.org/browse/MXS-969): use_sql_variables_in=master can break functionality of important session variables
|
||||
* [MXS-967](https://jira.mariadb.org/browse/MXS-967): setting connection_timeout=value cause error : Not a boolean value
|
||||
* [MXS-965](https://jira.mariadb.org/browse/MXS-965): galeramon erlaubt keine TLS verschlüsselte Verbindung
|
||||
* [MXS-960](https://jira.mariadb.org/browse/MXS-960): MaxScale Binlog Server does not allow comma to be in password
|
||||
* [MXS-957](https://jira.mariadb.org/browse/MXS-957): Temporary table creation from another temporary table isn't detected
|
||||
* [MXS-956](https://jira.mariadb.org/browse/MXS-956): Removing DCB 0x7fbf94016760 but was in state DCB_STATE_DISCONNECTED which is not legal for a call to dcb_close
|
||||
* [MXS-955](https://jira.mariadb.org/browse/MXS-955): MaxScale 2.0.1 doesn't recognize user and passwd options in .maxadmin file
|
||||
* [MXS-953](https://jira.mariadb.org/browse/MXS-953): Charset error when server configued in utf8mb4
|
||||
* [MXS-942](https://jira.mariadb.org/browse/MXS-942): describe table query not routed to shard that contains the schema
|
||||
* [MXS-917](https://jira.mariadb.org/browse/MXS-917): False error message about master not being in use
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
||||
|
@ -1,57 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.3 Release Notes
|
||||
|
||||
Release 2.0.3 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.3, when compared to
|
||||
release [2.0.2](MaxScale-2.0.2-Release-Notes.md).
|
||||
|
||||
If you are upgrading from release 1.4.4, please also read the release
|
||||
notes of release [2.0.0](./MaxScale-2.0.0-Release-Notes.md),
|
||||
release [2.0.1](./MaxScale-2.0.1-Release-Notes.md) and
|
||||
release [2.0.2](./MaxScale-2.0.2-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please submit a bug report at
|
||||
[Jira](https://jira.mariadb.org).
|
||||
|
||||
## Updated Features
|
||||
|
||||
### [MXS-1027] (https://jira.mariadb.org/browse/MXS-1027) Add Upstart support (including respawn) for MaxScale
|
||||
|
||||
MaxScale now provides an Upstart configuration file for systems that do not
|
||||
support systemd.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.3)
|
||||
is a list of bugs fixed since the release of MaxScale 2.0.2.
|
||||
|
||||
* [MXS-1048](https://jira.mariadb.org/browse/MXS-1048): ShemaRouter can't handle backquoted database names
|
||||
* [MXS-1047](https://jira.mariadb.org/browse/MXS-1047): Batch inserts through Maxscale with C/J stall
|
||||
* [MXS-1045](https://jira.mariadb.org/browse/MXS-1045): Defunct processes after maxscale have executed script during failover
|
||||
* [MXS-1044](https://jira.mariadb.org/browse/MXS-1044): Init-Script for SLES 11 displays error messages when called
|
||||
* [MXS-1043](https://jira.mariadb.org/browse/MXS-1043): Reading last insert id from @@identity variable does not work with maxscale
|
||||
* [MXS-1033](https://jira.mariadb.org/browse/MXS-1033): maxscale crushes on maxadmin request
|
||||
* [MXS-1026](https://jira.mariadb.org/browse/MXS-1026): Crash with NullAuth authenticator
|
||||
* [MXS-1009](https://jira.mariadb.org/browse/MXS-1009): maxinfo sigsegv in spinlock_release
|
||||
* [MXS-964](https://jira.mariadb.org/browse/MXS-964): Fatal: MaxScale 2.0.1 received fatal signal 6
|
||||
* [MXS-956](https://jira.mariadb.org/browse/MXS-956): Maxscale crash: Removing DCB 0x7fbf94016760 but was in state DCB_STATE_DISCONNECTED which is not legal for a call to dcb_close
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,57 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.4 Release Notes -- 2017-02-01
|
||||
|
||||
Release 2.0.4 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.4, when compared to
|
||||
release [2.0.3](MaxScale-2.0.3-Release-Notes.md).
|
||||
|
||||
If you are upgrading from release 1.4, please also read the release
|
||||
notes of release [2.0.3](./MaxScale-2.0.3-Release-Notes.md),
|
||||
release [2.0.2](./MaxScale-2.0.2-Release-Notes.md),
|
||||
release [2.0.1](./MaxScale-2.0.1-Release-Notes.md) and
|
||||
[2.0.0](./MaxScale-2.0.0-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please submit a bug report at
|
||||
[Jira](https://jira.mariadb.org).
|
||||
|
||||
## Changed Features
|
||||
|
||||
- The dbfwfilter now rejects all prepared statements instead of ignoring
|
||||
them. This affects _wildcard_, _columns_, _on_queries_ and _no_where_clause_
|
||||
type rules which previously ignored prepared statements.
|
||||
|
||||
- The dbfwfilter now allows COM_PING and other commands though when
|
||||
`action=allow`. See [../Filters/Database-Firewall-Filter.md](documentation)
|
||||
for more details.
|
||||
|
||||
- The MariaDB Connector-C was upgraded to a preliminary release of version 2.3.3 (fixes MXS-951).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.4)
|
||||
is a list of bugs fixed since the release of MaxScale 2.0.3.
|
||||
|
||||
* [MXS-1111](https://jira.mariadb.org/browse/MXS-1111): Request Ping not allowed
|
||||
* [MXS-1082](https://jira.mariadb.org/browse/MXS-1082): Block prepared statements
|
||||
* [MXS-1080](https://jira.mariadb.org/browse/MXS-1080): Readwritesplit (documentation of max_slave_replication_lag)
|
||||
* [MXS-951](https://jira.mariadb.org/browse/MXS-951): Using utf8mb4 on galera hosts stops maxscale connections
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,47 +0,0 @@
|
||||
# MariaDB MaxScale 2.0.5 Release Notes
|
||||
|
||||
Release 2.0.5 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.5, when compared to
|
||||
release [2.0.4](MaxScale-2.0.4-Release-Notes.md).
|
||||
|
||||
If you are upgrading from release 1.4, please also read the following
|
||||
release notes:
|
||||
[2.0.4](./MaxScale-2.0.4-Release-Notes.md),
|
||||
[2.0.3](./MaxScale-2.0.3-Release-Notes.md),
|
||||
[2.0.2](./MaxScale-2.0.2-Release-Notes.md),
|
||||
[2.0.1](./MaxScale-2.0.1-Release-Notes.md) and
|
||||
[2.0.0](./MaxScale-2.0.0-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please submit a bug report at
|
||||
[Jira](https://jira.mariadb.org).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.5)
|
||||
is a list of bugs fixed since the release of MaxScale 2.0.4.
|
||||
|
||||
* [MXS-1130](https://jira.mariadb.org/browse/MXS-1130): Unexpected length encoding 'ff' encountered
|
||||
* [MXS-1123](https://jira.mariadb.org/browse/MXS-1123): connect_timeout setting causes frequent disconnects
|
||||
* [MXS-1081](https://jira.mariadb.org/browse/MXS-1081): Avro data file corruption
|
||||
* [MXS-1025](https://jira.mariadb.org/browse/MXS-1025): qc_sqlite always reports " Statement was parsed, but not classified"
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
@ -1,4 +1,4 @@
|
||||
# MariaDB MaxScale 2.1.1 Release Notes
|
||||
# MariaDB MaxScale 2.1.1 Release Notes -- 2017-03-14
|
||||
|
||||
Release 2.1.1 is a Beta release.
|
||||
|
||||
|
@ -15,11 +15,23 @@ report at [Jira](https://jira.mariadb.org).
|
||||
|
||||
## Changed Features
|
||||
|
||||
### Cache
|
||||
|
||||
* The storage `storage_inmemory` is now the default, so the parameter
|
||||
`storage` no longer need to be set explicitly.
|
||||
|
||||
### Improved Wildcard Matching
|
||||
|
||||
The MySQLAuth module now supports all types of wildcards for both IP addresses
|
||||
as well as hostnames.
|
||||
|
||||
### Configurable Connector-C Plugin Directory
|
||||
|
||||
The Connector-C used by MaxScale can now be configured to load authentication
|
||||
plugins from a specific directory with the new `connector_plugindir`
|
||||
parameter. Read the [Configuration Guide](../Getting-Started/Configuration-Guide.md)
|
||||
for more details about this new parameter.
|
||||
|
||||
## New Features
|
||||
|
||||
### IPv6 Support
|
||||
@ -27,10 +39,18 @@ as well as hostnames.
|
||||
MaxScale now supports IPv6 connections on both the client and backend side as
|
||||
well as being able to listen on IPv6 addresses.
|
||||
|
||||
### ReadWriteSplit Connection Keepalive
|
||||
|
||||
The _readwritesplit_ module now implements a `connection_keepalive`
|
||||
feature which allows sending of keepalive pings to idle connections. For
|
||||
more details, read the [ReadWriteSplit documentation](../Routers/ReadWriteSplit.md).
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 2.1.1.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%202.1.2%20AND%20fixVersion%20NOT%20IN%20(2.1.1))
|
||||
|
||||
* [MXS-1032](https://jira.mariadb.org/browse/MXS-1032) missing mysql_clear_password.so plugin
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
|
@ -35,6 +35,14 @@ The commands can be issued, but have no effect.
|
||||
|
||||
## New Features
|
||||
|
||||
### MySQL Monitor Crash Safety
|
||||
|
||||
The MySQL monitor keeps a journal of the state of the servers and the currently
|
||||
elected master. This information will be read if MaxScale suffers an
|
||||
uncontrolled shutdown. By doing the journaling of server states, the mysqlmon
|
||||
monitor is able to keep track of stale master and stale slave states across
|
||||
restarts and crashes.
|
||||
|
||||
### Avrorouter `deflate` compression
|
||||
|
||||
The Avrorouter now supports the `deflate` compression method. This allows the
|
||||
|
@ -65,6 +65,22 @@ disable_sescmd_history=true
|
||||
master_failure_mode=fail_on_write
|
||||
```
|
||||
|
||||
### `connection_keepalive`
|
||||
|
||||
Send keepalive pings to backend servers. This feature was introduced in MaxScale
|
||||
2.1.2 and is disabled by default.
|
||||
|
||||
The parameter value is the interval in seconds between each keepalive ping. A
|
||||
keepalive ping will be sent to a backend server if the connection is idle and it
|
||||
has not been used within `n` seconds where `n` is greater than or equal to the
|
||||
value of _connection_keepalive_. The keepalive pings are only sent when the
|
||||
client executes a query.
|
||||
|
||||
This functionality allows the readwritesplit module to keep all backend
|
||||
connections alive even if they are not used. This is a common problem if the
|
||||
backend servers have a low _wait_timeout_ value and the client connections live
|
||||
for a long time.
|
||||
|
||||
## Router options
|
||||
|
||||
**`router_options`** may include multiple **readwritesplit**-specific options. All the options are parameter-value pairs. All parameters listed in this section must be configured as a value in `router_options`.
|
||||
|
@ -136,7 +136,7 @@ uint64_t avro_length_integer(uint64_t val)
|
||||
*
|
||||
* @see maxavro_get_error
|
||||
*/
|
||||
char* maxavro_read_string(MAXAVRO_FILE* file)
|
||||
char* maxavro_read_string(MAXAVRO_FILE* file, size_t* size)
|
||||
{
|
||||
char *key = NULL;
|
||||
uint64_t len;
|
||||
@ -149,6 +149,7 @@ char* maxavro_read_string(MAXAVRO_FILE* file)
|
||||
memcpy(key, file->buffer_ptr, len);
|
||||
key[len] = '\0';
|
||||
file->buffer_ptr += len;
|
||||
*size = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -282,19 +283,10 @@ MAXAVRO_MAP* maxavro_read_map_from_file(MAXAVRO_FILE *file)
|
||||
{
|
||||
for (long i = 0; i < blocks; i++)
|
||||
{
|
||||
size_t size;
|
||||
MAXAVRO_MAP* val = calloc(1, sizeof(MAXAVRO_MAP));
|
||||
uint64_t keylen;
|
||||
uint64_t valuelen;
|
||||
|
||||
if (val && maxavro_read_integer_from_file(file, &keylen) &&
|
||||
(val->key = MXS_MALLOC(keylen + 1)) &&
|
||||
fread(val->key, 1, keylen, file->file) == keylen &&
|
||||
maxavro_read_integer_from_file(file, &valuelen) &&
|
||||
(val->value = MXS_MALLOC(valuelen + 1)) &&
|
||||
fread(val->value, 1, valuelen, file->file) == valuelen)
|
||||
if (val && (val->key = maxavro_read_string(file, &size)) && (val->value = maxavro_read_string(file, &size)))
|
||||
{
|
||||
val->key[keylen] = '\0';
|
||||
val->value[valuelen] = '\0';
|
||||
val->next = rval;
|
||||
rval = val;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
/** Reading primitives */
|
||||
bool maxavro_read_integer(MAXAVRO_FILE *file, uint64_t *val);
|
||||
char* maxavro_read_string(MAXAVRO_FILE *file);
|
||||
char* maxavro_read_string(MAXAVRO_FILE *file, size_t *size);
|
||||
bool maxavro_skip_string(MAXAVRO_FILE* file);
|
||||
bool maxavro_read_float(MAXAVRO_FILE *file, float *dest);
|
||||
bool maxavro_read_double(MAXAVRO_FILE *file, double *dest);
|
||||
|
@ -98,10 +98,11 @@ static json_t* read_and_pack_value(MAXAVRO_FILE *file, MAXAVRO_SCHEMA_FIELD *fie
|
||||
case MAXAVRO_TYPE_BYTES:
|
||||
case MAXAVRO_TYPE_STRING:
|
||||
{
|
||||
char *str = maxavro_read_string(file);
|
||||
size_t len;
|
||||
char *str = maxavro_read_string(file, &len);
|
||||
if (str)
|
||||
{
|
||||
value = json_string(str);
|
||||
value = json_stringn(str, len);
|
||||
MXS_FREE(str);
|
||||
}
|
||||
}
|
||||
|
@ -44,11 +44,6 @@ check_include_files(time.h HAVE_TIME)
|
||||
check_include_files(unistd.h HAVE_UNISTD)
|
||||
|
||||
# Check for libraries MaxScale depends on
|
||||
find_library(HAVE_LIBAIO NAMES aio)
|
||||
if(NOT HAVE_LIBAIO)
|
||||
message(FATAL_ERROR "Could not find libaio")
|
||||
endif()
|
||||
|
||||
find_library(HAVE_LIBSSL NAMES ssl)
|
||||
if(NOT HAVE_LIBSSL)
|
||||
message(FATAL_ERROR "Could not find libssl")
|
||||
|
@ -22,6 +22,7 @@ set(DEFAULT_EXEC_SUBPATH "${MAXSCALE_BINDIR}" CACHE PATH "Default executable sub
|
||||
set(DEFAULT_CONFIG_SUBPATH "etc" CACHE PATH "Default configuration subpath")
|
||||
set(DEFAULT_CONFIG_PERSIST_SUBPATH "maxscale.cnf.d" CACHE PATH "Default persisted configuration subpath")
|
||||
set(DEFAULT_MODULE_CONFIG_SUBPATH "${DEFAULT_CONFIG_SUBPATH}/maxscale.modules.d" CACHE PATH "Default configuration subpath")
|
||||
set(DEFAULT_CONNECTOR_PLUGIN_SUBPATH "lib/plugin" CACHE PATH "Default connector plugin subpath")
|
||||
|
||||
set(DEFAULT_PIDDIR ${MAXSCALE_VARDIR}/${DEFAULT_PID_SUBPATH} CACHE PATH "Default PID file directory")
|
||||
set(DEFAULT_LOGDIR ${MAXSCALE_VARDIR}/${DEFAULT_LOG_SUBPATH} CACHE PATH "Default log directory")
|
||||
@ -33,6 +34,7 @@ set(DEFAULT_EXECDIR ${CMAKE_INSTALL_PREFIX}/${DEFAULT_EXEC_SUBPATH} CACHE PATH "
|
||||
set(DEFAULT_CONFIGDIR /${DEFAULT_CONFIG_SUBPATH} CACHE PATH "Default configuration directory")
|
||||
set(DEFAULT_CONFIG_PERSISTDIR ${DEFAULT_DATADIR}/${DEFAULT_CONFIG_PERSIST_SUBPATH} CACHE PATH "Default persisted configuration directory")
|
||||
set(DEFAULT_MODULE_CONFIGDIR /${DEFAULT_MODULE_CONFIG_SUBPATH} CACHE PATH "Default module configuration directory")
|
||||
set(DEFAULT_CONNECTOR_PLUGINDIR ${MAXSCALE_VARDIR}/${DEFAULT_CONNECTOR_PLUGIN_SUBPATH} CACHE PATH "Default connector plugin directory")
|
||||
|
||||
# Massage TARGET_COMPONENT into a list
|
||||
if (TARGET_COMPONENT)
|
||||
|
@ -607,6 +607,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
MXS_ROUTER_VERSION, /* Implemented module API version */
|
||||
"A simple round robin router", /* Description */
|
||||
"V1.1.0", /* Module version */
|
||||
RCAP_TYPE_CONTIGUOUS_INPUT | RCAP_TYPE_RESULTSET_OUTPUT,
|
||||
&entryPoints, /* Defined above */
|
||||
process_init, /* Process init, can be null */
|
||||
process_finish, /* Process finish, can be null */
|
||||
@ -819,7 +820,7 @@ static uint64_t getCapabilities(MXS_ROUTER *instance)
|
||||
* For output, parsing is not required but counting SQL replies is. RCAP_TYPE_RESULTSET_OUTPUT
|
||||
* should be sufficient.
|
||||
*/
|
||||
return RCAP_TYPE_CONTIGUOUS_INPUT | RCAP_TYPE_RESULTSET_OUTPUT;
|
||||
return RCAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,6 +93,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
MXS_FILTER_VERSION,
|
||||
"A simple query counting filter",
|
||||
"V2.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
|
@ -117,6 +117,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
MXS_PROTOCOL_VERSION,
|
||||
"Test protocol",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
|
@ -69,6 +69,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
MXS_ROUTER_VERSION,
|
||||
"A test router - not for use in real systems",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
@ -151,7 +152,7 @@ diagnostic(MXS_ROUTER *instance, DCB *dcb)
|
||||
|
||||
static uint64_t getCapabilities(MXS_ROUTER* instance)
|
||||
{
|
||||
return 0;
|
||||
return RCAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,7 +58,8 @@ typedef enum
|
||||
GWBUF_TYPE_SESCMD_RESPONSE = 0x08,
|
||||
GWBUF_TYPE_RESPONSE_END = 0x10,
|
||||
GWBUF_TYPE_SESCMD = 0x20,
|
||||
GWBUF_TYPE_HTTP = 0x40
|
||||
GWBUF_TYPE_HTTP = 0x40,
|
||||
GWBUF_TYPE_IGNORABLE = 0x80
|
||||
} gwbuf_type_t;
|
||||
|
||||
#define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0)
|
||||
@ -68,6 +69,7 @@ typedef enum
|
||||
#define GWBUF_IS_TYPE_SESCMD_RESPONSE(b) (b->gwbuf_type & GWBUF_TYPE_SESCMD_RESPONSE)
|
||||
#define GWBUF_IS_TYPE_RESPONSE_END(b) (b->gwbuf_type & GWBUF_TYPE_RESPONSE_END)
|
||||
#define GWBUF_IS_TYPE_SESCMD(b) (b->gwbuf_type & GWBUF_TYPE_SESCMD)
|
||||
#define GWBUF_IS_IGNORABLE(b) (b->gwbuf_type & GWBUF_TYPE_IGNORABLE)
|
||||
|
||||
/**
|
||||
* A structure to encapsulate the data in a form that the data itself can be
|
||||
@ -127,8 +129,8 @@ typedef struct gwbuf
|
||||
void *end; /*< First byte after the valid data */
|
||||
SHARED_BUF *sbuf; /*< The shared buffer with the real data */
|
||||
buffer_object_t *gwbuf_bufobj; /*< List of objects referred to by GWBUF */
|
||||
gwbuf_info_t gwbuf_info; /*< Info bits */
|
||||
gwbuf_type_t gwbuf_type; /*< buffer's data type information */
|
||||
uint32_t gwbuf_info; /*< Info bits; mask of gwbuf_info_t values. */
|
||||
uint32_t gwbuf_type; /*< Type bits; mask of gwbuf_type_t values. */
|
||||
HINT *hint; /*< Hint data for this buffer */
|
||||
BUF_PROPERTY *properties; /*< Buffer properties */
|
||||
struct server *server; /*< The target server where the buffer is executed */
|
||||
@ -141,7 +143,7 @@ typedef struct gwbuf
|
||||
#define GWBUF_DATA(b) ((uint8_t*)(b)->start)
|
||||
|
||||
/*< Number of bytes in the individual buffer */
|
||||
#define GWBUF_LENGTH(b) ((char *)(b)->end - (char *)(b)->start)
|
||||
#define GWBUF_LENGTH(b) ((size_t)((char *)(b)->end - (char *)(b)->start))
|
||||
|
||||
/*< Return the byte at offset byte from the start of the unconsumed portion of the buffer */
|
||||
#define GWBUF_DATA_CHAR(b, byte) (GWBUF_LENGTH(b) < ((byte)+1) ? -1 : *(((char *)(b)->start)+4))
|
||||
@ -326,9 +328,9 @@ extern GWBUF *gwbuf_split(GWBUF **buf, size_t length);
|
||||
* Set given type to all buffers on the list.
|
||||
* *
|
||||
* @param buf The shared buffer
|
||||
* @param type Type to be added
|
||||
* @param type Type to be added, mask of @c gwbuf_type_t values.
|
||||
*/
|
||||
extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
|
||||
extern void gwbuf_set_type(GWBUF *head, uint32_t type);
|
||||
|
||||
/**
|
||||
* Add a property to a buffer.
|
||||
|
@ -205,6 +205,35 @@ struct service* config_get_service(const MXS_CONFIG_PARAMETER *params, const cha
|
||||
*/
|
||||
struct server* config_get_server(const MXS_CONFIG_PARAMETER *params, const char *key);
|
||||
|
||||
/**
|
||||
* @brief Get an array of servers. The caller should free the produced array,
|
||||
* but not the array elements.
|
||||
*
|
||||
* @param params List of configuration parameters
|
||||
* @param key Parameter name
|
||||
* @param output Where to save the output
|
||||
* @return How many servers were found, equal to output array size
|
||||
*/
|
||||
int config_get_server_list(const MXS_CONFIG_PARAMETER *params, const char *key,
|
||||
struct server*** output);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* deallocated by the caller. The server names are not checked to be actual
|
||||
* configured servers.
|
||||
*
|
||||
* The output array may contain more elements than the the value returned, but these
|
||||
* extra elements are null and in the end of the array. If no server names were
|
||||
* parsed or if an error occurs, nothing is written to the output parameter.
|
||||
*
|
||||
* @param servers A list of server names
|
||||
* @param output_array Where to save the output
|
||||
* @return How many servers were found and set into the array. 0 on error or if
|
||||
* none were found.
|
||||
*/
|
||||
int config_parse_server_list(const char *servers, char ***output_array);
|
||||
|
||||
/**
|
||||
* @brief Get copy of parameter value if it is defined
|
||||
*
|
||||
@ -265,10 +294,6 @@ void config_enable_feedback_task(void);
|
||||
*/
|
||||
void config_disable_feedback_task(void);
|
||||
|
||||
|
||||
/** TODO: Add new capability that allows skipping of permission checks */
|
||||
bool is_internal_service(const char *router);
|
||||
|
||||
/**
|
||||
* @brief Reload the configuration
|
||||
*
|
||||
|
@ -218,7 +218,7 @@ typedef struct dcb
|
||||
void *authenticator_data; /**< The authenticator data for this DCB */
|
||||
DCBMM memdata; /**< The data related to DCB memory management */
|
||||
DCB_CALLBACK *callbacks; /**< The list of callbacks for the DCB */
|
||||
long last_read; /*< Last time the DCB received data */
|
||||
int64_t last_read; /*< Last time the DCB received data */
|
||||
struct server *server; /**< The associated backend server */
|
||||
SSL* ssl; /*< SSL struct for connection */
|
||||
bool ssl_read_want_read; /*< Flag */
|
||||
@ -235,13 +235,6 @@ typedef struct dcb
|
||||
skygw_chk_t dcb_chk_tail;
|
||||
} DCB;
|
||||
|
||||
#define DCB_INIT {.dcb_chk_top = CHK_NUM_DCB, \
|
||||
.evq = DCBEVENTQ_INIT, .ip = {0}, .func = {0}, .authfunc = {0}, \
|
||||
.stats = {0}, .memdata = DCBMM_INIT, \
|
||||
.fd = DCBFD_CLOSED, .stats = DCBSTATS_INIT, .ssl_state = SSL_HANDSHAKE_UNKNOWN, \
|
||||
.state = DCB_STATE_ALLOC, .dcb_chk_tail = CHK_NUM_DCB, \
|
||||
.authenticator_data = NULL, .thread = {0}}
|
||||
|
||||
/**
|
||||
* The DCB usage filer used for returning DCB's in use for a certain reason
|
||||
*/
|
||||
|
@ -51,7 +51,9 @@ typedef struct mxs_filter_session
|
||||
|
||||
/**
|
||||
* @verbatim
|
||||
* The "module object" structure for a filter module
|
||||
* The "module object" structure for a filter module. All entry points
|
||||
* marked with `(optional)` are optional entry points which can be set to NULL
|
||||
* if no implementation is required.
|
||||
*
|
||||
* The entry points are:
|
||||
* createInstance Called by the service to create a new instance of the filter
|
||||
@ -61,10 +63,10 @@ typedef struct mxs_filter_session
|
||||
* setDownstream Sets the downstream component of the filter pipline
|
||||
* setUpstream Sets the upstream component of the filter pipline
|
||||
* routeQuery Called on each query that requires routing
|
||||
* clientReply Called for each reply packet
|
||||
* clientReply Called for each reply packet (optional)
|
||||
* diagnostics Called for diagnostic output
|
||||
* getCapabilities Called to obtain the capabilities of the filter
|
||||
* destroyInstance Called for destroying a filter instance
|
||||
* getCapabilities Called to obtain the capabilities of the filter (optional)
|
||||
* destroyInstance Called for destroying a filter instance (optional)
|
||||
*
|
||||
* @endverbatim
|
||||
*
|
||||
@ -97,7 +99,7 @@ typedef struct mxs_filter_object
|
||||
* @c setDownstream and @c setUpstream functions.
|
||||
*
|
||||
* @param instance Filter instance
|
||||
* @param session Client SESSION object
|
||||
* @param session Client MXS_SESSION object
|
||||
*
|
||||
* @return New filter session or NULL on error
|
||||
*/
|
||||
|
@ -21,6 +21,6 @@ MXS_BEGIN_DECLS
|
||||
* every 100 milliseconds and may be used for crude timing etc.
|
||||
*/
|
||||
|
||||
extern long hkheartbeat;
|
||||
extern int64_t hkheartbeat;
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
@ -79,6 +81,7 @@ enum mxs_module_param_type
|
||||
MXS_MODULE_PARAM_PATH, /**< Path to a file or a directory */
|
||||
MXS_MODULE_PARAM_SERVICE, /**< Service name */
|
||||
MXS_MODULE_PARAM_SERVER, /**< Server name */
|
||||
MXS_MODULE_PARAM_SERVERLIST /**< List of server names, separated by ',' */
|
||||
};
|
||||
|
||||
/** Maximum and minimum values for integer types */
|
||||
@ -132,6 +135,7 @@ typedef struct mxs_module
|
||||
MXS_MODULE_VERSION api_version; /**< Module API version */
|
||||
const char *description; /**< Module description */
|
||||
const char *version; /**< Module version */
|
||||
uint64_t module_capabilities; /**< Declared module capabilities */
|
||||
void *module_object; /**< Module type specific API implementation */
|
||||
/**
|
||||
* If non-NULL, this function is called once at process startup. If the
|
||||
@ -174,6 +178,13 @@ typedef struct mxs_module
|
||||
*/
|
||||
#define MXS_END_MODULE_PARAMS 0
|
||||
|
||||
/**
|
||||
* This value should be given to the @c module_capabilities member if the module
|
||||
* declares no capabilities. Currently only routers and filters can declare
|
||||
* capabilities.
|
||||
*/
|
||||
#define MXS_NO_MODULE_CAPABILITIES 0
|
||||
|
||||
/**
|
||||
* Name of the module entry point
|
||||
*
|
||||
|
@ -53,9 +53,37 @@ GWBUF* modutil_create_mysql_err_msg(int packet_number,
|
||||
int merrno,
|
||||
const char *statemsg,
|
||||
const char *msg);
|
||||
|
||||
int modutil_count_signal_packets(GWBUF*, int, int, int*);
|
||||
mxs_pcre2_result_t modutil_mysql_wildcard_match(const char* pattern, const char* string);
|
||||
|
||||
/**
|
||||
* Given a buffer containing a MySQL statement, this function will return
|
||||
* a pointer to the first character that is not whitespace. In this context,
|
||||
* comments are also counted as whitespace. For instance:
|
||||
*
|
||||
* "SELECT" => "SELECT"
|
||||
* " SELECT => "SELECT"
|
||||
* " / * A comment * / SELECT" => "SELECT"
|
||||
* "-- comment\nSELECT" => "SELECT"
|
||||
*
|
||||
* @param sql Pointer to buffer containing a MySQL statement
|
||||
* @param len Length of sql.
|
||||
*
|
||||
* @return The first non whitespace (including comments) character. If the
|
||||
* entire buffer is only whitespace, the returned pointer will point
|
||||
* to the character following the buffer (i.e. sql + len).
|
||||
*/
|
||||
char* modutil_MySQL_bypass_whitespace(char* sql, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Write a COM_PING to a DCB and ignore the response
|
||||
*
|
||||
* @param dcb The backend DCB where the COM_PING is written
|
||||
* @return True if command was successfully sent
|
||||
*/
|
||||
bool modutil_ignorable_ping(DCB *dcb);
|
||||
|
||||
/** Character and token searching functions */
|
||||
char* strnchr_esc(char* ptr, char c, int len);
|
||||
char* strnchr_esc_mysql(char* ptr, char c, int len);
|
||||
|
@ -31,6 +31,7 @@ MXS_BEGIN_DECLS
|
||||
#define MXS_DEFAULT_CONFIG_SUBPATH "@DEFAULT_CONFIG_SUBPATH@"
|
||||
#define MXS_DEFAULT_CONFIG_PERSIST_SUBPATH "@DEFAULT_CONFIG_PERSIST_SUBPATH@"
|
||||
#define MXS_DEFAULT_MODULE_CONFIG_SUBPATH "@DEFAULT_MODULE_CONFIG_SUBPATH@"
|
||||
#define MXS_DEFAULT_CONNECTOR_PLUGIN_SUBPATH "@DEFAULT_CONNECTOR_PLUGIN_SUBPATH@"
|
||||
|
||||
/** Default file locations, configured by CMake */
|
||||
#define MXS_DEFAULT_CONFIGDIR "@DEFAULT_CONFIGDIR@"
|
||||
@ -43,6 +44,7 @@ MXS_BEGIN_DECLS
|
||||
#define MXS_DEFAULT_EXECDIR "@DEFAULT_EXECDIR@"
|
||||
#define MXS_DEFAULT_CONFIG_PERSISTDIR "@DEFAULT_CONFIG_PERSISTDIR@"
|
||||
#define MXS_DEFAULT_MODULE_CONFIGDIR "@DEFAULT_MODULE_CONFIGDIR@"
|
||||
#define MXS_DEFAULT_CONNECTOR_PLUGINDIR "@DEFAULT_CONNECTOR_PLUGINDIR@"
|
||||
|
||||
static const char* default_cnf_fname = "maxscale.cnf";
|
||||
static const char* default_configdir = MXS_DEFAULT_CONFIGDIR;
|
||||
@ -59,6 +61,7 @@ static const char* default_langdir = MXS_DEFAULT_LANGDIR;
|
||||
static const char* default_execdir = MXS_DEFAULT_EXECDIR;
|
||||
static const char* default_config_persistdir = MXS_DEFAULT_CONFIG_PERSISTDIR;
|
||||
static const char* default_module_configdir = MXS_DEFAULT_MODULE_CONFIGDIR;
|
||||
static const char* default_connector_plugindir = MXS_DEFAULT_CONNECTOR_PLUGINDIR;
|
||||
|
||||
static char* configdir = NULL; /*< Where the config file is found e.g. /etc/ */
|
||||
static char* config_persistdir = NULL;/*< Persisted configs e.g. /var/lib/maxscale/maxscale.cnf.d/ */
|
||||
@ -71,6 +74,7 @@ static char* processdatadir = NULL; /*< Process specific data directory */
|
||||
static char* langdir = NULL;
|
||||
static char* piddir = NULL;
|
||||
static char* execdir = NULL;
|
||||
static char* connector_plugindir = NULL;
|
||||
|
||||
void set_libdir(char* param);
|
||||
void set_datadir(char* param);
|
||||
@ -83,6 +87,7 @@ void set_logdir(char* param);
|
||||
void set_langdir(char* param);
|
||||
void set_piddir(char* param);
|
||||
void set_execdir(char* param);
|
||||
void set_connector_plugindir(char* param);
|
||||
char* get_libdir();
|
||||
char* get_datadir();
|
||||
char* get_process_datadir();
|
||||
@ -94,5 +99,6 @@ char* get_piddir();
|
||||
char* get_logdir();
|
||||
char* get_langdir();
|
||||
char* get_execdir();
|
||||
char* get_connector_plugindir();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -305,7 +305,7 @@ typedef struct
|
||||
uint32_t extra_capabilities; /*< MariaDB 10.2 capabilities */
|
||||
unsigned long tid; /*< MySQL Thread ID, in handshake */
|
||||
unsigned int charset; /*< MySQL character set at connect time */
|
||||
bool ignore_reply; /*< If the reply should be discarded */
|
||||
int ignore_replies; /*< How many replies should be discarded */
|
||||
GWBUF* stored_query; /*< Temporarily stored queries */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t protocol_chk_tail;
|
||||
|
@ -19,6 +19,29 @@ MXS_BEGIN_DECLS
|
||||
|
||||
#define QUERY_CLASSIFIER_VERSION {1, 1, 0}
|
||||
|
||||
/**
|
||||
* qc_init_kind_t specifies what kind of initialization should be performed.
|
||||
*/
|
||||
typedef enum qc_init_kind
|
||||
{
|
||||
QC_INIT_SELF = 0x01, /*< Initialize/finalize the query classifier itself. */
|
||||
QC_INIT_PLUGIN = 0x02, /*< Initialize/finalize the plugin. */
|
||||
QC_INIT_BOTH = 0x03
|
||||
} qc_init_kind_t;
|
||||
|
||||
/**
|
||||
* @c qc_collect_info_t specifies what information should be collected during parsing.
|
||||
*/
|
||||
typedef enum qc_collect_info
|
||||
{
|
||||
QC_COLLECT_ESSENTIALS = 0x00, /*< Collect only the base minimum. */
|
||||
QC_COLLECT_TABLES = 0x01, /*< Collect table names. */
|
||||
QC_COLLECT_DATABASES = 0x02, /*< Collect database names. */
|
||||
QC_COLLECT_FIELDS = 0x04, /*< Collect field information. */
|
||||
QC_COLLECT_FUNCTIONS = 0x08, /*< Collect function information. */
|
||||
|
||||
QC_COLLECT_ALL = (QC_COLLECT_TABLES|QC_COLLECT_DATABASES|QC_COLLECT_FIELDS|QC_COLLECT_FUNCTIONS)
|
||||
} qc_collect_info_t;
|
||||
/**
|
||||
* qc_query_type_t defines bits that provide information about a
|
||||
* particular statement.
|
||||
@ -186,13 +209,16 @@ typedef struct query_classifier
|
||||
/**
|
||||
* Called to explicitly parse a statement.
|
||||
*
|
||||
* @param stmt The statement to be parsed.
|
||||
* @param result On return, the parse result, if @c QC_RESULT_OK is returned.
|
||||
* @param stmt The statement to be parsed.
|
||||
* @param collect A bitmask of @c qc_collect_info_t values. Specifies what information
|
||||
* should be collected. Only a hint and must not restrict what information
|
||||
* later can be queried.
|
||||
* @param result On return, the parse result, if @c QC_RESULT_OK is returned.
|
||||
*
|
||||
* @return QC_RESULT_OK, if the parsing was not aborted due to resource
|
||||
* exhaustion or equivalent.
|
||||
*/
|
||||
int32_t (*qc_parse)(GWBUF* stmt, int32_t* result);
|
||||
int32_t (*qc_parse)(GWBUF* stmt, uint32_t collect, int32_t* result);
|
||||
|
||||
/**
|
||||
* Reports the type of the statement.
|
||||
@ -377,11 +403,14 @@ bool qc_setup(const char* plugin_name, const char* plugin_args);
|
||||
*
|
||||
* MaxScale calls this functions, so plugins should not do that.
|
||||
*
|
||||
* @param kind What kind of initialization should be performed.
|
||||
* Combination of qc_init_kind_t.
|
||||
*
|
||||
* @return True, if the process wide initialization could be performed.
|
||||
*
|
||||
* @see qc_process_end qc_thread_init
|
||||
*/
|
||||
bool qc_process_init(void);
|
||||
bool qc_process_init(uint32_t kind);
|
||||
|
||||
/**
|
||||
* Finalizes the query classifier.
|
||||
@ -390,9 +419,12 @@ bool qc_process_init(void);
|
||||
* by a call to this function. MaxScale calls this function, so plugins
|
||||
* should not do that.
|
||||
*
|
||||
* @param kind What kind of finalization should be performed.
|
||||
* Combination of qc_init_kind_t.
|
||||
*
|
||||
* @see qc_process_init qc_thread_end
|
||||
*/
|
||||
void qc_process_end(void);
|
||||
void qc_process_end(uint32_t kind);
|
||||
|
||||
/**
|
||||
* Loads a particular query classifier.
|
||||
@ -426,11 +458,14 @@ void qc_unload(QUERY_CLASSIFIER* classifier);
|
||||
*
|
||||
* MaxScale calls this function, so plugins should not do that.
|
||||
*
|
||||
* @param kind What kind of initialization should be performed.
|
||||
* Combination of qc_init_kind_t.
|
||||
*
|
||||
* @return True if the initialization succeeded, false otherwise.
|
||||
*
|
||||
* @see qc_thread_end
|
||||
*/
|
||||
bool qc_thread_init(void);
|
||||
bool qc_thread_init(uint32_t kind);
|
||||
|
||||
/**
|
||||
* Performs thread finalization needed by the query classifier.
|
||||
@ -439,9 +474,12 @@ bool qc_thread_init(void);
|
||||
*
|
||||
* MaxScale calls this function, so plugins should not do that.
|
||||
*
|
||||
* @param kind What kind of finalization should be performed.
|
||||
* Combination of qc_init_kind_t.
|
||||
*
|
||||
* @see qc_thread_init
|
||||
*/
|
||||
void qc_thread_end(void);
|
||||
void qc_thread_end(uint32_t kind);
|
||||
|
||||
/**
|
||||
* Parses the statement in the provided buffer and returns a value specifying
|
||||
@ -454,11 +492,17 @@ void qc_thread_end(void);
|
||||
* already then this function will only return the result of that parsing;
|
||||
* the statement will not be parsed again.
|
||||
*
|
||||
* @param stmt A buffer containing an COM_QUERY or COM_STMT_PREPARE packet.
|
||||
* @param stmt A buffer containing an COM_QUERY or COM_STMT_PREPARE packet.
|
||||
* @param collect A bitmask of @c qc_collect_info_t values. Specifies what information
|
||||
* should be collected.
|
||||
*
|
||||
* Note that this is merely a hint and does not restrict what
|
||||
* information can be queried for. If necessary, the statement
|
||||
* will transparently be reparsed.
|
||||
*
|
||||
* @return To what extent the statement could be parsed.
|
||||
*/
|
||||
qc_parse_result_t qc_parse(GWBUF* stmt);
|
||||
qc_parse_result_t qc_parse(GWBUF* stmt, uint32_t collect);
|
||||
|
||||
/**
|
||||
* Convert a qc_field_usage_t enum to corresponding string.
|
||||
@ -623,6 +667,30 @@ char** qc_get_table_names(GWBUF* stmt, int* size, bool fullnames);
|
||||
*/
|
||||
uint32_t qc_get_type_mask(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns the type bitmask of transaction related statements.
|
||||
*
|
||||
* If the statement starts a transaction, ends a transaction or
|
||||
* changes the autocommit state, the returned bitmap will be a
|
||||
* combination of:
|
||||
*
|
||||
* QUERY_TYPE_BEGIN_TRX
|
||||
* QUERY_TYPE_COMMIT
|
||||
* QUERY_TYPE_ROLLBACK
|
||||
* QUERY_TYPE_ENABLE_AUTOCOMMIT
|
||||
* QUERY_TYPE_DISABLE_AUTOCOMMIT
|
||||
* QUERY_TYPE_READ (explicitly read only transaction)
|
||||
* QUERY_TYPE_WRITE (explicitly read write transaction)
|
||||
*
|
||||
* Otherwise the result will be 0.
|
||||
*
|
||||
* @param stmt A COM_QUERY or COM_STMT_PREPARE packet.
|
||||
*
|
||||
* @return The relevant type bits if the statement is transaction
|
||||
* related, otherwise 0.
|
||||
*/
|
||||
uint32_t qc_get_trx_type_mask(GWBUF* stmt);
|
||||
|
||||
/**
|
||||
* Returns whether the statement is a DROP TABLE statement.
|
||||
*
|
||||
|
@ -58,7 +58,9 @@ typedef enum error_action
|
||||
|
||||
/**
|
||||
* @verbatim
|
||||
* The "module object" structure for a query router module
|
||||
* The "module object" structure for a query router module. All entry points
|
||||
* marked with `(optional)` are optional entry points which can be set to NULL
|
||||
* if no implementation is required.
|
||||
*
|
||||
* The entry points are:
|
||||
* createInstance Called by the service to create a new instance of the query router
|
||||
@ -67,11 +69,11 @@ typedef enum error_action
|
||||
* freeSession Called when a session is freed
|
||||
* routeQuery Called on each query that requires routing
|
||||
* diagnostics Called to force the router to print diagnostic output
|
||||
* clientReply Called to reply to client the data from one or all backends
|
||||
* clientReply Called to reply to client the data from one or all backends (optional)
|
||||
* handleError Called to reply to client errors with optional closeSession
|
||||
* or make a request for a new backend connection
|
||||
* getCapabilities Called to obtain the capabilities of the router
|
||||
* destroyInstance Called for destroying a router instance
|
||||
* getCapabilities Called to obtain the capabilities of the router (optional)
|
||||
* destroyInstance Called for destroying a router instance (optional)
|
||||
*
|
||||
* @endverbatim
|
||||
*
|
||||
@ -79,21 +81,123 @@ typedef enum error_action
|
||||
*/
|
||||
typedef struct mxs_router_object
|
||||
{
|
||||
/**
|
||||
* @brief Create a new instance of the router
|
||||
*
|
||||
* This function is called when a new router instance is created. The return
|
||||
* value of this function will be passed as the first parameter to the
|
||||
* other API functions.
|
||||
*
|
||||
* @param service The service where the instance is created
|
||||
* @param options Router options
|
||||
*
|
||||
* @return New router instance on NULL on error
|
||||
*/
|
||||
MXS_ROUTER *(*createInstance)(SERVICE *service, char **options);
|
||||
|
||||
/**
|
||||
* Called to create a new user session within the router
|
||||
*
|
||||
* This function is called when a new router session is created for a client.
|
||||
* The return value of this function will be passed as the second parameter
|
||||
* to the @c routeQuery, @c clientReply, @c closeSession, @c freeSession,
|
||||
* and @c handleError functions.
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param session Client MXS_SESSION object
|
||||
*
|
||||
* @return New router session or NULL on error
|
||||
*/
|
||||
MXS_ROUTER_SESSION *(*newSession)(MXS_ROUTER *instance, MXS_SESSION *session);
|
||||
|
||||
/**
|
||||
* @brief Called when a session is closed
|
||||
*
|
||||
* The router should close all objects (including backend DCBs) but not free any memory.
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router session
|
||||
*/
|
||||
void (*closeSession)(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
|
||||
|
||||
/**
|
||||
* @brief Called when a session is freed
|
||||
*
|
||||
* The session should free all allocated memory in this function.
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router session
|
||||
*/
|
||||
void (*freeSession)(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session);
|
||||
|
||||
/**
|
||||
* @brief Called on each query that requires routing
|
||||
*
|
||||
* TODO: Document how routeQuery should be used
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router session
|
||||
* @param queue Request from the client
|
||||
*
|
||||
* @return If successful, the function returns 1. If an error occurs
|
||||
* and the session should be closed, the function returns 0.
|
||||
*/
|
||||
int32_t (*routeQuery)(MXS_ROUTER *instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Called for diagnostic output
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param dcb DCB where the diagnostic information should be written
|
||||
*/
|
||||
void (*diagnostics)(MXS_ROUTER *instance, DCB *dcb);
|
||||
void (*clientReply)(MXS_ROUTER* instance, MXS_ROUTER_SESSION *router_session, GWBUF *queue,
|
||||
DCB *backend_dcb);
|
||||
|
||||
/**
|
||||
* @brief Called for each reply packet
|
||||
*
|
||||
* TODO: Document how clientReply should be used
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router session
|
||||
* @param queue Response from the server
|
||||
* @param backend_dcb The backend DCB which responded to the query
|
||||
*/
|
||||
void (*clientReply)(MXS_ROUTER* instance, MXS_ROUTER_SESSION *router_session,
|
||||
GWBUF *queue, DCB *backend_dcb);
|
||||
|
||||
/**
|
||||
* @brief Called when a backend DCB has failed
|
||||
*
|
||||
* @param instance Router instance
|
||||
* @param router_session Router session
|
||||
* @param errmsgbuf Error message buffer
|
||||
* @param backend_dcb The backend DCB that has failed
|
||||
* @param action The type of the action (TODO: Remove this parameter)
|
||||
*
|
||||
* @param succp Pointer to a `bool` which should be set to true for success or false for error
|
||||
*/
|
||||
void (*handleError)(MXS_ROUTER *instance,
|
||||
MXS_ROUTER_SESSION *router_session,
|
||||
GWBUF *errmsgbuf,
|
||||
DCB *backend_dcb,
|
||||
mxs_error_action_t action,
|
||||
bool* succp);
|
||||
|
||||
/**
|
||||
* @brief Called to obtain the capabilities of the router
|
||||
*
|
||||
* @return Zero or more bitwise-or'd values from the mxs_routing_capability_t enum
|
||||
*
|
||||
* @see routing.h
|
||||
*/
|
||||
uint64_t (*getCapabilities)(MXS_ROUTER *instance);
|
||||
|
||||
/**
|
||||
* @brief Called for destroying a router instance
|
||||
*
|
||||
* @param instance Router instance
|
||||
*/
|
||||
void (*destroyInstance)(MXS_ROUTER *instance);
|
||||
} MXS_ROUTER_OBJECT;
|
||||
|
||||
@ -118,6 +222,7 @@ typedef enum router_capability
|
||||
RCAP_TYPE_NO_RSESSION = 0x00010000, /**< Router does not use router sessions */
|
||||
RCAP_TYPE_NO_USERS_INIT = 0x00020000, /**< Prevent the loading of authenticator
|
||||
users when the service is started */
|
||||
RCAP_TYPE_NO_AUTH = 0x00040000, /**< No `user` or `password` parameter required */
|
||||
} mxs_router_capability_t;
|
||||
|
||||
typedef enum
|
||||
|
@ -254,13 +254,22 @@ void server_add_parameter(SERVER *server, const char *name, const char *value);
|
||||
*/
|
||||
bool server_remove_parameter(SERVER *server, const char *name);
|
||||
|
||||
/**
|
||||
* @brief Check if a server points to a local MaxScale service
|
||||
*
|
||||
* @param server Server to check
|
||||
* @return True if the server points to a local MaxScale service
|
||||
*/
|
||||
bool server_is_mxs_service(const SERVER *server);
|
||||
|
||||
extern int server_free(SERVER *server);
|
||||
extern SERVER *server_find_by_unique_name(const char *name);
|
||||
extern int server_find_by_unique_names(char **server_names, int size, SERVER*** output);
|
||||
extern SERVER *server_find(const char *servname, unsigned short port);
|
||||
extern char *server_status(const SERVER *);
|
||||
extern void server_clear_set_status(SERVER *server, int specified_bits, int bits_to_set);
|
||||
extern void server_set_status_nolock(SERVER *server, int bit);
|
||||
extern void server_clear_status_nolock(SERVER *server, int bit);
|
||||
extern void server_clear_set_status(SERVER *server, unsigned specified_bits, unsigned bits_to_set);
|
||||
extern void server_set_status_nolock(SERVER *server, unsigned bit);
|
||||
extern void server_clear_status_nolock(SERVER *server, unsigned bit);
|
||||
extern void server_transfer_status(SERVER *dest_server, const SERVER *source_server);
|
||||
extern void server_add_mon_user(SERVER *server, const char *user, const char *passwd);
|
||||
extern const char *server_get_parameter(const SERVER *server, char *name);
|
||||
|
@ -38,7 +38,7 @@
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
struct server;
|
||||
struct router;
|
||||
struct mxs_router;
|
||||
struct mxs_router_object;
|
||||
struct users;
|
||||
|
||||
@ -126,7 +126,7 @@ typedef struct service
|
||||
char *routerModule; /**< Name of router module to use */
|
||||
char **routerOptions; /**< Router specific option strings */
|
||||
struct mxs_router_object *router; /**< The router we are using */
|
||||
void *router_instance; /**< The router instance for this service */
|
||||
struct mxs_router *router_instance;/**< The router instance for this service */
|
||||
char *version_string; /**< version string for this service listeners */
|
||||
SERVER_REF *dbref; /**< server references */
|
||||
int n_dbref; /**< Number of server references */
|
||||
@ -145,12 +145,13 @@ typedef struct service
|
||||
SERVICE_REFRESH_RATE rate_limit; /**< The refresh rate limit for users table */
|
||||
MXS_FILTER_DEF **filters; /**< Ordered list of filters */
|
||||
int n_filters; /**< Number of filters */
|
||||
uint64_t conn_idle_timeout; /**< Session timeout in seconds */
|
||||
int64_t conn_idle_timeout; /**< Session timeout in seconds */
|
||||
char *weightby; /**< Service weighting parameter name */
|
||||
struct service *next; /**< The next service in the linked list */
|
||||
bool retry_start; /**< If starting of the service should be retried later */
|
||||
bool log_auth_warnings; /**< Log authentication failures and warnings */
|
||||
uint64_t capabilities; /**< The capabilities of the service. */
|
||||
int max_retry_interval; /**< Maximum retry interval */
|
||||
} SERVICE;
|
||||
|
||||
typedef enum count_spec_t
|
||||
@ -241,6 +242,14 @@ bool serviceHasBackend(SERVICE *service, SERVER *server);
|
||||
bool serviceHasListener(SERVICE *service, const char *protocol,
|
||||
const char* address, unsigned short port);
|
||||
|
||||
/**
|
||||
* @brief Check if a MaxScale service listens on a port
|
||||
*
|
||||
* @param port The port to check
|
||||
* @return True if a MaxScale service uses the port
|
||||
*/
|
||||
bool service_port_is_used(unsigned short port);
|
||||
|
||||
int serviceGetUser(SERVICE *service, char **user, char **auth);
|
||||
int serviceSetUser(SERVICE *service, char *user, char *auth);
|
||||
bool serviceSetFilters(SERVICE *service, char *filters);
|
||||
|
@ -31,6 +31,9 @@ MXS_BEGIN_DECLS
|
||||
struct dcb;
|
||||
struct service;
|
||||
struct mxs_filter_def;
|
||||
struct mxs_filter;
|
||||
struct mxs_filter_session;
|
||||
struct mxs_router_session;
|
||||
struct server;
|
||||
|
||||
typedef enum
|
||||
@ -86,19 +89,22 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
struct mxs_filter_def *filter;
|
||||
void *instance;
|
||||
void *session;
|
||||
struct mxs_filter *instance;
|
||||
struct mxs_filter_session *session;
|
||||
} SESSION_FILTER;
|
||||
|
||||
/**
|
||||
* The downstream element in the filter chain. This may refer to
|
||||
* another filter or to a router.
|
||||
*/
|
||||
struct mxs_filter;
|
||||
struct mxs_filter_session;
|
||||
|
||||
typedef struct mxs_downstream
|
||||
{
|
||||
void *instance;
|
||||
void *session;
|
||||
int32_t (*routeQuery)(void *instance, void *session, GWBUF *request);
|
||||
struct mxs_filter *instance;
|
||||
struct mxs_filter_session *session;
|
||||
int32_t (*routeQuery)(struct mxs_filter *instance, struct mxs_filter_session *session, GWBUF *request);
|
||||
} MXS_DOWNSTREAM;
|
||||
|
||||
/**
|
||||
@ -107,9 +113,9 @@ typedef struct mxs_downstream
|
||||
*/
|
||||
typedef struct mxs_upstream
|
||||
{
|
||||
void *instance;
|
||||
void *session;
|
||||
int32_t (*clientReply)(void *instance, void *session, GWBUF *response);
|
||||
struct mxs_filter *instance;
|
||||
struct mxs_filter_session *session;
|
||||
int32_t (*clientReply)(struct mxs_filter *instance, struct mxs_filter_session *session, GWBUF *response);
|
||||
int32_t (*error)(void *instance, void *session, void *);
|
||||
} MXS_UPSTREAM;
|
||||
|
||||
@ -129,7 +135,7 @@ typedef struct session
|
||||
mxs_session_state_t state; /*< Current descriptor state */
|
||||
size_t ses_id; /*< Unique session identifier */
|
||||
struct dcb *client_dcb; /*< The client connection */
|
||||
void *router_session; /*< The router instance data */
|
||||
struct mxs_router_session *router_session; /*< The router instance data */
|
||||
MXS_SESSION_STATS stats; /*< Session statistics */
|
||||
struct service *service; /*< The service this session is using */
|
||||
int n_filters; /*< Number of filter sessions */
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <maxscale/debug.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
@ -42,37 +43,73 @@ MXS_BEGIN_DECLS
|
||||
*/
|
||||
typedef struct spinlock
|
||||
{
|
||||
volatile int lock;/*< Is the lock held? */
|
||||
int lock; /*< Is the lock held? */
|
||||
#if SPINLOCK_PROFILE
|
||||
int spins; /*< Number of spins on this lock */
|
||||
int maxspins; /*< Max no of spins to acquire lock */
|
||||
int acquired; /*< No. of times lock was acquired */
|
||||
int waiting; /*< No. of threads acquiring this lock */
|
||||
int max_waiting; /*< Max no of threads waiting for lock */
|
||||
int contended; /*< No. of times acquire was contended */
|
||||
THREAD owner; /*< Last owner of this lock */
|
||||
uint64_t spins; /*< Number of spins on this lock */
|
||||
uint64_t maxspins; /*< Max no of spins to acquire lock */
|
||||
uint64_t acquired; /*< No. of times lock was acquired */
|
||||
uint64_t waiting; /*< No. of threads acquiring this lock */
|
||||
uint64_t max_waiting; /*< Max no of threads waiting for lock */
|
||||
uint64_t contended; /*< No. of times acquire was contended */
|
||||
THREAD owner; /*< Last owner of this lock */
|
||||
#endif
|
||||
} SPINLOCK;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE true
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE false
|
||||
#endif
|
||||
|
||||
#if SPINLOCK_PROFILE
|
||||
#define SPINLOCK_INIT { 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
#else
|
||||
#define SPINLOCK_INIT { 0 }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Debugging macro for testing the state of a spinlock.
|
||||
*
|
||||
* @attention ONLY to be used in debugging context.
|
||||
*/
|
||||
#define SPINLOCK_IS_LOCKED(l) ((l)->lock != 0 ? true : false)
|
||||
|
||||
/**
|
||||
* Initialise a spinlock.
|
||||
*
|
||||
* @param lock The spinlock to initialise.
|
||||
*/
|
||||
extern void spinlock_init(SPINLOCK *lock);
|
||||
|
||||
/**
|
||||
* Acquire a spinlock.
|
||||
*
|
||||
* @param lock The spinlock to acquire
|
||||
*/
|
||||
extern void spinlock_acquire(const SPINLOCK *lock);
|
||||
extern int spinlock_acquire_nowait(const SPINLOCK *lock);
|
||||
|
||||
/**
|
||||
* Acquire a spinlock if it is not already locked.
|
||||
*
|
||||
* @param lock The spinlock to acquire
|
||||
* @return True if the spinlock was acquired, otherwise false
|
||||
*/
|
||||
extern bool spinlock_acquire_nowait(const SPINLOCK *lock);
|
||||
|
||||
/*
|
||||
* Release a spinlock.
|
||||
*
|
||||
* @param lock The spinlock to release
|
||||
*/
|
||||
extern void spinlock_release(const SPINLOCK *lock);
|
||||
|
||||
/**
|
||||
* Report statistics on a spinlock. This only has an effect if the
|
||||
* spinlock code has been compiled with the SPINLOCK_PROFILE option set.
|
||||
*
|
||||
* NB A callback function is used to return the data rather than
|
||||
* merely printing to a DCB in order to avoid a dependency on the DCB
|
||||
* form the spinlock code and also to facilitate other uses of the
|
||||
* statistics reporting.
|
||||
*
|
||||
* @param lock The spinlock to report on
|
||||
* @param reporter The callback function to pass the statistics to
|
||||
* @param hdl A handle that is passed to the reporter function
|
||||
*/
|
||||
extern void spinlock_stats(const SPINLOCK *lock, void (*reporter)(void *, char *, int), void *hdl);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -58,7 +58,7 @@ typedef struct ssl_listener
|
||||
SSL_METHOD *method; /*< SSLv3 or TLS1.0/1.1/1.2 methods
|
||||
* see: https://www.openssl.org/docs/ssl/SSL_CTX_new.html */
|
||||
int ssl_cert_verify_depth; /*< SSL certificate verification depth */
|
||||
int ssl_method_type; /*< Which of the SSLv3 or TLS1.0/1.1/1.2 methods to use */
|
||||
ssl_method_type_t ssl_method_type; /*< Which of the SSLv3 or TLS1.0/1.1/1.2 methods to use */
|
||||
char *ssl_cert; /*< SSL certificate */
|
||||
char *ssl_key; /*< SSL private key */
|
||||
char *ssl_ca_cert; /*< SSL CA certificate */
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#include "../../server/core/maxscale/config.h"
|
||||
|
||||
int32_t qc_dummy_parse(GWBUF* querybuf, int32_t* pResult)
|
||||
int32_t qc_dummy_parse(GWBUF* querybuf, uint32_t collect, int32_t* pResult)
|
||||
{
|
||||
*pResult = QC_QUERY_INVALID;
|
||||
return QC_RESULT_OK;
|
||||
@ -148,6 +148,7 @@ extern "C"
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
"Dummy Query Classifier",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&qc,
|
||||
qc_dummy_process_init,
|
||||
qc_dummy_process_end,
|
||||
|
@ -136,7 +136,7 @@ bool ensure_query_is_parsed(GWBUF* query)
|
||||
return parsed;
|
||||
}
|
||||
|
||||
int32_t qc_mysql_parse(GWBUF* querybuf, int32_t* result)
|
||||
int32_t qc_mysql_parse(GWBUF* querybuf, uint32_t collect, int32_t* result)
|
||||
{
|
||||
bool parsed = ensure_query_is_parsed(querybuf);
|
||||
|
||||
@ -2752,6 +2752,7 @@ extern "C"
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
"Query classifier based upon MySQL Embedded",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&qc,
|
||||
qc_mysql_process_init,
|
||||
qc_mysql_process_end,
|
||||
|
@ -55,6 +55,8 @@ static inline bool qc_info_was_parsed(qc_parse_result_t status)
|
||||
typedef struct qc_sqlite_info
|
||||
{
|
||||
qc_parse_result_t status; // The validity of the information in this structure.
|
||||
uint32_t collect; // What information should be collected.
|
||||
uint32_t collected; // What information has been collected.
|
||||
const char* query; // The query passed to sqlite.
|
||||
size_t query_len; // The length of the query.
|
||||
|
||||
@ -128,18 +130,18 @@ typedef enum qc_token_position
|
||||
static void buffer_object_free(void* data);
|
||||
static char** copy_string_array(char** strings, int* pn);
|
||||
static void enlarge_string_array(size_t n, size_t len, char*** ppzStrings, size_t* pCapacity);
|
||||
static bool ensure_query_is_parsed(GWBUF* query);
|
||||
static bool ensure_query_is_parsed(GWBUF* query, uint32_t collect);
|
||||
static void free_field_infos(QC_FIELD_INFO* infos, size_t n_infos);
|
||||
static void free_string_array(char** sa);
|
||||
static QC_SQLITE_INFO* get_query_info(GWBUF* query);
|
||||
static QC_SQLITE_INFO* info_alloc(void);
|
||||
static QC_SQLITE_INFO* get_query_info(GWBUF* query, uint32_t collect);
|
||||
static QC_SQLITE_INFO* info_alloc(uint32_t collect);
|
||||
static void info_finish(QC_SQLITE_INFO* info);
|
||||
static void info_free(QC_SQLITE_INFO* info);
|
||||
static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info);
|
||||
static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info, uint32_t collect);
|
||||
static void log_invalid_data(GWBUF* query, const char* message);
|
||||
static bool parse_query(GWBUF* query);
|
||||
static bool parse_query(GWBUF* query, uint32_t collect);
|
||||
static void parse_query_string(const char* query, size_t len);
|
||||
static bool query_is_parsed(GWBUF* query);
|
||||
static bool query_is_parsed(GWBUF* query, uint32_t collect);
|
||||
static bool should_exclude(const char* zName, const ExprList* pExclude);
|
||||
static void update_field_info(QC_SQLITE_INFO* info,
|
||||
const char* database,
|
||||
@ -259,13 +261,13 @@ static void enlarge_string_array(size_t n, size_t len, char*** ppzStrings, size_
|
||||
}
|
||||
}
|
||||
|
||||
static bool ensure_query_is_parsed(GWBUF* query)
|
||||
static bool ensure_query_is_parsed(GWBUF* query, uint32_t collect)
|
||||
{
|
||||
bool parsed = query_is_parsed(query);
|
||||
bool parsed = query_is_parsed(query, collect);
|
||||
|
||||
if (!parsed)
|
||||
{
|
||||
parsed = parse_query(query);
|
||||
parsed = parse_query(query, collect);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
@ -315,11 +317,11 @@ static void free_string_array(char** sa)
|
||||
}
|
||||
}
|
||||
|
||||
static QC_SQLITE_INFO* get_query_info(GWBUF* query)
|
||||
static QC_SQLITE_INFO* get_query_info(GWBUF* query, uint32_t collect)
|
||||
{
|
||||
QC_SQLITE_INFO* info = NULL;
|
||||
|
||||
if (ensure_query_is_parsed(query))
|
||||
if (ensure_query_is_parsed(query, collect))
|
||||
{
|
||||
info = (QC_SQLITE_INFO*) gwbuf_get_buffer_object_data(query, GWBUF_PARSING_INFO);
|
||||
ss_dassert(info);
|
||||
@ -328,12 +330,12 @@ static QC_SQLITE_INFO* get_query_info(GWBUF* query)
|
||||
return info;
|
||||
}
|
||||
|
||||
static QC_SQLITE_INFO* info_alloc(void)
|
||||
static QC_SQLITE_INFO* info_alloc(uint32_t collect)
|
||||
{
|
||||
QC_SQLITE_INFO* info = MXS_MALLOC(sizeof(*info));
|
||||
MXS_ABORT_IF_NULL(info);
|
||||
|
||||
info_init(info);
|
||||
info_init(info, collect);
|
||||
|
||||
return info;
|
||||
}
|
||||
@ -359,11 +361,13 @@ static void info_free(QC_SQLITE_INFO* info)
|
||||
}
|
||||
}
|
||||
|
||||
static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info)
|
||||
static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info, uint32_t collect)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->status = QC_QUERY_INVALID;
|
||||
info->collect = collect;
|
||||
info->collected = 0;
|
||||
|
||||
info->type_mask = QUERY_TYPE_UNKNOWN;
|
||||
info->operation = QUERY_OP_UNDEFINED;
|
||||
@ -495,10 +499,10 @@ static void parse_query_string(const char* query, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_query(GWBUF* query)
|
||||
static bool parse_query(GWBUF* query, uint32_t collect)
|
||||
{
|
||||
bool parsed = false;
|
||||
ss_dassert(!query_is_parsed(query));
|
||||
ss_dassert(!query_is_parsed(query, collect));
|
||||
|
||||
if (GWBUF_IS_CONTIGUOUS(query))
|
||||
{
|
||||
@ -511,7 +515,29 @@ static bool parse_query(GWBUF* query)
|
||||
|
||||
if ((command == MYSQL_COM_QUERY) || (command == MYSQL_COM_STMT_PREPARE))
|
||||
{
|
||||
QC_SQLITE_INFO* info = info_alloc();
|
||||
QC_SQLITE_INFO* info =
|
||||
(QC_SQLITE_INFO*) gwbuf_get_buffer_object_data(query, GWBUF_PARSING_INFO);
|
||||
|
||||
if (info)
|
||||
{
|
||||
ss_dassert((~info->collect & collect) != 0);
|
||||
ss_dassert((~info->collected & collect) != 0);
|
||||
|
||||
// If we get here, then the statement has been parsed once, but
|
||||
// not all needed was collected. Now we turn on all blinkelichts to
|
||||
// ensure that a statement is parsed at most twice.
|
||||
info->collect = QC_COLLECT_ALL;
|
||||
}
|
||||
else
|
||||
{
|
||||
info = info_alloc(collect);
|
||||
|
||||
if (info)
|
||||
{
|
||||
// TODO: Add return value to gwbuf_add_buffer_object.
|
||||
gwbuf_add_buffer_object(query, GWBUF_PARSING_INFO, info, buffer_object_free);
|
||||
}
|
||||
}
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -532,10 +558,8 @@ static bool parse_query(GWBUF* query)
|
||||
info->type_mask |= QUERY_TYPE_PREPARE_STMT;
|
||||
}
|
||||
|
||||
// TODO: Add return value to gwbuf_add_buffer_object.
|
||||
// Always added; also when it was not recognized. If it was not recognized now,
|
||||
// it won't be if we try a second time.
|
||||
gwbuf_add_buffer_object(query, GWBUF_PARSING_INFO, info, buffer_object_free);
|
||||
info->collected = info->collect;
|
||||
|
||||
parsed = true;
|
||||
|
||||
this_thread.info = NULL;
|
||||
@ -566,9 +590,24 @@ static bool parse_query(GWBUF* query)
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static bool query_is_parsed(GWBUF* query)
|
||||
static bool query_is_parsed(GWBUF* query, uint32_t collect)
|
||||
{
|
||||
return query && GWBUF_IS_PARSED(query);
|
||||
bool rc = query && GWBUF_IS_PARSED(query);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
QC_SQLITE_INFO* info = (QC_SQLITE_INFO*) gwbuf_get_buffer_object_data(query, GWBUF_PARSING_INFO);
|
||||
ss_dassert(info);
|
||||
|
||||
if ((~info->collected & collect) != 0)
|
||||
{
|
||||
// The statement has been parsed once, but the needed information
|
||||
// was not collected at that time.
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -652,6 +691,13 @@ static void update_field_info(QC_SQLITE_INFO* info,
|
||||
{
|
||||
ss_dassert(column);
|
||||
|
||||
if (!(info->collect & QC_COLLECT_FIELDS) || (info->collected & QC_COLLECT_FIELDS))
|
||||
{
|
||||
// If field information should not be collected, or if field information
|
||||
// has already been collected, we just return.
|
||||
return;
|
||||
}
|
||||
|
||||
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage };
|
||||
|
||||
int i;
|
||||
@ -737,6 +783,13 @@ static void update_function_info(QC_SQLITE_INFO* info,
|
||||
{
|
||||
ss_dassert(name);
|
||||
|
||||
if (!(info->collect & QC_COLLECT_FUNCTIONS) || (info->collected & QC_COLLECT_FUNCTIONS))
|
||||
{
|
||||
// If function information should not be collected, or if function information
|
||||
// has already been collected, we just return.
|
||||
return;
|
||||
}
|
||||
|
||||
QC_FUNCTION_INFO item = { (char*)name, usage };
|
||||
|
||||
int i;
|
||||
@ -1220,37 +1273,46 @@ static void update_database_names(QC_SQLITE_INFO* info, const char* zDatabase)
|
||||
|
||||
static void update_names(QC_SQLITE_INFO* info, const char* zDatabase, const char* zTable)
|
||||
{
|
||||
char* zCopy = MXS_STRDUP(zTable);
|
||||
MXS_ABORT_IF_NULL(zCopy);
|
||||
// TODO: Is this call really needed. Check also sqlite3Dequote.
|
||||
exposed_sqlite3Dequote(zCopy);
|
||||
|
||||
enlarge_string_array(1, info->table_names_len, &info->table_names, &info->table_names_capacity);
|
||||
info->table_names[info->table_names_len++] = zCopy;
|
||||
info->table_names[info->table_names_len] = NULL;
|
||||
|
||||
if (zDatabase)
|
||||
if ((info->collect & QC_COLLECT_TABLES) && !(info->collected & QC_COLLECT_TABLES))
|
||||
{
|
||||
zCopy = MXS_MALLOC(strlen(zDatabase) + 1 + strlen(zTable) + 1);
|
||||
char* zCopy = MXS_STRDUP(zTable);
|
||||
MXS_ABORT_IF_NULL(zCopy);
|
||||
|
||||
strcpy(zCopy, zDatabase);
|
||||
strcat(zCopy, ".");
|
||||
strcat(zCopy, zTable);
|
||||
// TODO: Is this call really needed. Check also sqlite3Dequote.
|
||||
exposed_sqlite3Dequote(zCopy);
|
||||
|
||||
update_database_names(info, zDatabase);
|
||||
}
|
||||
else
|
||||
{
|
||||
zCopy = MXS_STRDUP(zCopy);
|
||||
MXS_ABORT_IF_NULL(zCopy);
|
||||
enlarge_string_array(1, info->table_names_len, &info->table_names, &info->table_names_capacity);
|
||||
info->table_names[info->table_names_len++] = zCopy;
|
||||
info->table_names[info->table_names_len] = NULL;
|
||||
|
||||
if (zDatabase)
|
||||
{
|
||||
zCopy = MXS_MALLOC(strlen(zDatabase) + 1 + strlen(zTable) + 1);
|
||||
MXS_ABORT_IF_NULL(zCopy);
|
||||
|
||||
strcpy(zCopy, zDatabase);
|
||||
strcat(zCopy, ".");
|
||||
strcat(zCopy, zTable);
|
||||
exposed_sqlite3Dequote(zCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
zCopy = MXS_STRDUP(zCopy);
|
||||
MXS_ABORT_IF_NULL(zCopy);
|
||||
}
|
||||
|
||||
enlarge_string_array(1, info->table_fullnames_len,
|
||||
&info->table_fullnames, &info->table_fullnames_capacity);
|
||||
info->table_fullnames[info->table_fullnames_len++] = zCopy;
|
||||
info->table_fullnames[info->table_fullnames_len] = NULL;
|
||||
}
|
||||
|
||||
enlarge_string_array(1, info->table_fullnames_len,
|
||||
&info->table_fullnames, &info->table_fullnames_capacity);
|
||||
info->table_fullnames[info->table_fullnames_len++] = zCopy;
|
||||
info->table_fullnames[info->table_fullnames_len] = NULL;
|
||||
if ((info->collect & QC_COLLECT_DATABASES) && !(info->collected & QC_COLLECT_DATABASES))
|
||||
{
|
||||
if (zDatabase)
|
||||
{
|
||||
update_database_names(info, zDatabase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_names_from_srclist(QC_SQLITE_INFO* info, const SrcList* pSrc)
|
||||
@ -1736,8 +1798,21 @@ void mxs_sqlite3StartTable(Parse *pParse, /* Parser context */
|
||||
update_names(info, NULL, name);
|
||||
}
|
||||
|
||||
info->created_table_name = MXS_STRDUP(info->table_names[0]);
|
||||
MXS_ABORT_IF_NULL(info->created_table_name);
|
||||
if (info->collect & QC_COLLECT_TABLES)
|
||||
{
|
||||
// If information is collected in several passes, then we may
|
||||
// this information already.
|
||||
if (!info->created_table_name)
|
||||
{
|
||||
info->created_table_name = MXS_STRDUP(info->table_names[0]);
|
||||
MXS_ABORT_IF_NULL(info->created_table_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(info->collect != info->collected);
|
||||
ss_dassert(strcmp(info->created_table_name, info->table_names[0]) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1899,11 +1974,21 @@ void maxscaleDeallocate(Parse* pParse, Token* pName)
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->type_mask = QUERY_TYPE_WRITE;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
// If information is collected in several passes, then we may
|
||||
// this information already.
|
||||
if (!info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(info->collect != info->collected);
|
||||
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1942,11 +2027,21 @@ void maxscaleExecute(Parse* pParse, Token* pName)
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->type_mask = QUERY_TYPE_WRITE;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
// If information is collected in several passes, then we may
|
||||
// this information already.
|
||||
if (!info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(info->collect != info->collected);
|
||||
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2312,32 +2407,42 @@ void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->type_mask = QUERY_TYPE_PREPARE_NAMED_STMT;
|
||||
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
// If information is collected in several passes, then we may
|
||||
// this information already.
|
||||
if (!info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
info->prepare_name = MXS_MALLOC(pName->n + 1);
|
||||
if (info->prepare_name)
|
||||
{
|
||||
memcpy(info->prepare_name, pName->z, pName->n);
|
||||
info->prepare_name[pName->n] = 0;
|
||||
}
|
||||
|
||||
size_t preparable_stmt_len = pStmt->n - 2;
|
||||
size_t payload_len = 1 + preparable_stmt_len;
|
||||
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
|
||||
|
||||
info->preparable_stmt = gwbuf_alloc(packet_len);
|
||||
|
||||
if (info->preparable_stmt)
|
||||
{
|
||||
uint8_t* ptr = GWBUF_DATA(info->preparable_stmt);
|
||||
// Payload length
|
||||
*ptr++ = payload_len;
|
||||
*ptr++ = (payload_len >> 8);
|
||||
*ptr++ = (payload_len >> 16);
|
||||
// Sequence id
|
||||
*ptr++ = 0x00;
|
||||
// Command
|
||||
*ptr++ = MYSQL_COM_QUERY;
|
||||
|
||||
memcpy(ptr, pStmt->z + 1, pStmt->n - 2);
|
||||
}
|
||||
}
|
||||
|
||||
size_t preparable_stmt_len = pStmt->n - 2;
|
||||
size_t payload_len = 1 + preparable_stmt_len;
|
||||
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
|
||||
|
||||
info->preparable_stmt = gwbuf_alloc(packet_len);
|
||||
|
||||
if (info->preparable_stmt)
|
||||
else
|
||||
{
|
||||
uint8_t* ptr = GWBUF_DATA(info->preparable_stmt);
|
||||
// Payload length
|
||||
*ptr++ = payload_len;
|
||||
*ptr++ = (payload_len >> 8);
|
||||
*ptr++ = (payload_len >> 16);
|
||||
// Sequence id
|
||||
*ptr++ = 0x00;
|
||||
// Command
|
||||
*ptr++ = MYSQL_COM_QUERY;
|
||||
|
||||
memcpy(ptr, pStmt->z + 1, pStmt->n - 2);
|
||||
ss_dassert(info->collect != info->collected);
|
||||
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2792,7 +2897,7 @@ static int32_t qc_sqlite_process_init(void);
|
||||
static void qc_sqlite_process_end(void);
|
||||
static int32_t qc_sqlite_thread_init(void);
|
||||
static void qc_sqlite_thread_end(void);
|
||||
static int32_t qc_sqlite_parse(GWBUF* query, int32_t* result);
|
||||
static int32_t qc_sqlite_parse(GWBUF* query, uint32_t collect, int32_t* result);
|
||||
static int32_t qc_sqlite_get_type_mask(GWBUF* query, uint32_t* typemask);
|
||||
static int32_t qc_sqlite_get_operation(GWBUF* query, int32_t* op);
|
||||
static int32_t qc_sqlite_get_created_table_name(GWBUF* query, char** name);
|
||||
@ -2952,7 +3057,7 @@ static int32_t qc_sqlite_thread_init(void)
|
||||
MXS_INFO("In-memory sqlite database successfully opened for thread %lu.",
|
||||
(unsigned long) pthread_self());
|
||||
|
||||
QC_SQLITE_INFO* info = info_alloc();
|
||||
QC_SQLITE_INFO* info = info_alloc(QC_COLLECT_ALL);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3010,13 +3115,13 @@ static void qc_sqlite_thread_end(void)
|
||||
this_thread.initialized = false;
|
||||
}
|
||||
|
||||
static int32_t qc_sqlite_parse(GWBUF* query, int32_t* result)
|
||||
static int32_t qc_sqlite_parse(GWBUF* query, uint32_t collect, int32_t* result)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(this_unit.initialized);
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, collect);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3038,7 +3143,7 @@ static int32_t qc_sqlite_get_type_mask(GWBUF* query, uint32_t* type_mask)
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*type_mask = QUERY_TYPE_UNKNOWN;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3068,7 +3173,7 @@ static int32_t qc_sqlite_get_operation(GWBUF* query, int32_t* op)
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*op = QUERY_OP_UNDEFINED;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3098,7 +3203,7 @@ static int32_t qc_sqlite_get_created_table_name(GWBUF* query, char** created_tab
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*created_table_name = NULL;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_TABLES);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3132,7 +3237,7 @@ static int32_t qc_sqlite_is_drop_table_query(GWBUF* query, int32_t* is_drop_tabl
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*is_drop_table = 0;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3166,7 +3271,7 @@ static int32_t qc_sqlite_get_table_names(GWBUF* query,
|
||||
|
||||
*table_names = NULL;
|
||||
*tblsize = 0;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_TABLES);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3227,7 +3332,7 @@ static int32_t qc_sqlite_query_has_clause(GWBUF* query, int32_t* has_clause)
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*has_clause = false;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3258,7 +3363,7 @@ static int32_t qc_sqlite_get_database_names(GWBUF* query, char*** database_names
|
||||
|
||||
*database_names = NULL;
|
||||
*sizep = 0;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_DATABASES);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3292,7 +3397,7 @@ static int32_t qc_sqlite_get_prepare_name(GWBUF* query, char** prepare_name)
|
||||
ss_dassert(this_thread.initialized);
|
||||
|
||||
*prepare_name = NULL;
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3328,7 +3433,7 @@ int32_t qc_sqlite_get_field_info(GWBUF* query, const QC_FIELD_INFO** infos, uint
|
||||
*infos = NULL;
|
||||
*n_infos = 0;
|
||||
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_FIELDS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3362,7 +3467,7 @@ int32_t qc_sqlite_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** infos
|
||||
*infos = NULL;
|
||||
*n_infos = 0;
|
||||
|
||||
QC_SQLITE_INFO* info = get_query_info(query);
|
||||
QC_SQLITE_INFO* info = get_query_info(query, QC_COLLECT_FUNCTIONS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3395,7 +3500,7 @@ int32_t qc_sqlite_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt)
|
||||
|
||||
*preparable_stmt = NULL;
|
||||
|
||||
QC_SQLITE_INFO* info = get_query_info(stmt);
|
||||
QC_SQLITE_INFO* info = get_query_info(stmt, QC_COLLECT_ESSENTIALS);
|
||||
|
||||
if (info)
|
||||
{
|
||||
@ -3452,6 +3557,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
"Query classifier using sqlite.",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&qc,
|
||||
qc_sqlite_process_init,
|
||||
qc_sqlite_process_end,
|
||||
|
@ -47,7 +47,7 @@ int main(int argc, char** argv)
|
||||
set_process_datadir(strdup("/tmp"));
|
||||
|
||||
qc_setup("qc_sqlite", NULL);
|
||||
qc_process_init();
|
||||
qc_process_init(QC_INIT_BOTH);
|
||||
|
||||
infile = fopen(argv[1], "rb");
|
||||
outfile = fopen(argv[2], "wb");
|
||||
@ -83,6 +83,6 @@ int main(int argc, char** argv)
|
||||
}
|
||||
fclose(infile);
|
||||
fclose(outfile);
|
||||
qc_process_end();
|
||||
qc_process_end(QC_INIT_BOTH);
|
||||
return 0;
|
||||
}
|
||||
|
@ -314,10 +314,10 @@ int main(int argc, char** argv)
|
||||
|
||||
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
|
||||
{
|
||||
if (qc_setup(lib, NULL) && qc_process_init())
|
||||
if (qc_setup(lib, NULL) && qc_process_init(QC_INIT_BOTH))
|
||||
{
|
||||
rc = run(input_name, expected_name);
|
||||
qc_process_end();
|
||||
qc_process_end(QC_INIT_BOTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -312,13 +312,13 @@ bool compare_parse(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1,
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||
int32_t rv1;
|
||||
pClassifier1->qc_parse(pCopy1, &rv1);
|
||||
pClassifier1->qc_parse(pCopy1, QC_COLLECT_ESSENTIALS, &rv1);
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &finish);
|
||||
update_time(&global.time1, start, finish);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||
int32_t rv2;
|
||||
pClassifier2->qc_parse(pCopy2, &rv2);
|
||||
pClassifier2->qc_parse(pCopy2, QC_COLLECT_ESSENTIALS, &rv2);
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &finish);
|
||||
update_time(&global.time2, start, finish);
|
||||
|
||||
|
@ -41,7 +41,7 @@ int main()
|
||||
|
||||
set_libdir(strdup("../qc_sqlite"));
|
||||
|
||||
if (qc_setup("qc_sqlite", NULL) && qc_process_init())
|
||||
if (qc_setup("qc_sqlite", NULL) && qc_process_init(QC_INIT_BOTH))
|
||||
{
|
||||
const char s[] = "SELECT @@global.max_allowed_packet";
|
||||
|
||||
@ -51,9 +51,9 @@ int main()
|
||||
// being of the opinion that the statement was not the one to be
|
||||
// classified and hence an alien parse-tree being passed to sqlite3's
|
||||
// code generator.
|
||||
qc_parse(stmt);
|
||||
qc_parse(stmt, QC_COLLECT_ALL);
|
||||
|
||||
qc_process_end();
|
||||
qc_process_end(QC_INIT_BOTH);
|
||||
|
||||
rv = EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -1,4 +1,47 @@
|
||||
add_library(maxscale-common SHARED adminusers.c alloc.c authenticator.c atomic.c buffer.c config.c config_runtime.c dcb.c filter.c filter.cc externcmd.c paths.c hashtable.c hint.c housekeeper.c load_utils.c log_manager.cc maxscale_pcre2.c misc.c mlist.c modutil.c monitor.c queuemanager.c query_classifier.c poll.c random_jkiss.c resultset.c router.cc secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c skygw_utils.cc statistics.c listener.c ssl.c mysql_utils.c mysql_binlog.c modulecmd.c )
|
||||
add_library(maxscale-common SHARED
|
||||
adminusers.cc
|
||||
alloc.cc
|
||||
atomic.cc
|
||||
authenticator.cc
|
||||
buffer.cc
|
||||
config.cc
|
||||
config_runtime.cc
|
||||
dcb.cc
|
||||
externcmd.cc
|
||||
filter.cc
|
||||
hashtable.cc
|
||||
hint.cc
|
||||
housekeeper.cc
|
||||
listener.cc
|
||||
load_utils.cc
|
||||
log_manager.cc
|
||||
maxscale_pcre2.cc
|
||||
misc.cc
|
||||
mlist.cc
|
||||
modulecmd.cc
|
||||
modutil.cc
|
||||
monitor.cc
|
||||
mysql_binlog.cc
|
||||
mysql_utils.cc
|
||||
paths.cc
|
||||
poll.cc
|
||||
query_classifier.cc
|
||||
queuemanager.cc
|
||||
random_jkiss.cc
|
||||
resultset.cc
|
||||
router.cc
|
||||
secrets.cc
|
||||
server.cc
|
||||
service.cc
|
||||
session.cc
|
||||
skygw_utils.cc
|
||||
spinlock.cc
|
||||
ssl.cc
|
||||
statistics.cc
|
||||
thread.cc
|
||||
users.cc
|
||||
utils.cc
|
||||
)
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES})
|
||||
|
@ -25,19 +25,8 @@
|
||||
|
||||
/**
|
||||
* @file adminusers.c - Administration user account management
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 18/07/13 Mark Riddoch Initial implementation
|
||||
* 23/07/13 Mark Riddoch Addition of error mechanism to add user
|
||||
* 23/05/16 Massimiliano Pinto admin_add_user and admin_remove_user
|
||||
* no longer accept password parameter
|
||||
* 02/09/16 Johan Wikman Enabled Linux accounts and MaxScale users
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static void initialise();
|
||||
|
||||
static USERS *loadLinuxUsers();
|
||||
@ -55,18 +44,18 @@ static USERS *linux_users = NULL;
|
||||
static USERS *inet_users = NULL;
|
||||
static int admin_init = 0;
|
||||
|
||||
static char *ADMIN_ERR_NOMEM = "Out of memory";
|
||||
static char *ADMIN_ERR_FILEOPEN = "Unable to create password file";
|
||||
static char *ADMIN_ERR_DUPLICATE = "Duplicate username specified";
|
||||
static char *ADMIN_ERR_USERNOTFOUND = "User not found";
|
||||
static char *ADMIN_ERR_AUTHENTICATION = "Authentication failed";
|
||||
static char *ADMIN_ERR_FILEAPPEND = "Unable to append to password file";
|
||||
static char *ADMIN_ERR_PWDFILEOPEN = "Failed to open password file";
|
||||
static char *ADMIN_ERR_TMPFILEOPEN = "Failed to open temporary password file";
|
||||
static char *ADMIN_ERR_PWDFILEACCESS = "Failed to access password file";
|
||||
static char *ADMIN_ERR_DELLASTUSER = "Deleting the last user is forbidden";
|
||||
static char *ADMIN_ERR_DELROOT = "Deleting the default admin user is forbidden";
|
||||
static char *ADMIN_SUCCESS = NULL;
|
||||
static const char *ADMIN_ERR_NOMEM = "Out of memory";
|
||||
static const char *ADMIN_ERR_FILEOPEN = "Unable to create password file";
|
||||
static const char *ADMIN_ERR_DUPLICATE = "Duplicate username specified";
|
||||
static const char *ADMIN_ERR_USERNOTFOUND = "User not found";
|
||||
static const char *ADMIN_ERR_AUTHENTICATION = "Authentication failed";
|
||||
static const char *ADMIN_ERR_FILEAPPEND = "Unable to append to password file";
|
||||
static const char *ADMIN_ERR_PWDFILEOPEN = "Failed to open password file";
|
||||
static const char *ADMIN_ERR_TMPFILEOPEN = "Failed to open temporary password file";
|
||||
static const char *ADMIN_ERR_PWDFILEACCESS = "Failed to access password file";
|
||||
static const char *ADMIN_ERR_DELLASTUSER = "Deleting the last user is forbidden";
|
||||
static const char *ADMIN_ERR_DELROOT = "Deleting the default admin user is forbidden";
|
||||
static const char *ADMIN_SUCCESS = NULL;
|
||||
|
||||
static const int LINELEN = 80;
|
||||
|
||||
@ -329,10 +318,10 @@ void dcb_print_users(DCB *dcb, const char* heading, USERS *users)
|
||||
|
||||
if (iter)
|
||||
{
|
||||
char *sep = "";
|
||||
const char *sep = "";
|
||||
const char *user;
|
||||
|
||||
while ((user = hashtable_next(iter)) != NULL)
|
||||
while ((user = (const char*)hashtable_next(iter)) != NULL)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s", sep, user);
|
||||
sep = ", ";
|
||||
@ -387,7 +376,7 @@ loadUsers(const char *fname)
|
||||
break;
|
||||
}
|
||||
|
||||
char *password;
|
||||
const char *password;
|
||||
char *colon = strchr(uname, ':');
|
||||
if (colon)
|
||||
{
|
@ -11,16 +11,10 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/atomic.h>
|
||||
|
||||
/**
|
||||
* @file atomic.c - Implementation of atomic operations for MaxScale
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 10/06/13 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
int
|
@ -643,7 +643,7 @@ gwbuf_rtrim(GWBUF *head, unsigned int n_bytes)
|
||||
return rval;
|
||||
}
|
||||
|
||||
void gwbuf_set_type(GWBUF* buf, gwbuf_type_t type)
|
||||
void gwbuf_set_type(GWBUF* buf, uint32_t type)
|
||||
{
|
||||
/** Set type consistenly to all buffers on the list */
|
||||
while (buf != NULL)
|
@ -72,6 +72,7 @@
|
||||
#include "maxscale/service.h"
|
||||
#include "maxscale/monitor.h"
|
||||
#include "maxscale/modules.h"
|
||||
#include "maxscale/router.h"
|
||||
|
||||
typedef struct duplicate_context
|
||||
{
|
||||
@ -123,6 +124,7 @@ static const char *service_params[] =
|
||||
"passwd", // DEPRECATE: See config_get_password.
|
||||
"password",
|
||||
"enable_root_user",
|
||||
"max_retry_interval",
|
||||
"max_connections",
|
||||
"max_queued_connections",
|
||||
"queued_connection_timeout",
|
||||
@ -266,7 +268,7 @@ static void duplicate_context_finish(DUPLICATE_CONTEXT* context)
|
||||
char* config_clean_string_list(const char* str)
|
||||
{
|
||||
size_t destsize = strlen(str) + 1;
|
||||
char *dest = MXS_MALLOC(destsize);
|
||||
char *dest = (char*)MXS_MALLOC(destsize);
|
||||
|
||||
if (dest)
|
||||
{
|
||||
@ -295,7 +297,7 @@ char* config_clean_string_list(const char* str)
|
||||
(PCRE2_SPTR) replace, PCRE2_ZERO_TERMINATED,
|
||||
(PCRE2_UCHAR*) dest, &destsize)) == PCRE2_ERROR_NOMEMORY)
|
||||
{
|
||||
char* tmp = MXS_REALLOC(dest, destsize * 2);
|
||||
char* tmp = (char*)MXS_REALLOC(dest, destsize * 2);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
MXS_FREE(dest);
|
||||
@ -621,7 +623,7 @@ config_load_and_process(const char* filename, bool (*process_config)(CONFIG_CONT
|
||||
|
||||
if (duplicate_context_init(&dcontext))
|
||||
{
|
||||
CONFIG_CONTEXT ccontext = {.object = ""};
|
||||
CONFIG_CONTEXT ccontext = {.object = (char*)""};
|
||||
|
||||
if (config_load_single_file(filename, &dcontext, &ccontext))
|
||||
{
|
||||
@ -1057,6 +1059,47 @@ SERVER* config_get_server(const MXS_CONFIG_PARAMETER *params, const char *key)
|
||||
return server_find_by_unique_name(value);
|
||||
}
|
||||
|
||||
int config_get_server_list(const MXS_CONFIG_PARAMETER *params, const char *key,
|
||||
SERVER*** output)
|
||||
{
|
||||
const char *value = config_get_value_string(params, key);
|
||||
char **server_names = NULL;
|
||||
int found = 0;
|
||||
const int n_names = config_parse_server_list(value, &server_names);
|
||||
if (n_names > 0)
|
||||
{
|
||||
SERVER** servers;
|
||||
found = server_find_by_unique_names(server_names, n_names, &servers);
|
||||
for (int i = 0; i < n_names; i++)
|
||||
{
|
||||
MXS_FREE(server_names[i]);
|
||||
}
|
||||
MXS_FREE(server_names);
|
||||
|
||||
if (found)
|
||||
{
|
||||
/* Fill in the result array */
|
||||
SERVER** result = (SERVER**)MXS_CALLOC(found, sizeof(SERVER*));
|
||||
if (result)
|
||||
{
|
||||
int res_ind = 0;
|
||||
for (int i = 0; i < n_names; i++)
|
||||
{
|
||||
if (servers[i])
|
||||
{
|
||||
result[res_ind] = servers[i];
|
||||
res_ind++;
|
||||
}
|
||||
}
|
||||
*output = result;
|
||||
ss_dassert(found == res_ind);
|
||||
}
|
||||
MXS_FREE(servers);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
char* config_copy_string(const MXS_CONFIG_PARAMETER *params, const char *key)
|
||||
{
|
||||
const char *value = config_get_value_string(params, key);
|
||||
@ -1073,7 +1116,7 @@ char* config_copy_string(const MXS_CONFIG_PARAMETER *params, const char *key)
|
||||
|
||||
MXS_CONFIG_PARAMETER* config_clone_param(const MXS_CONFIG_PARAMETER* param)
|
||||
{
|
||||
MXS_CONFIG_PARAMETER *p2 = MXS_MALLOC(sizeof(MXS_CONFIG_PARAMETER));
|
||||
MXS_CONFIG_PARAMETER *p2 = (MXS_CONFIG_PARAMETER*)MXS_MALLOC(sizeof(MXS_CONFIG_PARAMETER));
|
||||
|
||||
if (p2)
|
||||
{
|
||||
@ -1163,9 +1206,9 @@ config_get_feedback_data()
|
||||
|
||||
static struct
|
||||
{
|
||||
char* name;
|
||||
int priority;
|
||||
char* replacement;
|
||||
const char* name;
|
||||
int priority;
|
||||
const char* replacement;
|
||||
} lognames[] =
|
||||
{
|
||||
{ "log_messages", LOG_NOTICE, "log_notice" }, // Deprecated
|
||||
@ -1426,7 +1469,7 @@ SSL_LISTENER* make_ssl_structure (CONFIG_CONTEXT *obj, bool require_cert, int *e
|
||||
{
|
||||
if (!strcmp(ssl, "required"))
|
||||
{
|
||||
if ((new_ssl = MXS_CALLOC(1, sizeof(SSL_LISTENER))) == NULL)
|
||||
if ((new_ssl = (SSL_LISTENER*)MXS_CALLOC(1, sizeof(SSL_LISTENER))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@ -1853,7 +1896,7 @@ static void process_path_parameter(MXS_CONFIG_PARAMETER *param)
|
||||
{
|
||||
const char *mod_dir = get_module_configdir();
|
||||
size_t size = strlen(param->value) + strlen(mod_dir) + 3;
|
||||
char *value = MXS_MALLOC(size);
|
||||
char *value = (char*)MXS_MALLOC(size);
|
||||
MXS_ABORT_IF_NULL(value);
|
||||
|
||||
sprintf(value, "/%s/%s", mod_dir, param->value);
|
||||
@ -1971,38 +2014,6 @@ config_truth_value(const char *str)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *InternalRouters[] =
|
||||
{
|
||||
"debugcli",
|
||||
"cli",
|
||||
"maxinfo",
|
||||
"binlogrouter",
|
||||
"testroute",
|
||||
"avrorouter",
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the router is one of the special internal services that
|
||||
* MaxScale offers.
|
||||
*
|
||||
* @param router The router name
|
||||
* @return Non-zero if the router is in the InternalRouters table
|
||||
*/
|
||||
bool is_internal_service(const char *router)
|
||||
{
|
||||
if (router)
|
||||
{
|
||||
for (int i = 0; InternalRouters[i]; i++)
|
||||
{
|
||||
if (strcmp(router, InternalRouters[i]) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get the MAC address of first network interface
|
||||
*
|
||||
@ -2142,7 +2153,7 @@ config_get_release_string(char* release)
|
||||
if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0)
|
||||
{
|
||||
int fd;
|
||||
int k = 0;
|
||||
size_t k = 0;
|
||||
int skipindex = 0;
|
||||
int startindex = 0;
|
||||
|
||||
@ -2328,7 +2339,7 @@ bool config_has_duplicate_sections(const char* filename, DUPLICATE_CONTEXT* cont
|
||||
bool rval = false;
|
||||
|
||||
int size = 1024;
|
||||
char *buffer = MXS_MALLOC(size * sizeof(char));
|
||||
char *buffer = (char*)MXS_MALLOC(size * sizeof(char));
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
@ -2352,7 +2363,7 @@ bool config_has_duplicate_sections(const char* filename, DUPLICATE_CONTEXT* cont
|
||||
PCRE2_UCHAR section[len];
|
||||
pcre2_substring_copy_bynumber(context->mdata, 1, section, &len);
|
||||
|
||||
if (hashtable_add(context->hash, section, "") == 0)
|
||||
if (hashtable_add(context->hash, section, (char*)"") == 0)
|
||||
{
|
||||
MXS_ERROR("Duplicate section found: %s", section);
|
||||
rval = true;
|
||||
@ -2408,7 +2419,7 @@ int maxscale_getline(char** dest, int* size, FILE* file)
|
||||
{
|
||||
if (*size <= offset)
|
||||
{
|
||||
char* tmp = (char*) MXS_REALLOC(destptr, *size * 2);
|
||||
char* tmp = (char*)MXS_REALLOC(destptr, *size * 2);
|
||||
if (tmp)
|
||||
{
|
||||
destptr = tmp;
|
||||
@ -2542,19 +2553,37 @@ int create_new_service(CONFIG_CONTEXT *obj)
|
||||
char *retry = config_get_value(obj->parameters, "retry_on_failure");
|
||||
if (retry)
|
||||
{
|
||||
serviceSetRetryOnFailure(obj->element, retry);
|
||||
serviceSetRetryOnFailure(service, retry);
|
||||
}
|
||||
|
||||
char *enable_root_user = config_get_value(obj->parameters, "enable_root_user");
|
||||
if (enable_root_user)
|
||||
{
|
||||
serviceEnableRootUser(obj->element, config_truth_value(enable_root_user));
|
||||
serviceEnableRootUser(service, config_truth_value(enable_root_user));
|
||||
}
|
||||
|
||||
char *max_retry_interval = config_get_value(obj->parameters, "max_retry_interval");
|
||||
|
||||
if (max_retry_interval)
|
||||
{
|
||||
char *endptr;
|
||||
long val = strtol(max_retry_interval, &endptr, 10);
|
||||
|
||||
if (val && *endptr == '\0')
|
||||
{
|
||||
service_set_retry_interval(service, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Invalid value for 'max_retry_interval': %s", max_retry_interval);
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
|
||||
char *connection_timeout = config_get_value(obj->parameters, "connection_timeout");
|
||||
if (connection_timeout)
|
||||
{
|
||||
serviceSetTimeout(obj->element, atoi(connection_timeout));
|
||||
serviceSetTimeout(service, atoi(connection_timeout));
|
||||
}
|
||||
|
||||
const char *max_connections = config_get_value_string(obj->parameters, "max_connections");
|
||||
@ -2562,32 +2591,32 @@ int create_new_service(CONFIG_CONTEXT *obj)
|
||||
const char *queued_connection_timeout = config_get_value_string(obj->parameters, "queued_connection_timeout");
|
||||
if (strlen(max_connections))
|
||||
{
|
||||
serviceSetConnectionLimits(obj->element, atoi(max_connections),
|
||||
serviceSetConnectionLimits(service, atoi(max_connections),
|
||||
atoi(max_queued_connections), atoi(queued_connection_timeout));
|
||||
}
|
||||
|
||||
char *auth_all_servers = config_get_value(obj->parameters, "auth_all_servers");
|
||||
if (auth_all_servers)
|
||||
{
|
||||
serviceAuthAllServers(obj->element, config_truth_value(auth_all_servers));
|
||||
serviceAuthAllServers(service, config_truth_value(auth_all_servers));
|
||||
}
|
||||
|
||||
char *strip_db_esc = config_get_value(obj->parameters, "strip_db_esc");
|
||||
if (strip_db_esc)
|
||||
{
|
||||
serviceStripDbEsc(obj->element, config_truth_value(strip_db_esc));
|
||||
serviceStripDbEsc(service, config_truth_value(strip_db_esc));
|
||||
}
|
||||
|
||||
char *weightby = config_get_value(obj->parameters, "weightby");
|
||||
if (weightby)
|
||||
{
|
||||
serviceWeightBy(obj->element, weightby);
|
||||
serviceWeightBy(service, weightby);
|
||||
}
|
||||
|
||||
char *wildcard = config_get_value(obj->parameters, "localhost_match_wildcard_host");
|
||||
if (wildcard)
|
||||
{
|
||||
serviceEnableLocalhostMatchWildcardHost(obj->element, config_truth_value(wildcard));
|
||||
serviceEnableLocalhostMatchWildcardHost(service, config_truth_value(wildcard));
|
||||
}
|
||||
|
||||
char *user = config_get_value(obj->parameters, "user");
|
||||
@ -2595,9 +2624,9 @@ int create_new_service(CONFIG_CONTEXT *obj)
|
||||
|
||||
if (user && auth)
|
||||
{
|
||||
serviceSetUser(obj->element, user, auth);
|
||||
serviceSetUser(service, user, auth);
|
||||
}
|
||||
else if (!is_internal_service(router))
|
||||
else if (!rcap_type_required(service_get_capabilities(service), RCAP_TYPE_NO_AUTH))
|
||||
{
|
||||
error_count++;
|
||||
MXS_ERROR("Service '%s' is missing %s%s%s.",
|
||||
@ -2630,7 +2659,7 @@ int create_new_service(CONFIG_CONTEXT *obj)
|
||||
if (version_string[0] != '5')
|
||||
{
|
||||
size_t len = strlen(version_string) + strlen("5.5.5-") + 1;
|
||||
service->version_string = MXS_MALLOC(len);
|
||||
service->version_string = (char*)MXS_MALLOC(len);
|
||||
MXS_ABORT_IF_NULL(service->version_string);
|
||||
strcpy(service->version_string, "5.5.5-");
|
||||
strcat(service->version_string, version_string);
|
||||
@ -2655,7 +2684,7 @@ int create_new_service(CONFIG_CONTEXT *obj)
|
||||
if (mod)
|
||||
{
|
||||
config_add_defaults(obj, mod->parameters);
|
||||
service_add_parameters(obj->element, obj->parameters);
|
||||
service_add_parameters(service, obj->parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2716,7 +2745,7 @@ int create_new_server(CONFIG_CONTEXT *obj)
|
||||
|
||||
if (error_count == 0)
|
||||
{
|
||||
SERVER *server = obj->element;
|
||||
SERVER *server = (SERVER*)obj->element;
|
||||
|
||||
if (monuser && monpw)
|
||||
{
|
||||
@ -2774,7 +2803,7 @@ int create_new_server(CONFIG_CONTEXT *obj)
|
||||
{
|
||||
if (!is_normal_server_parameter(params->name))
|
||||
{
|
||||
server_add_parameter(obj->element, params->name, params->value);
|
||||
server_add_parameter(server, params->name, params->value);
|
||||
}
|
||||
params = params->next;
|
||||
}
|
||||
@ -2797,7 +2826,7 @@ int configure_new_service(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj)
|
||||
char *servers = config_get_value(obj->parameters, "servers");
|
||||
char *monitor = config_get_value(obj->parameters, "monitor");
|
||||
char *roptions = config_get_value(obj->parameters, "router_options");
|
||||
SERVICE *service = obj->element;
|
||||
SERVICE *service = (SERVICE*)obj->element;
|
||||
|
||||
if (service)
|
||||
{
|
||||
@ -2843,7 +2872,7 @@ int configure_new_service(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj)
|
||||
if (strcmp(trim(s), obj1->object) == 0 && obj1->element)
|
||||
{
|
||||
found = 1;
|
||||
serviceAddBackend(service, obj1->element);
|
||||
serviceAddBackend(service, (SERVER*)obj1->element);
|
||||
break;
|
||||
}
|
||||
obj1 = obj1->next;
|
||||
@ -2920,12 +2949,13 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
|
||||
if (error_count == 0)
|
||||
{
|
||||
MXS_MONITOR* monitor = (MXS_MONITOR*)obj->element;
|
||||
const MXS_MODULE *mod = get_module(module, MODULE_MONITOR);
|
||||
|
||||
if (mod)
|
||||
{
|
||||
config_add_defaults(obj, mod->parameters);
|
||||
monitorAddParameters(obj->element, obj->parameters);
|
||||
monitorAddParameters(monitor, obj->parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2941,7 +2971,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
Perhaps a greater minimum value should be added? */
|
||||
if (*endptr == '\0' && interval > 0)
|
||||
{
|
||||
monitorSetInterval(obj->element, (unsigned long)interval);
|
||||
monitorSetInterval(monitor, (unsigned long)interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2960,7 +2990,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
char *connect_timeout = config_get_value(obj->parameters, "backend_connect_timeout");
|
||||
if (connect_timeout)
|
||||
{
|
||||
if (!monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, atoi(connect_timeout)))
|
||||
if (!monitorSetNetworkTimeout(monitor, MONITOR_CONNECT_TIMEOUT, atoi(connect_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set backend_connect_timeout");
|
||||
error_count++;
|
||||
@ -2970,7 +3000,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
char *read_timeout = config_get_value(obj->parameters, "backend_read_timeout");
|
||||
if (read_timeout)
|
||||
{
|
||||
if (!monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, atoi(read_timeout)))
|
||||
if (!monitorSetNetworkTimeout(monitor, MONITOR_READ_TIMEOUT, atoi(read_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set backend_read_timeout");
|
||||
error_count++;
|
||||
@ -2980,7 +3010,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
char *write_timeout = config_get_value(obj->parameters, "backend_write_timeout");
|
||||
if (write_timeout)
|
||||
{
|
||||
if (!monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, atoi(write_timeout)))
|
||||
if (!monitorSetNetworkTimeout(monitor, MONITOR_WRITE_TIMEOUT, atoi(write_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set backend_write_timeout");
|
||||
error_count++;
|
||||
@ -3001,13 +3031,13 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
if (strcmp(trim(s), obj1->object) == 0 && obj->element && obj1->element)
|
||||
{
|
||||
found = 1;
|
||||
if (hashtable_add(monitorhash, obj1->object, "") == 0)
|
||||
if (hashtable_add(monitorhash, obj1->object, (char*)"") == 0)
|
||||
{
|
||||
MXS_WARNING("Multiple monitors are monitoring server [%s]. "
|
||||
"This will cause undefined behavior.",
|
||||
obj1->object);
|
||||
}
|
||||
monitorAddServer(obj->element, obj1->element);
|
||||
monitorAddServer(monitor, (SERVER*)obj1->element);
|
||||
}
|
||||
obj1 = obj1->next;
|
||||
}
|
||||
@ -3026,7 +3056,7 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
|
||||
char *passwd = config_get_password(obj->parameters);
|
||||
if (user && passwd)
|
||||
{
|
||||
monitorAddUser(obj->element, user, passwd);
|
||||
monitorAddUser(monitor, user, passwd);
|
||||
}
|
||||
else if (user)
|
||||
{
|
||||
@ -3130,6 +3160,7 @@ int create_new_filter(CONFIG_CONTEXT *obj)
|
||||
{
|
||||
if ((obj->element = filter_alloc(obj->object, module)))
|
||||
{
|
||||
MXS_FILTER_DEF* filter_def = (MXS_FILTER_DEF*)obj->element;
|
||||
char *options = config_get_value(obj->parameters, "options");
|
||||
if (options)
|
||||
{
|
||||
@ -3137,7 +3168,7 @@ int create_new_filter(CONFIG_CONTEXT *obj)
|
||||
char *s = strtok_r(options, ",", &lasts);
|
||||
while (s)
|
||||
{
|
||||
filter_add_option(obj->element, s);
|
||||
filter_add_option(filter_def, s);
|
||||
s = strtok_r(NULL, ",", &lasts);
|
||||
}
|
||||
}
|
||||
@ -3155,7 +3186,7 @@ int create_new_filter(CONFIG_CONTEXT *obj)
|
||||
|
||||
for (MXS_CONFIG_PARAMETER *p = obj->parameters; p; p = p->next)
|
||||
{
|
||||
filter_add_parameter(obj->element, p->name, p->value);
|
||||
filter_add_parameter(filter_def, p->name, p->value);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -3413,6 +3444,30 @@ bool config_param_is_valid(const MXS_MODULE_PARAM *params, const char *key,
|
||||
}
|
||||
break;
|
||||
|
||||
case MXS_MODULE_PARAM_SERVERLIST:
|
||||
if (context)
|
||||
{
|
||||
valid = true;
|
||||
char **server_names = NULL;
|
||||
int n_serv = config_parse_server_list(value, &server_names);
|
||||
if (n_serv > 0)
|
||||
{
|
||||
/* Check that every server name in the list is found in the config. */
|
||||
for (int i = 0; i < n_serv; i++)
|
||||
{
|
||||
if (valid &&
|
||||
!config_contains_type(context, server_names[i], "server"))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
MXS_FREE(server_names[i]);
|
||||
}
|
||||
MXS_FREE(server_names);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case MXS_MODULE_PARAM_PATH:
|
||||
valid = check_path_parameter(¶ms[i], value);
|
||||
break;
|
||||
@ -3427,3 +3482,72 @@ bool config_param_is_valid(const MXS_MODULE_PARAM *params, const char *key,
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
int config_parse_server_list(const char *servers, char ***output_array)
|
||||
{
|
||||
ss_dassert(servers);
|
||||
|
||||
/* First, check the string for the maximum amount of servers it
|
||||
* might contain by counting the commas. */
|
||||
int out_arr_size = 1;
|
||||
const char *pos = servers;
|
||||
while ((pos = strchr(pos, ',')) != NULL)
|
||||
{
|
||||
pos++;
|
||||
out_arr_size++;
|
||||
}
|
||||
char **results = (char**)MXS_CALLOC(out_arr_size, sizeof(char*));
|
||||
if (!results)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse the server names from the list. They are separated by ',' and will
|
||||
* be trimmed of whitespace. */
|
||||
char srv_list_tmp[strlen(servers) + 1];
|
||||
strcpy(srv_list_tmp, servers);
|
||||
trim(srv_list_tmp);
|
||||
|
||||
bool error = false;
|
||||
int output_ind = 0;
|
||||
char *lasts;
|
||||
char *s = strtok_r(srv_list_tmp, ",", &lasts);
|
||||
while (s)
|
||||
{
|
||||
char srv_name_tmp[strlen(s) + 1];
|
||||
strcpy(srv_name_tmp, s);
|
||||
trim(srv_name_tmp);
|
||||
if (strlen(srv_name_tmp) > 0)
|
||||
{
|
||||
results[output_ind] = MXS_STRDUP(srv_name_tmp);
|
||||
if (!results[output_ind])
|
||||
{
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
output_ind++;
|
||||
}
|
||||
s = strtok_r(NULL, ",", &lasts);
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
int i = 0;
|
||||
while (results[i])
|
||||
{
|
||||
MXS_FREE(results[i]);
|
||||
i++;
|
||||
}
|
||||
output_ind = 0;
|
||||
}
|
||||
|
||||
if (output_ind == 0)
|
||||
{
|
||||
MXS_FREE(results);
|
||||
}
|
||||
else
|
||||
{
|
||||
*output_array = results;
|
||||
}
|
||||
return output_ind;
|
||||
}
|
@ -348,7 +348,7 @@ static void add_monitor_defaults(MXS_MONITOR *monitor)
|
||||
{
|
||||
/** Inject the default module parameters in case we only deleted
|
||||
* a parameter */
|
||||
CONFIG_CONTEXT ctx = {.object = ""};
|
||||
CONFIG_CONTEXT ctx = {.object = (char*)""};
|
||||
const MXS_MODULE *mod = get_module(monitor->module_name, MODULE_MONITOR);
|
||||
|
||||
if (mod)
|
@ -99,7 +99,7 @@
|
||||
#include "maxscale/queuemanager.h"
|
||||
|
||||
/* A DCB with null values, used for initialization */
|
||||
static DCB dcb_initialized = DCB_INIT;
|
||||
static DCB dcb_initialized;
|
||||
|
||||
static DCB **all_dcbs;
|
||||
static SPINLOCK *all_dcbs_lock;
|
||||
@ -114,12 +114,18 @@ thread_local long next_timeout_check = 0;
|
||||
|
||||
void dcb_global_init()
|
||||
{
|
||||
dcb_initialized.dcb_chk_top = CHK_NUM_DCB;
|
||||
dcb_initialized.fd = DCBFD_CLOSED;
|
||||
dcb_initialized.state = DCB_STATE_ALLOC;
|
||||
dcb_initialized.ssl_state = SSL_HANDSHAKE_UNKNOWN;
|
||||
dcb_initialized.dcb_chk_tail = CHK_NUM_DCB;
|
||||
|
||||
int nthreads = config_threadcount();
|
||||
|
||||
if ((zombies = MXS_CALLOC(nthreads, sizeof(DCB*))) == NULL ||
|
||||
(all_dcbs = MXS_CALLOC(nthreads, sizeof(DCB*))) == NULL ||
|
||||
(all_dcbs_lock = MXS_CALLOC(nthreads, sizeof(SPINLOCK))) == NULL ||
|
||||
(nzombies = MXS_CALLOC(nthreads, sizeof(int))) == NULL)
|
||||
if ((zombies = (DCB**)MXS_CALLOC(nthreads, sizeof(DCB*))) == NULL ||
|
||||
(all_dcbs = (DCB**)MXS_CALLOC(nthreads, sizeof(DCB*))) == NULL ||
|
||||
(all_dcbs_lock = (SPINLOCK*)MXS_CALLOC(nthreads, sizeof(SPINLOCK))) == NULL ||
|
||||
(nzombies = (int*)MXS_CALLOC(nthreads, sizeof(int))) == NULL)
|
||||
{
|
||||
MXS_OOM();
|
||||
raise(SIGABRT);
|
||||
@ -1033,7 +1039,7 @@ dcb_read_SSL(DCB *dcb, GWBUF **head)
|
||||
}
|
||||
}
|
||||
|
||||
ss_dassert(gwbuf_length(*head) == (start_length + nreadtotal));
|
||||
ss_dassert(gwbuf_length(*head) == (size_t)(start_length + nreadtotal));
|
||||
|
||||
return nsingleread < 0 ? nsingleread : nreadtotal;
|
||||
}
|
||||
@ -2506,8 +2512,8 @@ int dcb_accept_SSL(DCB* dcb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ss_debug(char *remote = dcb->remote ? dcb->remote : "");
|
||||
ss_debug(char *user = dcb->user ? dcb->user : "");
|
||||
ss_debug(const char *remote = dcb->remote ? dcb->remote : "");
|
||||
ss_debug(const char *user = dcb->user ? dcb->user : "");
|
||||
|
||||
int ssl_rval = SSL_accept(dcb->ssl);
|
||||
|
||||
@ -2711,8 +2717,7 @@ dcb_accept(DCB *listener)
|
||||
if (client_conn.ss_family == AF_UNIX)
|
||||
{
|
||||
// client address
|
||||
// Should this be `localhost` like it is in the MariaDB server?
|
||||
client_dcb->remote = MXS_STRDUP_A("localhost_from_socket");
|
||||
client_dcb->remote = MXS_STRDUP_A("localhost");
|
||||
}
|
||||
else
|
||||
{
|
@ -217,7 +217,7 @@ bool externcmd_substitute_arg(EXTERNCMD* cmd, const char* match, const char* rep
|
||||
for (int i = 0; cmd->argv[i] && rval; i++)
|
||||
{
|
||||
size_t size = strlen(cmd->argv[i]);
|
||||
char* dest = MXS_MALLOC(size);
|
||||
char* dest = (char*)MXS_MALLOC(size);
|
||||
if (dest)
|
||||
{
|
||||
mxs_pcre2_result_t rc = mxs_pcre2_substitute(re, cmd->argv[i], replace, &dest, &size);
|
||||
@ -274,7 +274,7 @@ char* get_command(const char* str)
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
rval = MXS_MALLOC(len + 1);
|
||||
rval = (char*)MXS_MALLOC(len + 1);
|
||||
|
||||
if (rval)
|
||||
{
|
@ -1,474 +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: 2019-07-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file filter.c - A representation of a filter within MaxScale.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 29/05/14 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <maxscale/filter.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/spinlock.h>
|
||||
#include "maxscale/filter.h"
|
||||
|
||||
#include "maxscale/config.h"
|
||||
#include "maxscale/modules.h"
|
||||
|
||||
static SPINLOCK filter_spin = SPINLOCK_INIT; /**< Protects the list of all filters */
|
||||
static MXS_FILTER_DEF *allFilters = NULL; /**< The list of all filters */
|
||||
|
||||
static void filter_free_parameters(MXS_FILTER_DEF *filter);
|
||||
|
||||
/**
|
||||
* Allocate a new filter within MaxScale
|
||||
*
|
||||
*
|
||||
* @param name The filter name
|
||||
* @param module The module to load
|
||||
*
|
||||
* @return The newly created filter or NULL if an error occured
|
||||
*/
|
||||
MXS_FILTER_DEF *
|
||||
filter_alloc(const char *name, const char *module)
|
||||
{
|
||||
char* my_name = MXS_STRDUP(name);
|
||||
char* my_module = MXS_STRDUP(module);
|
||||
|
||||
MXS_FILTER_DEF *filter = (MXS_FILTER_DEF *)MXS_MALLOC(sizeof(MXS_FILTER_DEF));
|
||||
|
||||
if (!my_name || !my_module || !filter)
|
||||
{
|
||||
MXS_FREE(my_name);
|
||||
MXS_FREE(my_module);
|
||||
MXS_FREE(filter);
|
||||
return NULL;
|
||||
}
|
||||
filter->name = my_name;
|
||||
filter->module = my_module;
|
||||
filter->filter = NULL;
|
||||
filter->options = NULL;
|
||||
filter->obj = NULL;
|
||||
filter->parameters = NULL;
|
||||
|
||||
spinlock_init(&filter->spin);
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
filter->next = allFilters;
|
||||
allFilters = filter;
|
||||
spinlock_release(&filter_spin);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deallocate the specified filter
|
||||
*
|
||||
* @param filter The filter to deallocate
|
||||
* @return Returns true if the server was freed
|
||||
*/
|
||||
void
|
||||
filter_free(MXS_FILTER_DEF *filter)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
|
||||
if (filter)
|
||||
{
|
||||
/* First of all remove from the linked list */
|
||||
spinlock_acquire(&filter_spin);
|
||||
if (allFilters == filter)
|
||||
{
|
||||
allFilters = filter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = allFilters;
|
||||
while (ptr && ptr->next != filter)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (ptr)
|
||||
{
|
||||
ptr->next = filter->next;
|
||||
}
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
|
||||
/* Clean up session and free the memory */
|
||||
MXS_FREE(filter->name);
|
||||
MXS_FREE(filter->module);
|
||||
|
||||
if (filter->options)
|
||||
{
|
||||
for (int i = 0; filter->options[i]; i++)
|
||||
{
|
||||
MXS_FREE(filter->options[i]);
|
||||
}
|
||||
MXS_FREE(filter->options);
|
||||
}
|
||||
|
||||
filter_free_parameters(filter);
|
||||
|
||||
MXS_FREE(filter);
|
||||
}
|
||||
}
|
||||
|
||||
MXS_FILTER_DEF *
|
||||
filter_def_find(const char *name)
|
||||
{
|
||||
MXS_FILTER_DEF *filter;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
filter = allFilters;
|
||||
while (filter)
|
||||
{
|
||||
if (strcmp(filter->name, name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
filter = filter->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
return filter;
|
||||
}
|
||||
|
||||
const char* filter_def_get_name(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->name;
|
||||
}
|
||||
|
||||
const char* filter_def_get_module_name(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->module;
|
||||
}
|
||||
|
||||
MXS_FILTER* filter_def_get_instance(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a parameter to see if it is a standard filter parameter
|
||||
*
|
||||
* @param name Parameter name to check
|
||||
*/
|
||||
int
|
||||
filter_standard_parameter(const char *name)
|
||||
{
|
||||
if (strcmp(name, "type") == 0 || strcmp(name, "module") == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print all filters to a DCB
|
||||
*
|
||||
* Designed to be called within a debugger session in order
|
||||
* to display all active filters within MaxScale
|
||||
*/
|
||||
void
|
||||
dprintAllFilters(DCB *dcb)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
ptr = allFilters;
|
||||
while (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "Filter %p (%s)\n", ptr, ptr->name);
|
||||
dcb_printf(dcb, "\tModule: %s\n", ptr->module);
|
||||
if (ptr->options)
|
||||
{
|
||||
dcb_printf(dcb, "\tOptions: ");
|
||||
for (i = 0; ptr->options && ptr->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", ptr->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
if (ptr->obj && ptr->filter)
|
||||
{
|
||||
ptr->obj->diagnostics(ptr->filter, NULL, dcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "\tModule not loaded.\n");
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print filter details to a DCB
|
||||
*
|
||||
* Designed to be called within a debug CLI in order
|
||||
* to display all active filters in MaxScale
|
||||
*/
|
||||
void
|
||||
dprintFilter(DCB *dcb, const MXS_FILTER_DEF *filter)
|
||||
{
|
||||
int i;
|
||||
|
||||
dcb_printf(dcb, "Filter %p (%s)\n", filter, filter->name);
|
||||
dcb_printf(dcb, "\tModule: %s\n", filter->module);
|
||||
if (filter->options)
|
||||
{
|
||||
dcb_printf(dcb, "\tOptions: ");
|
||||
for (i = 0; filter->options && filter->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", filter->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
if (filter->obj && filter->filter)
|
||||
{
|
||||
filter->obj->diagnostics(filter->filter, NULL, dcb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all filters in a tabular form to a DCB
|
||||
*
|
||||
*/
|
||||
void
|
||||
dListFilters(DCB *dcb)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
ptr = allFilters;
|
||||
if (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "Filters\n");
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
dcb_printf(dcb, "%-19s | %-15s | Options\n",
|
||||
"Filter", "Module");
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "%-19s | %-15s | ",
|
||||
ptr->name, ptr->module);
|
||||
for (i = 0; ptr->options && ptr->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", ptr->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allFilters)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"--------------------+-----------------+----------------------------------------\n\n");
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a router option to a service
|
||||
*
|
||||
* @param filter The filter to add the option to
|
||||
* @param option The option string
|
||||
*/
|
||||
void
|
||||
filter_add_option(MXS_FILTER_DEF *filter, const char *option)
|
||||
{
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter->spin);
|
||||
if (filter->options == NULL)
|
||||
{
|
||||
filter->options = (char **)MXS_CALLOC(2, sizeof(char *));
|
||||
MXS_ABORT_IF_NULL(filter->options);
|
||||
filter->options[0] = MXS_STRDUP_A(option);
|
||||
filter->options[1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; filter->options[i]; i++)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
filter->options = (char **)MXS_REALLOC(filter->options, (i + 2) * sizeof(char *));
|
||||
MXS_ABORT_IF_NULL(filter->options);
|
||||
filter->options[i] = MXS_STRDUP_A(option);
|
||||
MXS_ABORT_IF_NULL(filter->options[i]);
|
||||
filter->options[i + 1] = NULL;
|
||||
}
|
||||
spinlock_release(&filter->spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a router parameter to a service
|
||||
*
|
||||
* @param filter The filter to add the parameter to
|
||||
* @param name The parameter name
|
||||
* @param value The parameter value
|
||||
*/
|
||||
void
|
||||
filter_add_parameter(MXS_FILTER_DEF *filter, const char *name, const char *value)
|
||||
{
|
||||
CONFIG_CONTEXT ctx = {.object = ""};
|
||||
|
||||
config_add_param(&ctx, name, value);
|
||||
ctx.parameters->next = filter->parameters;
|
||||
filter->parameters = ctx.parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free filter parameters
|
||||
* @param filter Filter whose parameters are to be freed
|
||||
*/
|
||||
static void filter_free_parameters(MXS_FILTER_DEF *filter)
|
||||
{
|
||||
config_parameter_free(filter->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a filter module for use and create an instance of it for a service.
|
||||
* @param filter Filter definition
|
||||
* @return True if module was successfully loaded, false if an error occurred
|
||||
*/
|
||||
bool filter_load(MXS_FILTER_DEF* filter)
|
||||
{
|
||||
bool rval = false;
|
||||
if (filter)
|
||||
{
|
||||
if (filter->filter)
|
||||
{
|
||||
// Already loaded and created.
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter->obj == NULL)
|
||||
{
|
||||
/* Filter not yet loaded */
|
||||
if ((filter->obj = load_module(filter->module, MODULE_FILTER)) == NULL)
|
||||
{
|
||||
MXS_ERROR("Failed to load filter module '%s'.", filter->module);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->obj)
|
||||
{
|
||||
ss_dassert(!filter->filter);
|
||||
|
||||
if ((filter->filter = (filter->obj->createInstance)(filter->name,
|
||||
filter->options,
|
||||
filter->parameters)))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to create filter '%s' instance.", filter->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the downstream filter chain for a filter.
|
||||
*
|
||||
* This will create the filter instance, loading the filter module, and
|
||||
* conenct the fitler into the downstream chain.
|
||||
*
|
||||
* @param filter The filter to add into the chain
|
||||
* @param session The client session
|
||||
* @param downstream The filter downstream of this filter
|
||||
* @return The downstream component for the next filter or NULL
|
||||
* if the filter could not be created
|
||||
*/
|
||||
MXS_DOWNSTREAM *
|
||||
filter_apply(MXS_FILTER_DEF *filter, MXS_SESSION *session, MXS_DOWNSTREAM *downstream)
|
||||
{
|
||||
MXS_DOWNSTREAM *me;
|
||||
|
||||
if ((me = (MXS_DOWNSTREAM *)MXS_CALLOC(1, sizeof(MXS_DOWNSTREAM))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
me->instance = filter->filter;
|
||||
me->routeQuery = (void *)(filter->obj->routeQuery);
|
||||
|
||||
if ((me->session = filter->obj->newSession(me->instance, session)) == NULL)
|
||||
{
|
||||
MXS_FREE(me);
|
||||
return NULL;
|
||||
}
|
||||
filter->obj->setDownstream(me->instance, me->session, downstream);
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a filter in the up stream filter chain for a session
|
||||
*
|
||||
* Note, the filter will have been created when the downstream chian was
|
||||
* previously setup.
|
||||
* Note all filters require to be in the upstream chain, so this routine
|
||||
* may skip a filter if it does not provide an upstream interface.
|
||||
*
|
||||
* @param filter The fitler to add to the chain
|
||||
* @param fsession The filter session
|
||||
* @param upstream The filter that should be upstream of this filter
|
||||
* @return The upstream component for the next filter
|
||||
*/
|
||||
MXS_UPSTREAM *
|
||||
filter_upstream(MXS_FILTER_DEF *filter, void *fsession, MXS_UPSTREAM *upstream)
|
||||
{
|
||||
MXS_UPSTREAM *me = NULL;
|
||||
|
||||
/*
|
||||
* The the filter has no setUpstream entry point then is does
|
||||
* not require to see results and can be left out of the chain.
|
||||
*/
|
||||
if (filter->obj->setUpstream == NULL)
|
||||
{
|
||||
return upstream;
|
||||
}
|
||||
|
||||
if (filter->obj->clientReply != NULL)
|
||||
{
|
||||
if ((me = (MXS_UPSTREAM *)MXS_CALLOC(1, sizeof(MXS_UPSTREAM))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
me->instance = filter->filter;
|
||||
me->session = fsession;
|
||||
me->clientReply = (void *)(filter->obj->clientReply);
|
||||
filter->obj->setUpstream(me->instance, me->session, upstream);
|
||||
}
|
||||
return me;
|
||||
}
|
@ -11,7 +11,462 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file filter.c - A representation of a filter within MaxScale.
|
||||
*/
|
||||
#include <maxscale/filter.h>
|
||||
#include <maxscale/filter.hh>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/spinlock.h>
|
||||
#include "maxscale/filter.h"
|
||||
|
||||
#include "maxscale/config.h"
|
||||
#include "maxscale/modules.h"
|
||||
|
||||
static SPINLOCK filter_spin = SPINLOCK_INIT; /**< Protects the list of all filters */
|
||||
static MXS_FILTER_DEF *allFilters = NULL; /**< The list of all filters */
|
||||
|
||||
static void filter_free_parameters(MXS_FILTER_DEF *filter);
|
||||
|
||||
/**
|
||||
* Allocate a new filter within MaxScale
|
||||
*
|
||||
*
|
||||
* @param name The filter name
|
||||
* @param module The module to load
|
||||
*
|
||||
* @return The newly created filter or NULL if an error occured
|
||||
*/
|
||||
MXS_FILTER_DEF *
|
||||
filter_alloc(const char *name, const char *module)
|
||||
{
|
||||
char* my_name = MXS_STRDUP(name);
|
||||
char* my_module = MXS_STRDUP(module);
|
||||
|
||||
MXS_FILTER_DEF *filter = (MXS_FILTER_DEF *)MXS_MALLOC(sizeof(MXS_FILTER_DEF));
|
||||
|
||||
if (!my_name || !my_module || !filter)
|
||||
{
|
||||
MXS_FREE(my_name);
|
||||
MXS_FREE(my_module);
|
||||
MXS_FREE(filter);
|
||||
return NULL;
|
||||
}
|
||||
filter->name = my_name;
|
||||
filter->module = my_module;
|
||||
filter->filter = NULL;
|
||||
filter->options = NULL;
|
||||
filter->obj = NULL;
|
||||
filter->parameters = NULL;
|
||||
|
||||
spinlock_init(&filter->spin);
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
filter->next = allFilters;
|
||||
allFilters = filter;
|
||||
spinlock_release(&filter_spin);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deallocate the specified filter
|
||||
*
|
||||
* @param filter The filter to deallocate
|
||||
* @return Returns true if the server was freed
|
||||
*/
|
||||
void
|
||||
filter_free(MXS_FILTER_DEF *filter)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
|
||||
if (filter)
|
||||
{
|
||||
/* First of all remove from the linked list */
|
||||
spinlock_acquire(&filter_spin);
|
||||
if (allFilters == filter)
|
||||
{
|
||||
allFilters = filter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = allFilters;
|
||||
while (ptr && ptr->next != filter)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (ptr)
|
||||
{
|
||||
ptr->next = filter->next;
|
||||
}
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
|
||||
/* Clean up session and free the memory */
|
||||
MXS_FREE(filter->name);
|
||||
MXS_FREE(filter->module);
|
||||
|
||||
if (filter->options)
|
||||
{
|
||||
for (int i = 0; filter->options[i]; i++)
|
||||
{
|
||||
MXS_FREE(filter->options[i]);
|
||||
}
|
||||
MXS_FREE(filter->options);
|
||||
}
|
||||
|
||||
filter_free_parameters(filter);
|
||||
|
||||
MXS_FREE(filter);
|
||||
}
|
||||
}
|
||||
|
||||
MXS_FILTER_DEF *
|
||||
filter_def_find(const char *name)
|
||||
{
|
||||
MXS_FILTER_DEF *filter;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
filter = allFilters;
|
||||
while (filter)
|
||||
{
|
||||
if (strcmp(filter->name, name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
filter = filter->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
return filter;
|
||||
}
|
||||
|
||||
const char* filter_def_get_name(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->name;
|
||||
}
|
||||
|
||||
const char* filter_def_get_module_name(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->module;
|
||||
}
|
||||
|
||||
MXS_FILTER* filter_def_get_instance(const MXS_FILTER_DEF* filter_def)
|
||||
{
|
||||
return filter_def->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a parameter to see if it is a standard filter parameter
|
||||
*
|
||||
* @param name Parameter name to check
|
||||
*/
|
||||
int
|
||||
filter_standard_parameter(const char *name)
|
||||
{
|
||||
if (strcmp(name, "type") == 0 || strcmp(name, "module") == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print all filters to a DCB
|
||||
*
|
||||
* Designed to be called within a debugger session in order
|
||||
* to display all active filters within MaxScale
|
||||
*/
|
||||
void
|
||||
dprintAllFilters(DCB *dcb)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
ptr = allFilters;
|
||||
while (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "Filter %p (%s)\n", ptr, ptr->name);
|
||||
dcb_printf(dcb, "\tModule: %s\n", ptr->module);
|
||||
if (ptr->options)
|
||||
{
|
||||
dcb_printf(dcb, "\tOptions: ");
|
||||
for (i = 0; ptr->options && ptr->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", ptr->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
if (ptr->obj && ptr->filter)
|
||||
{
|
||||
ptr->obj->diagnostics(ptr->filter, NULL, dcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "\tModule not loaded.\n");
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print filter details to a DCB
|
||||
*
|
||||
* Designed to be called within a debug CLI in order
|
||||
* to display all active filters in MaxScale
|
||||
*/
|
||||
void
|
||||
dprintFilter(DCB *dcb, const MXS_FILTER_DEF *filter)
|
||||
{
|
||||
int i;
|
||||
|
||||
dcb_printf(dcb, "Filter %p (%s)\n", filter, filter->name);
|
||||
dcb_printf(dcb, "\tModule: %s\n", filter->module);
|
||||
if (filter->options)
|
||||
{
|
||||
dcb_printf(dcb, "\tOptions: ");
|
||||
for (i = 0; filter->options && filter->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", filter->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
if (filter->obj && filter->filter)
|
||||
{
|
||||
filter->obj->diagnostics(filter->filter, NULL, dcb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all filters in a tabular form to a DCB
|
||||
*
|
||||
*/
|
||||
void
|
||||
dListFilters(DCB *dcb)
|
||||
{
|
||||
MXS_FILTER_DEF *ptr;
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter_spin);
|
||||
ptr = allFilters;
|
||||
if (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "Filters\n");
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
dcb_printf(dcb, "%-19s | %-15s | Options\n",
|
||||
"Filter", "Module");
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "%-19s | %-15s | ",
|
||||
ptr->name, ptr->module);
|
||||
for (i = 0; ptr->options && ptr->options[i]; i++)
|
||||
{
|
||||
dcb_printf(dcb, "%s ", ptr->options[i]);
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allFilters)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"--------------------+-----------------+----------------------------------------\n\n");
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a router option to a service
|
||||
*
|
||||
* @param filter The filter to add the option to
|
||||
* @param option The option string
|
||||
*/
|
||||
void
|
||||
filter_add_option(MXS_FILTER_DEF *filter, const char *option)
|
||||
{
|
||||
int i;
|
||||
|
||||
spinlock_acquire(&filter->spin);
|
||||
if (filter->options == NULL)
|
||||
{
|
||||
filter->options = (char **)MXS_CALLOC(2, sizeof(char *));
|
||||
MXS_ABORT_IF_NULL(filter->options);
|
||||
filter->options[0] = MXS_STRDUP_A(option);
|
||||
filter->options[1] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; filter->options[i]; i++)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
filter->options = (char **)MXS_REALLOC(filter->options, (i + 2) * sizeof(char *));
|
||||
MXS_ABORT_IF_NULL(filter->options);
|
||||
filter->options[i] = MXS_STRDUP_A(option);
|
||||
MXS_ABORT_IF_NULL(filter->options[i]);
|
||||
filter->options[i + 1] = NULL;
|
||||
}
|
||||
spinlock_release(&filter->spin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a router parameter to a service
|
||||
*
|
||||
* @param filter The filter to add the parameter to
|
||||
* @param name The parameter name
|
||||
* @param value The parameter value
|
||||
*/
|
||||
void
|
||||
filter_add_parameter(MXS_FILTER_DEF *filter, const char *name, const char *value)
|
||||
{
|
||||
CONFIG_CONTEXT ctx = {.object = (char*)""};
|
||||
|
||||
config_add_param(&ctx, name, value);
|
||||
ctx.parameters->next = filter->parameters;
|
||||
filter->parameters = ctx.parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free filter parameters
|
||||
* @param filter Filter whose parameters are to be freed
|
||||
*/
|
||||
static void filter_free_parameters(MXS_FILTER_DEF *filter)
|
||||
{
|
||||
config_parameter_free(filter->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a filter module for use and create an instance of it for a service.
|
||||
* @param filter Filter definition
|
||||
* @return True if module was successfully loaded, false if an error occurred
|
||||
*/
|
||||
bool filter_load(MXS_FILTER_DEF* filter)
|
||||
{
|
||||
bool rval = false;
|
||||
if (filter)
|
||||
{
|
||||
if (filter->filter)
|
||||
{
|
||||
// Already loaded and created.
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter->obj == NULL)
|
||||
{
|
||||
/* Filter not yet loaded */
|
||||
if ((filter->obj = (MXS_FILTER_OBJECT*)load_module(filter->module, MODULE_FILTER)) == NULL)
|
||||
{
|
||||
MXS_ERROR("Failed to load filter module '%s'.", filter->module);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->obj)
|
||||
{
|
||||
ss_dassert(!filter->filter);
|
||||
|
||||
if ((filter->filter = (filter->obj->createInstance)(filter->name,
|
||||
filter->options,
|
||||
filter->parameters)))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to create filter '%s' instance.", filter->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect the downstream filter chain for a filter.
|
||||
*
|
||||
* This will create the filter instance, loading the filter module, and
|
||||
* conenct the fitler into the downstream chain.
|
||||
*
|
||||
* @param filter The filter to add into the chain
|
||||
* @param session The client session
|
||||
* @param downstream The filter downstream of this filter
|
||||
* @return The downstream component for the next filter or NULL
|
||||
* if the filter could not be created
|
||||
*/
|
||||
MXS_DOWNSTREAM *
|
||||
filter_apply(MXS_FILTER_DEF *filter, MXS_SESSION *session, MXS_DOWNSTREAM *downstream)
|
||||
{
|
||||
MXS_DOWNSTREAM *me;
|
||||
|
||||
if ((me = (MXS_DOWNSTREAM *)MXS_CALLOC(1, sizeof(MXS_DOWNSTREAM))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
me->instance = filter->filter;
|
||||
me->routeQuery = filter->obj->routeQuery;
|
||||
|
||||
if ((me->session = filter->obj->newSession(me->instance, session)) == NULL)
|
||||
{
|
||||
MXS_FREE(me);
|
||||
return NULL;
|
||||
}
|
||||
filter->obj->setDownstream(me->instance, me->session, downstream);
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a filter in the up stream filter chain for a session
|
||||
*
|
||||
* Note, the filter will have been created when the downstream chian was
|
||||
* previously setup.
|
||||
* Note all filters require to be in the upstream chain, so this routine
|
||||
* may skip a filter if it does not provide an upstream interface.
|
||||
*
|
||||
* @param filter The fitler to add to the chain
|
||||
* @param fsession The filter session
|
||||
* @param upstream The filter that should be upstream of this filter
|
||||
* @return The upstream component for the next filter
|
||||
*/
|
||||
MXS_UPSTREAM *
|
||||
filter_upstream(MXS_FILTER_DEF *filter, MXS_FILTER_SESSION *fsession, MXS_UPSTREAM *upstream)
|
||||
{
|
||||
MXS_UPSTREAM *me = NULL;
|
||||
|
||||
/*
|
||||
* The the filter has no setUpstream entry point then is does
|
||||
* not require to see results and can be left out of the chain.
|
||||
*/
|
||||
if (filter->obj->setUpstream == NULL)
|
||||
{
|
||||
return upstream;
|
||||
}
|
||||
|
||||
if (filter->obj->clientReply != NULL)
|
||||
{
|
||||
if ((me = (MXS_UPSTREAM *)MXS_CALLOC(1, sizeof(MXS_UPSTREAM))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
me->instance = filter->filter;
|
||||
me->session = fsession;
|
||||
me->clientReply = filter->obj->clientReply;
|
||||
filter->obj->setUpstream(me->instance, me->session, upstream);
|
||||
}
|
||||
return me;
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
@ -58,4 +513,3 @@ void FilterSession::diagnostics(DCB* pDcb)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ static int pidfd = PIDFD_CLOSED;
|
||||
/**
|
||||
* exit flag for log flusher.
|
||||
*/
|
||||
static bool do_exit = FALSE;
|
||||
static bool do_exit = false;
|
||||
|
||||
/**
|
||||
* If MaxScale is started to run in daemon process the value is true.
|
||||
@ -138,6 +138,7 @@ static struct option long_options[] =
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"version-full", no_argument, 0, 'V'},
|
||||
{"help", no_argument, 0, '?'},
|
||||
{"connector_plugindir", required_argument, 0, 'H'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static bool syslog_configured = false;
|
||||
@ -920,6 +921,8 @@ static void usage(void)
|
||||
" -E, --execdir=PATH path to the maxscale and other executable files\n"
|
||||
" -F, --persistdir=PATH path to persisted configuration directory\n"
|
||||
" -M, --module_configdir=PATH path to module configuration directory\n"
|
||||
" -H, --connector_plugindir=PATH\n"
|
||||
" path to MariaDB Connector-C plugin directory\n"
|
||||
" -N, --language=PATH path to errmsg.sys file\n"
|
||||
" -P, --piddir=PATH path to PID file directory\n"
|
||||
" -R, --basedir=PATH base path for all other paths\n"
|
||||
@ -967,15 +970,25 @@ static void usage(void)
|
||||
*/
|
||||
void worker_thread_main(void* arg)
|
||||
{
|
||||
if (modules_thread_init())
|
||||
if (qc_thread_init(QC_INIT_SELF))
|
||||
{
|
||||
poll_waitevents(arg);
|
||||
if (modules_thread_init())
|
||||
{
|
||||
poll_waitevents(arg);
|
||||
|
||||
modules_thread_finish();
|
||||
modules_thread_finish();
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Could not perform thread initialization for all modules. Thread exits.");
|
||||
}
|
||||
|
||||
qc_thread_end(QC_INIT_SELF);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Could not perform thread initialization for all modules. Thread exits.");
|
||||
MXS_ERROR("Could not perform thread initialization for the "
|
||||
"internal query classifier. Thread exits.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1236,6 +1249,12 @@ bool set_dirs(const char *basedir)
|
||||
set_config_persistdir(path);
|
||||
}
|
||||
|
||||
if (rv && (rv = handle_path_arg(&path, basedir,
|
||||
"var/" MXS_DEFAULT_CONNECTOR_PLUGIN_SUBPATH, true, true)))
|
||||
{
|
||||
set_connector_plugindir(path);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1325,7 +1344,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:",
|
||||
while ((opt = getopt_long(argc, argv, "dcf:l:vVs:S:?L:D:C:B:U:A:P:G:N:E:F:M:H:",
|
||||
long_options, &option_index)) != -1)
|
||||
{
|
||||
bool succp = true;
|
||||
@ -1491,6 +1510,16 @@ int main(int argc, char **argv)
|
||||
succp = false;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (handle_path_arg(&tmp_path, optarg, NULL, true, false))
|
||||
{
|
||||
set_connector_plugindir(tmp_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
if (handle_path_arg(&tmp_path, optarg, NULL, true, true))
|
||||
{
|
||||
@ -1899,6 +1928,16 @@ int main(int argc, char **argv)
|
||||
|
||||
dcb_global_init();
|
||||
|
||||
/* Initialize the internal query classifier. The plugin will be initialized
|
||||
* via the module initialization below.
|
||||
*/
|
||||
if (!qc_process_init(QC_INIT_SELF))
|
||||
{
|
||||
MXS_ERROR("Failed to initialize the internal query classifier.");
|
||||
rc = MAXSCALE_INTERNALERROR;
|
||||
goto return_main;
|
||||
}
|
||||
|
||||
/* Init MaxScale modules */
|
||||
if (!modules_process_init())
|
||||
{
|
||||
@ -2017,6 +2056,11 @@ int main(int argc, char **argv)
|
||||
/*< Call finish on all modules. */
|
||||
modules_process_finish();
|
||||
|
||||
/* Finalize the internal query classifier. The plugin was finalized
|
||||
* via the module finalizarion above.
|
||||
*/
|
||||
qc_process_end(QC_INIT_SELF);
|
||||
|
||||
log_exit_status();
|
||||
MXS_NOTICE("MaxScale is shutting down.");
|
||||
|
||||
@ -2078,7 +2122,7 @@ int maxscale_shutdown()
|
||||
|
||||
static void log_flush_shutdown(void)
|
||||
{
|
||||
do_exit = TRUE;
|
||||
do_exit = true;
|
||||
}
|
||||
|
||||
|
||||
@ -2525,6 +2569,20 @@ static int cnf_preparser(void* data, const char* section, const char* name, cons
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(name, "connector_plugindir") == 0)
|
||||
{
|
||||
if (strcmp(get_connector_plugindir(), default_connector_plugindir) == 0)
|
||||
{
|
||||
if (handle_path_arg((char**)&tmp, (char*)value, NULL, true, false))
|
||||
{
|
||||
set_connector_plugindir(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(name, "persistdir") == 0)
|
||||
{
|
||||
if (strcmp(get_config_persistdir(), default_config_persistdir) == 0)
|
||||
|
@ -133,7 +133,7 @@ hashtable_alloc_real(HASHTABLE* target,
|
||||
|
||||
if (target == NULL)
|
||||
{
|
||||
if ((rval = MXS_MALLOC(sizeof(HASHTABLE))) == NULL)
|
||||
if ((rval = (HASHTABLE*)MXS_MALLOC(sizeof(HASHTABLE))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@ -52,7 +52,7 @@ hint_dup(const HINT *hint)
|
||||
ptr2->type = ptr1->type;
|
||||
if (ptr1->data)
|
||||
{
|
||||
ptr2->data = MXS_STRDUP_A(ptr1->data);
|
||||
ptr2->data = MXS_STRDUP_A((const char*)ptr1->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -60,7 +60,7 @@ hint_dup(const HINT *hint)
|
||||
}
|
||||
if (ptr1->value)
|
||||
{
|
||||
ptr2->value = MXS_STRDUP_A(ptr1->value);
|
||||
ptr2->value = MXS_STRDUP_A((const char*)ptr1->value);
|
||||
}
|
||||
else
|
||||
{
|
@ -52,7 +52,7 @@ static SPINLOCK tasklock = SPINLOCK_INIT;
|
||||
|
||||
static bool do_shutdown = 0;
|
||||
|
||||
long hkheartbeat = 0; /*< One heartbeat is 100 milliseconds */
|
||||
int64_t hkheartbeat = 0; /*< One heartbeat is 100 milliseconds */
|
||||
static THREAD hk_thr_handle;
|
||||
|
||||
static void hkthread(void *);
|
@ -90,7 +90,7 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
size_t realsize = size * nmemb;
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||
|
||||
void *data = MXS_REALLOC(mem->data, mem->size + realsize + 1);
|
||||
char *data = (char*)MXS_REALLOC(mem->data, mem->size + realsize + 1);
|
||||
|
||||
if (data == NULL)
|
||||
{
|
||||
@ -201,8 +201,8 @@ void *load_module(const char *module, const char *type)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *(*entry_point)() = sym;
|
||||
MXS_MODULE *mod_info = entry_point();
|
||||
void *(*entry_point)() = (void *(*)())sym;
|
||||
MXS_MODULE *mod_info = (MXS_MODULE*)entry_point();
|
||||
|
||||
if (!check_module(mod_info, type, module) ||
|
||||
(mod = register_module(module, type, dlhandle, mod_info)) == NULL)
|
||||
@ -730,7 +730,7 @@ do_http_post(GWBUF *buffer, void *cfg)
|
||||
FEEDBACK_CONF *feedback_config = (FEEDBACK_CONF *) cfg;
|
||||
|
||||
/* allocate first memory chunck for httpd servr reply */
|
||||
chunk.data = MXS_MALLOC(1); /* will be grown as needed by the realloc above */
|
||||
chunk.data = (char*)MXS_MALLOC(1); /* will be grown as needed by the realloc above */
|
||||
MXS_ABORT_IF_NULL(chunk.data);
|
||||
chunk.size = 0; /* no data at this point */
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/service.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief Create a new server
|
||||
*
|
||||
@ -176,3 +178,5 @@ bool runtime_create_monitor(const char *name, const char *module);
|
||||
* @return True if monitor was destroyed
|
||||
*/
|
||||
bool runtime_destroy_monitor(MXS_MONITOR *monitor);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -44,6 +44,8 @@ MXS_DOWNSTREAM *filter_apply(MXS_FILTER_DEF *filter_def, MXS_SESSION *session, M
|
||||
void filter_free(MXS_FILTER_DEF *filter_def);
|
||||
bool filter_load(MXS_FILTER_DEF *filter_def);
|
||||
int filter_standard_parameter(const char *name);
|
||||
MXS_UPSTREAM *filter_upstream(MXS_FILTER_DEF *filter_def, void *fsession, MXS_UPSTREAM *upstream);
|
||||
MXS_UPSTREAM *filter_upstream(MXS_FILTER_DEF *filter_def,
|
||||
MXS_FILTER_SESSION *fsession,
|
||||
MXS_UPSTREAM *upstream);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -41,7 +41,7 @@ typedef enum
|
||||
MXS_MONITOR *monitor_alloc(char *, char *);
|
||||
void monitor_free(MXS_MONITOR *);
|
||||
|
||||
void monitorStart(MXS_MONITOR *, void*);
|
||||
void monitorStart(MXS_MONITOR *, const MXS_CONFIG_PARAMETER*);
|
||||
void monitorStop(MXS_MONITOR *);
|
||||
void monitorStopAll();
|
||||
void monitorStartAll();
|
||||
|
39
server/core/maxscale/query_classifier.h
Normal file
39
server/core/maxscale/query_classifier.h
Normal file
@ -0,0 +1,39 @@
|
||||
#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: 2019-07-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/cdefs.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
typedef enum qc_trx_parse_using
|
||||
{
|
||||
QC_TRX_PARSE_USING_QC, /**< Use the query classifier. */
|
||||
QC_TRX_PARSE_USING_PARSER, /**< Use custom parser. */
|
||||
} qc_trx_parse_using_t;
|
||||
|
||||
/**
|
||||
* Returns the type bitmask of transaction related statements.
|
||||
*
|
||||
* @param stmt A COM_QUERY or COM_STMT_PREPARE packet.
|
||||
* @param use What method should be used.
|
||||
*
|
||||
* @return The relevant type bits if the statement is transaction
|
||||
* related, otherwise 0.
|
||||
*
|
||||
* @see qc_get_trx_type_mask
|
||||
*/
|
||||
uint32_t qc_get_trx_type_mask_using(GWBUF* stmt, qc_trx_parse_using_t use);
|
||||
|
||||
MXS_END_DECLS
|
@ -129,6 +129,14 @@ void service_update(SERVICE *service, char *router, char *user, char *auth);
|
||||
*/
|
||||
void service_add_parameters(SERVICE *service, const MXS_CONFIG_PARAMETER *param);
|
||||
|
||||
/**
|
||||
* @brief Set listener rebinding interval
|
||||
*
|
||||
* @param service Service to configure
|
||||
* @param value String value o
|
||||
*/
|
||||
void service_set_retry_interval(SERVICE *service, int value);
|
||||
|
||||
/**
|
||||
* Internal debugging diagnostics
|
||||
*/
|
||||
|
@ -41,8 +41,7 @@ typedef enum
|
||||
} SESSIONLISTFILTER;
|
||||
|
||||
int session_isvalid(MXS_SESSION *);
|
||||
int session_reply(void *inst, void *session, GWBUF *data);
|
||||
char *session_state(mxs_session_state_t);
|
||||
const char *session_state(mxs_session_state_t);
|
||||
bool session_link_dcb(MXS_SESSION *, struct dcb *);
|
||||
|
||||
RESULTSET *sessionGetList(SESSIONLISTFILTER);
|
||||
|
841
server/core/maxscale/trxboundaryparser.hh
Normal file
841
server/core/maxscale/trxboundaryparser.hh
Normal file
@ -0,0 +1,841 @@
|
||||
#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: 2019-07-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 <ctype.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
#define TBP_EXPECT_TOKEN(string_literal) string_literal, (sizeof(string_literal) - 1)
|
||||
|
||||
/**
|
||||
* @class TrxBoundaryParser
|
||||
*
|
||||
* @ TrxBoundaryParser is a class capable of parsing and returning the
|
||||
* correct type mask of statements affecting the transaction state and
|
||||
* autocommit mode.
|
||||
*
|
||||
* The class is intended to be used in context where the performance is
|
||||
* of utmost importance; consequently it is defined in its entirety
|
||||
* in the header to allow for aggressive inlining.
|
||||
*/
|
||||
class TrxBoundaryParser
|
||||
{
|
||||
public:
|
||||
enum token_t
|
||||
{
|
||||
TK_AUTOCOMMIT,
|
||||
TK_BEGIN,
|
||||
TK_COMMA,
|
||||
TK_COMMIT,
|
||||
TK_CONSISTENT,
|
||||
TK_DOT,
|
||||
TK_EQ,
|
||||
TK_FALSE,
|
||||
TK_GLOBAL,
|
||||
TK_GLOBAL_VAR,
|
||||
TK_ONE,
|
||||
TK_ONLY,
|
||||
TK_READ,
|
||||
TK_ROLLBACK,
|
||||
TK_SESSION,
|
||||
TK_SESSION_VAR,
|
||||
TK_SET,
|
||||
TK_SNAPSHOT,
|
||||
TK_START,
|
||||
TK_TRANSACTION,
|
||||
TK_TRUE,
|
||||
TK_WITH,
|
||||
TK_WORK,
|
||||
TK_WRITE,
|
||||
TK_ZERO,
|
||||
|
||||
PARSER_UNKNOWN_TOKEN,
|
||||
PARSER_EXHAUSTED,
|
||||
};
|
||||
|
||||
/**
|
||||
* TrxBoundaryParser is not thread-safe. As a very lightweight class,
|
||||
* the intention is that an instance is created on the stack whenever
|
||||
* parsing needs to be performed.
|
||||
*
|
||||
* @code
|
||||
* void f(GWBUF *pBuf)
|
||||
* {
|
||||
* TrxBoundaryParser tbp;
|
||||
*
|
||||
* uint32_t type_mask = tbp.parse(pBuf);
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
TrxBoundaryParser()
|
||||
: m_pSql(NULL)
|
||||
, m_len(0)
|
||||
, m_pI(NULL)
|
||||
, m_pEnd(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type mask of a statement, provided the statement affects
|
||||
* transaction state or autocommit mode.
|
||||
*
|
||||
* @param pSql SQL statament.
|
||||
* @param len Length of pSql.
|
||||
*
|
||||
* @return The corresponding type mask or 0, if the statement does not
|
||||
* affect transaction state or autocommit mode.
|
||||
*/
|
||||
uint32_t type_mask_of(const char* pSql, size_t len)
|
||||
{
|
||||
uint32_t type_mask = 0;
|
||||
|
||||
m_pSql = pSql;
|
||||
m_len = len;
|
||||
|
||||
m_pI = m_pSql;
|
||||
m_pEnd = m_pI + m_len;
|
||||
|
||||
return parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type mask of a statement, provided the statement affects
|
||||
* transaction state or autocommit mode.
|
||||
*
|
||||
* @param pBuf A COM_QUERY
|
||||
*
|
||||
* @return The corresponding type mask or 0, if the statement does not
|
||||
* affect transaction state or autocommit mode.
|
||||
*/
|
||||
uint32_t type_mask_of(GWBUF* pBuf)
|
||||
{
|
||||
uint32_t type_mask = 0;
|
||||
|
||||
char* pSql;
|
||||
if (modutil_extract_SQL(pBuf, &pSql, &m_len))
|
||||
{
|
||||
m_pSql = pSql;
|
||||
m_pI = m_pSql;
|
||||
m_pEnd = m_pI + m_len;
|
||||
|
||||
type_mask = parse();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
private:
|
||||
enum token_required_t
|
||||
{
|
||||
TOKEN_REQUIRED,
|
||||
TOKEN_NOT_REQUIRED,
|
||||
};
|
||||
|
||||
void log_unexpected()
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
MXS_NOTICE("Transaction tracking: In statement '%.*s', unexpected token at '%.*s'.",
|
||||
(int)m_len, m_pSql, (int)(m_pEnd - m_pI), m_pI);
|
||||
#endif
|
||||
}
|
||||
|
||||
void log_exhausted()
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
MXS_NOTICE("Transaction tracking: More tokens expected in statement '%.*s'.", (int)m_len, m_pSql);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t parse()
|
||||
{
|
||||
uint32_t type_mask = 0;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_BEGIN:
|
||||
type_mask = parse_begin(type_mask);
|
||||
break;
|
||||
|
||||
case TK_COMMIT:
|
||||
type_mask = parse_commit(type_mask);
|
||||
break;
|
||||
|
||||
case TK_ROLLBACK:
|
||||
type_mask = parse_rollback(type_mask);
|
||||
break;
|
||||
|
||||
case TK_START:
|
||||
type_mask = parse_start(type_mask);
|
||||
break;
|
||||
|
||||
case TK_SET:
|
||||
type_mask = parse_set(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_begin(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_BEGIN_TRX;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_WORK:
|
||||
type_mask = parse_work(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_commit(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_COMMIT;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_WORK:
|
||||
type_mask = parse_work(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_only(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_READ;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_COMMA:
|
||||
type_mask = parse_transaction(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_read(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_ONLY:
|
||||
type_mask = parse_only(type_mask);
|
||||
break;
|
||||
|
||||
case TK_WRITE:
|
||||
type_mask = parse_write(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
type_mask = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_rollback(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_ROLLBACK;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_WORK:
|
||||
type_mask = parse_work(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_set_autocommit(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_EQ:
|
||||
token = next_token(TOKEN_REQUIRED);
|
||||
if (token == TK_ONE || token == TK_TRUE)
|
||||
{
|
||||
type_mask |= (QUERY_TYPE_COMMIT | QUERY_TYPE_ENABLE_AUTOCOMMIT);
|
||||
}
|
||||
else if (token == TK_ZERO || token == TK_FALSE)
|
||||
{
|
||||
type_mask = (QUERY_TYPE_BEGIN_TRX | QUERY_TYPE_DISABLE_AUTOCOMMIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
type_mask = 0;
|
||||
|
||||
if (token != PARSER_EXHAUSTED)
|
||||
{
|
||||
log_unexpected();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
type_mask = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_set(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_AUTOCOMMIT:
|
||||
type_mask = parse_set_autocommit(type_mask);
|
||||
break;
|
||||
|
||||
case TK_GLOBAL:
|
||||
case TK_SESSION:
|
||||
token = next_token(TOKEN_REQUIRED);
|
||||
if (token == TK_AUTOCOMMIT)
|
||||
{
|
||||
type_mask = parse_set_autocommit(type_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
type_mask = 0;
|
||||
if (token != PARSER_EXHAUSTED)
|
||||
{
|
||||
log_unexpected();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_GLOBAL_VAR:
|
||||
case TK_SESSION_VAR:
|
||||
token = next_token(TOKEN_REQUIRED);
|
||||
if (token == TK_DOT)
|
||||
{
|
||||
token = next_token(TOKEN_REQUIRED);
|
||||
if (token == TK_AUTOCOMMIT)
|
||||
{
|
||||
type_mask = parse_set_autocommit(type_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
type_mask = 0;
|
||||
if (token != PARSER_EXHAUSTED)
|
||||
{
|
||||
log_unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type_mask = 0;
|
||||
if (token != PARSER_EXHAUSTED)
|
||||
{
|
||||
log_unexpected();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
type_mask = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_start(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_TRANSACTION:
|
||||
type_mask = parse_transaction(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
type_mask = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_transaction(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_BEGIN_TRX;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_READ:
|
||||
type_mask = parse_read(type_mask);
|
||||
break;
|
||||
|
||||
case TK_WITH:
|
||||
type_mask = parse_with_consistent_snapshot(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_with_consistent_snapshot(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
if (token == TK_CONSISTENT)
|
||||
{
|
||||
token = next_token(TOKEN_REQUIRED);
|
||||
|
||||
if (token == TK_SNAPSHOT)
|
||||
{
|
||||
token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_COMMA:
|
||||
type_mask = parse_transaction(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_work(uint32_t type_mask)
|
||||
{
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t parse_write(uint32_t type_mask)
|
||||
{
|
||||
type_mask |= QUERY_TYPE_WRITE;
|
||||
|
||||
token_t token = next_token();
|
||||
|
||||
switch (token)
|
||||
{
|
||||
case TK_COMMA:
|
||||
type_mask = parse_transaction(type_mask);
|
||||
break;
|
||||
|
||||
case PARSER_EXHAUSTED:
|
||||
break;
|
||||
|
||||
default:
|
||||
type_mask = 0;
|
||||
log_unexpected();
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
inline bool is_next_alpha(char uc, int offset = 1) const
|
||||
{
|
||||
ss_dassert(uc >= 'A' && uc <= 'Z');
|
||||
|
||||
char lc = uc + ('a' - 'A');
|
||||
|
||||
return
|
||||
((m_pI + offset) < m_pEnd) &&
|
||||
((*(m_pI + offset) == uc) || (*(m_pI + offset) == lc));
|
||||
}
|
||||
|
||||
bool is_next_char(char c, int offset = 1) const
|
||||
{
|
||||
return ((m_pI + offset) < m_pEnd) && (*(m_pI + offset) == c);
|
||||
}
|
||||
|
||||
bool peek_next_char(char* pC) const
|
||||
{
|
||||
bool rc = (m_pI + 1 < m_pEnd);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
*pC = *(m_pI + 1);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Significantly faster than library version.
|
||||
static char toupper(char c)
|
||||
{
|
||||
return (c >= 'a' && c <='z') ? c - ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
token_t expect_token(const char* zWord, int len, token_t token)
|
||||
{
|
||||
const char* pI = m_pI;
|
||||
const char* pEnd = zWord + len;
|
||||
|
||||
while ((pI < m_pEnd) && (zWord < pEnd) && (toupper(*pI) == *zWord))
|
||||
{
|
||||
++pI;
|
||||
++zWord;
|
||||
}
|
||||
|
||||
if (zWord == pEnd)
|
||||
{
|
||||
if ((pI == m_pEnd) || (!isalpha(*pI))) // Handwritten isalpha not faster than library version.
|
||||
{
|
||||
m_pI = pI;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = PARSER_UNKNOWN_TOKEN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token = PARSER_UNKNOWN_TOKEN;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void bypass_whitespace()
|
||||
{
|
||||
m_pI = modutil_MySQL_bypass_whitespace(const_cast<char*>(m_pI), m_pEnd - m_pI);
|
||||
}
|
||||
|
||||
token_t next_token(token_required_t required = TOKEN_NOT_REQUIRED)
|
||||
{
|
||||
token_t token = PARSER_UNKNOWN_TOKEN;
|
||||
|
||||
bypass_whitespace();
|
||||
|
||||
if (m_pI == m_pEnd)
|
||||
{
|
||||
token = PARSER_EXHAUSTED;
|
||||
}
|
||||
else if (*m_pI == ';')
|
||||
{
|
||||
++m_pI;
|
||||
|
||||
while ((m_pI != m_pEnd) && isspace(*m_pI))
|
||||
{
|
||||
++m_pI;
|
||||
}
|
||||
|
||||
if (m_pI != m_pEnd)
|
||||
{
|
||||
MXS_WARNING("Non-space data found after semi-colon: '%.*s'.",
|
||||
(int)(m_pEnd - m_pI), m_pI);
|
||||
}
|
||||
|
||||
token = PARSER_EXHAUSTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (*m_pI)
|
||||
{
|
||||
case '@':
|
||||
if (is_next_alpha('A', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("@@AUTOCOMMIT"), TK_AUTOCOMMIT);
|
||||
}
|
||||
else if (is_next_alpha('S', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("@@SESSION"), TK_SESSION_VAR);
|
||||
}
|
||||
else if (is_next_alpha('G', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("@@GLOBAL"), TK_GLOBAL_VAR);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
token = expect_token(TBP_EXPECT_TOKEN("AUTOCOMMIT"), TK_AUTOCOMMIT);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
case 'B':
|
||||
token = expect_token(TBP_EXPECT_TOKEN("BEGIN"), TK_BEGIN);
|
||||
break;
|
||||
|
||||
case ',':
|
||||
++m_pI;
|
||||
token = TK_COMMA;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case 'C':
|
||||
if (is_next_alpha('O'))
|
||||
{
|
||||
if (is_next_alpha('M', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("COMMIT"), TK_COMMIT);
|
||||
}
|
||||
else if (is_next_alpha('N', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("CONSISTENT"), TK_CONSISTENT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
++m_pI;
|
||||
token = TK_DOT;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
++m_pI;
|
||||
token = TK_EQ;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
token = expect_token(TBP_EXPECT_TOKEN("FALSE"), TK_FALSE);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'G':
|
||||
token = expect_token(TBP_EXPECT_TOKEN("GLOBAL"), TK_GLOBAL);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
{
|
||||
char c;
|
||||
if (!peek_next_char(&c) || !isdigit(c))
|
||||
{
|
||||
++m_pI;
|
||||
token = TK_ONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
case 'O':
|
||||
if (is_next_alpha('F'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("OFF"), TK_ZERO);
|
||||
}
|
||||
else if (is_next_alpha('N'))
|
||||
{
|
||||
if (is_next_char('L', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("ONLY"), TK_ONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("ON"), TK_ONE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 'R':
|
||||
if (is_next_alpha('E'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("READ"), TK_READ);
|
||||
}
|
||||
else if (is_next_alpha('O'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("ROLLBACK"), TK_ROLLBACK);
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
if (is_next_alpha('E'))
|
||||
{
|
||||
if (is_next_alpha('S', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("SESSION"), TK_SESSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("SET"), TK_SET);
|
||||
}
|
||||
}
|
||||
else if (is_next_alpha('N'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("SNAPSHOT"), TK_SNAPSHOT);
|
||||
}
|
||||
else if (is_next_char('T'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("START"), TK_START);
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'T':
|
||||
if (is_next_alpha('R'))
|
||||
{
|
||||
if (is_next_alpha('A', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("TRANSACTION"), TK_TRANSACTION);
|
||||
}
|
||||
else if (is_next_alpha('U', 2))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("TRUE"), TK_TRUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
case 'W':
|
||||
if (is_next_alpha('I'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("WITH"), TK_WITH);
|
||||
}
|
||||
else if (is_next_alpha('O'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("WORK"), TK_WORK);
|
||||
}
|
||||
else if (is_next_alpha('R'))
|
||||
{
|
||||
token = expect_token(TBP_EXPECT_TOKEN("WRITE"), TK_WRITE);
|
||||
}
|
||||
break;
|
||||
|
||||
case '0':
|
||||
{
|
||||
char c;
|
||||
if (!peek_next_char(&c) || !isdigit(c))
|
||||
{
|
||||
++m_pI;
|
||||
token = TK_ZERO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if ((token == PARSER_EXHAUSTED) && (required == TOKEN_REQUIRED))
|
||||
{
|
||||
log_exhausted();
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
TrxBoundaryParser(const TrxBoundaryParser&);
|
||||
TrxBoundaryParser& operator = (const TrxBoundaryParser&);
|
||||
|
||||
private:
|
||||
const char* m_pSql;
|
||||
int m_len;
|
||||
const char* m_pI;
|
||||
const char* m_pEnd;
|
||||
};
|
||||
|
||||
}
|
@ -59,7 +59,7 @@ mxs_pcre2_result_t mxs_pcre2_substitute(pcre2_code *re, const char *subject, con
|
||||
(PCRE2_SPTR) replace, PCRE2_ZERO_TERMINATED,
|
||||
(PCRE2_UCHAR*) *dest, size)) == PCRE2_ERROR_NOMEMORY)
|
||||
{
|
||||
char *tmp = MXS_REALLOC(*dest, *size * 2);
|
||||
char *tmp = (char*)MXS_REALLOC(*dest, *size * 2);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
break;
|
@ -53,7 +53,7 @@ static inline void prepare_error()
|
||||
{
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
errbuf = MXS_MALLOC(MODULECMD_ERRBUF_SIZE);
|
||||
errbuf = (char*)MXS_MALLOC(MODULECMD_ERRBUF_SIZE);
|
||||
MXS_ABORT_IF_NULL(errbuf);
|
||||
errbuf[0] = '\0';
|
||||
}
|
||||
@ -86,7 +86,7 @@ static void report_argc_mismatch(const MODULECMD *cmd, int argc)
|
||||
|
||||
static MODULECMD_DOMAIN* domain_create(const char *domain)
|
||||
{
|
||||
MODULECMD_DOMAIN *rval = MXS_MALLOC(sizeof(*rval));
|
||||
MODULECMD_DOMAIN *rval = (MODULECMD_DOMAIN*)MXS_MALLOC(sizeof(*rval));
|
||||
char *dm = MXS_STRDUP(domain);
|
||||
|
||||
if (rval && dm)
|
||||
@ -141,10 +141,10 @@ static MODULECMD* command_create(const char *identifier, const char *domain,
|
||||
modulecmd_arg_type_t* argv)
|
||||
{
|
||||
ss_dassert((argc && argv) || (argc == 0 && argv == NULL));
|
||||
MODULECMD *rval = MXS_MALLOC(sizeof(*rval));
|
||||
MODULECMD *rval = (MODULECMD*)MXS_MALLOC(sizeof(*rval));
|
||||
char *id = MXS_STRDUP(identifier);
|
||||
char *dm = MXS_STRDUP(domain);
|
||||
modulecmd_arg_type_t *types = MXS_MALLOC(sizeof(*types) * (argc ? argc : 1));
|
||||
modulecmd_arg_type_t *types = (modulecmd_arg_type_t*)MXS_MALLOC(sizeof(*types) * (argc ? argc : 1));
|
||||
|
||||
if (rval && id && dm && types)
|
||||
{
|
||||
@ -297,7 +297,7 @@ static bool process_argument(const MODULECMD *cmd, modulecmd_arg_type_t *type, c
|
||||
break;
|
||||
|
||||
case MODULECMD_ARG_SESSION:
|
||||
if ((arg->value.session = session_get_by_id(atoi(value))))
|
||||
if ((arg->value.session = session_get_by_id(atoi((const char*)value))))
|
||||
{
|
||||
arg->type.type = MODULECMD_ARG_SESSION;
|
||||
}
|
||||
@ -373,8 +373,8 @@ static bool process_argument(const MODULECMD *cmd, modulecmd_arg_type_t *type, c
|
||||
|
||||
static MODULECMD_ARG* modulecmd_arg_create(int argc)
|
||||
{
|
||||
MODULECMD_ARG* arg = MXS_MALLOC(sizeof(*arg));
|
||||
struct arg_node *argv = MXS_CALLOC(argc, sizeof(*argv));
|
||||
MODULECMD_ARG* arg = (MODULECMD_ARG*)MXS_MALLOC(sizeof(*arg));
|
||||
struct arg_node *argv = (struct arg_node*)MXS_CALLOC(argc, sizeof(*argv));
|
||||
|
||||
if (arg && argv)
|
||||
{
|
||||
@ -679,7 +679,7 @@ char* modulecmd_argtype_to_str(modulecmd_arg_type_t *type)
|
||||
|
||||
size_t slen = strlen(strtype);
|
||||
size_t extra = MODULECMD_ARG_IS_REQUIRED(type) ? 0 : 2;
|
||||
char *rval = MXS_MALLOC(slen + extra + 1);
|
||||
char *rval = (char*)MXS_MALLOC(slen + extra + 1);
|
||||
|
||||
if (rval)
|
||||
{
|
@ -46,11 +46,11 @@ static const char* sub_single = "$1.";
|
||||
static const char* sub_escape = "\\.";
|
||||
|
||||
static void modutil_reply_routing_error(
|
||||
DCB* backend_dcb,
|
||||
int error,
|
||||
char* state,
|
||||
char* errstr,
|
||||
uint32_t flags);
|
||||
DCB* backend_dcb,
|
||||
int error,
|
||||
const char* state,
|
||||
char* errstr,
|
||||
uint32_t flags);
|
||||
|
||||
|
||||
/**
|
||||
@ -329,7 +329,7 @@ modutil_get_query(GWBUF *buf)
|
||||
char* query_str = NULL;
|
||||
|
||||
packet = GWBUF_DATA(buf);
|
||||
packet_type = packet[4];
|
||||
packet_type = (mysql_server_cmd_t)packet[4];
|
||||
|
||||
switch (packet_type)
|
||||
{
|
||||
@ -758,11 +758,11 @@ void modutil_reply_auth_error(DCB* backend_dcb,
|
||||
* @param errstr Plain-text string error
|
||||
* @param flags GWBUF type flags
|
||||
*/
|
||||
static void modutil_reply_routing_error(DCB* backend_dcb,
|
||||
int error,
|
||||
char* state,
|
||||
char* errstr,
|
||||
uint32_t flags)
|
||||
static void modutil_reply_routing_error(DCB* backend_dcb,
|
||||
int error,
|
||||
const char* state,
|
||||
char* errstr,
|
||||
uint32_t flags)
|
||||
{
|
||||
GWBUF* buf;
|
||||
CHK_DCB(backend_dcb);
|
||||
@ -1213,3 +1213,134 @@ char* modutil_get_canonical(GWBUF* querybuf)
|
||||
|
||||
return querystr;
|
||||
}
|
||||
|
||||
|
||||
char* modutil_MySQL_bypass_whitespace(char* sql, size_t len)
|
||||
{
|
||||
char *i = sql;
|
||||
char *end = i + len;
|
||||
|
||||
while (i != end)
|
||||
{
|
||||
if (isspace(*i))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else if (*i == '/') // Might be a comment
|
||||
{
|
||||
if ((i + 1 != end) && (*(i + 1) == '*')) // Indeed it was
|
||||
{
|
||||
i += 2;
|
||||
|
||||
while (i != end)
|
||||
{
|
||||
if (*i == '*') // Might be the end of the comment
|
||||
{
|
||||
++i;
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
if (*i == '/') // Indeed it was
|
||||
{
|
||||
++i;
|
||||
break; // Out of this inner while.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// It was not the end of the comment.
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Was not a comment, so we'll bail out.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (*i == '-') // Might be the start of a comment to the end of line
|
||||
{
|
||||
bool is_comment = false;
|
||||
|
||||
if (i + 1 != end)
|
||||
{
|
||||
if (*(i + 1) == '-') // Might be, yes.
|
||||
{
|
||||
if (i + 2 != end)
|
||||
{
|
||||
if (isspace(*(i + 2))) // Yes, it is.
|
||||
{
|
||||
is_comment = true;
|
||||
|
||||
i += 3;
|
||||
|
||||
while ((i != end) && (*i != '\n'))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
ss_dassert(*i == '\n');
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_comment)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (*i == '#')
|
||||
{
|
||||
++i;
|
||||
|
||||
while ((i != end) && (*i != '\n'))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
ss_dassert(*i == '\n');
|
||||
++i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Neither whitespace not start of a comment, so we bail out.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
bool modutil_ignorable_ping(DCB *dcb)
|
||||
{
|
||||
static uint8_t com_ping_packet[] =
|
||||
{
|
||||
0x01, 0x00, 0x00, 0x00, 0x0e
|
||||
};
|
||||
|
||||
GWBUF *buf = gwbuf_alloc_and_load(sizeof(com_ping_packet), com_ping_packet);
|
||||
bool rval = false;
|
||||
|
||||
if (buf)
|
||||
{
|
||||
gwbuf_set_type(buf, GWBUF_TYPE_IGNORABLE);
|
||||
|
||||
if (dcb->func.write(dcb, buf))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
@ -86,7 +86,7 @@ monitor_alloc(char *name, char *module)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL)
|
||||
if ((mon->module = (MXS_MONITOR_OBJECT*)load_module(module, MODULE_MONITOR)) == NULL)
|
||||
{
|
||||
MXS_ERROR("Unable to load monitor module '%s'.", name);
|
||||
MXS_FREE(name);
|
||||
@ -161,7 +161,7 @@ monitor_free(MXS_MONITOR *mon)
|
||||
* @param monitor The Monitor that should be started
|
||||
*/
|
||||
void
|
||||
monitorStart(MXS_MONITOR *monitor, void* params)
|
||||
monitorStart(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
|
||||
{
|
||||
spinlock_acquire(&monitor->lock);
|
||||
|
||||
@ -957,7 +957,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)
|
||||
{
|
||||
char *separator = "";
|
||||
const char *separator = "";
|
||||
char arr[MAX_SERVER_NAME_LEN + 64]; // Some extra space for port and separator
|
||||
dest[0] = '\0';
|
||||
|
||||
@ -991,7 +991,7 @@ bool mon_status_changed(MXS_MONITOR_SERVERS* mon_srv)
|
||||
bool rval = false;
|
||||
|
||||
/* Previous status is -1 if not yet set */
|
||||
if (mon_srv->mon_prev_status != -1)
|
||||
if (mon_srv->mon_prev_status != (uint32_t)-1)
|
||||
{
|
||||
|
||||
unsigned int old_status = mon_srv->mon_prev_status & all_server_bits;
|
||||
@ -1107,7 +1107,7 @@ monitor_launch_script(MXS_MONITOR* mon, MXS_MONITOR_SERVERS* ptr, const char* sc
|
||||
totalStrLen += strlen(cmd->argv[i]) + 1; // +1 for space and one \0
|
||||
}
|
||||
int spaceRemaining = totalStrLen;
|
||||
if ((scriptStr = MXS_CALLOC(totalStrLen, sizeof(char))) != NULL)
|
||||
if ((scriptStr = (char*)MXS_CALLOC(totalStrLen, sizeof(char))) != NULL)
|
||||
{
|
||||
char *currentPos = scriptStr;
|
||||
// The script name should not begin with a space
|
||||
@ -1181,10 +1181,10 @@ mon_connect_to_db(MXS_MONITOR* mon, MXS_MONITOR_SERVERS *database)
|
||||
|
||||
char *dpwd = decrypt_password(passwd);
|
||||
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &mon->connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *) &mon->read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *) &mon->write_timeout);
|
||||
|
||||
mysql_optionsv(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *) &mon->connect_timeout);
|
||||
mysql_optionsv(database->con, MYSQL_OPT_READ_TIMEOUT, (void *) &mon->read_timeout);
|
||||
mysql_optionsv(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *) &mon->write_timeout);
|
||||
mysql_optionsv(database->con, MYSQL_PLUGIN_DIR, get_connector_plugindir());
|
||||
time_t start = time(NULL);
|
||||
bool result = (mxs_mysql_real_connect(database->con, database->server, uname, dpwd) != NULL);
|
||||
time_t end = time(NULL);
|
@ -117,7 +117,7 @@ uint64_t mxs_leint_consume(uint8_t ** c)
|
||||
char* mxs_lestr_consume_dup(uint8_t** c)
|
||||
{
|
||||
uint64_t slen = mxs_leint_consume(c);
|
||||
char *str = MXS_MALLOC((slen + 1) * sizeof(char));
|
||||
char *str = (char*)MXS_MALLOC((slen + 1) * sizeof(char));
|
||||
|
||||
if (str)
|
||||
{
|
@ -137,6 +137,17 @@ void set_execdir(char* param)
|
||||
execdir = param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connector plugin directory.
|
||||
* @param str Path to directory
|
||||
*/
|
||||
void set_connector_plugindir(char* param)
|
||||
{
|
||||
MXS_FREE(connector_plugindir);
|
||||
clean_up_pathname(param);
|
||||
connector_plugindir = param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory with all the modules.
|
||||
* @return The module directory
|
||||
@ -235,3 +246,12 @@ char* get_execdir()
|
||||
{
|
||||
return execdir ? execdir : (char*) default_execdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connector plugin directory
|
||||
* @return The connector plugin directory
|
||||
*/
|
||||
char* get_connector_plugindir()
|
||||
{
|
||||
return connector_plugindir ? connector_plugindir : (char*) default_connector_plugindir;
|
||||
}
|
@ -178,19 +178,19 @@ static THREAD_DATA *thread_data = NULL; /*< Status of each thread */
|
||||
*/
|
||||
static struct
|
||||
{
|
||||
ts_stats_t *n_read; /*< Number of read events */
|
||||
ts_stats_t *n_write; /*< Number of write events */
|
||||
ts_stats_t *n_error; /*< Number of error events */
|
||||
ts_stats_t *n_hup; /*< Number of hangup events */
|
||||
ts_stats_t *n_accept; /*< Number of accept events */
|
||||
ts_stats_t *n_polls; /*< Number of poll cycles */
|
||||
ts_stats_t *n_pollev; /*< Number of polls returning events */
|
||||
ts_stats_t *n_nbpollev; /*< Number of polls returning events */
|
||||
ts_stats_t *n_nothreads; /*< Number of times no threads are polling */
|
||||
int32_t n_fds[MAXNFDS]; /*< Number of wakeups with particular n_fds value */
|
||||
ts_stats_t *evq_length; /*< Event queue length */
|
||||
ts_stats_t *evq_max; /*< Maximum event queue length */
|
||||
ts_stats_t *blockingpolls; /*< Number of epoll_waits with a timeout specified */
|
||||
ts_stats_t n_read; /*< Number of read events */
|
||||
ts_stats_t n_write; /*< Number of write events */
|
||||
ts_stats_t n_error; /*< Number of error events */
|
||||
ts_stats_t n_hup; /*< Number of hangup events */
|
||||
ts_stats_t n_accept; /*< Number of accept events */
|
||||
ts_stats_t n_polls; /*< Number of poll cycles */
|
||||
ts_stats_t n_pollev; /*< Number of polls returning events */
|
||||
ts_stats_t n_nbpollev; /*< Number of polls returning events */
|
||||
ts_stats_t n_nothreads; /*< Number of times no threads are polling */
|
||||
int32_t n_fds[MAXNFDS]; /*< Number of wakeups with particular n_fds value */
|
||||
ts_stats_t evq_length; /*< Event queue length */
|
||||
ts_stats_t evq_max; /*< Maximum event queue length */
|
||||
ts_stats_t blockingpolls; /*< Number of epoll_waits with a timeout specified */
|
||||
} pollStats;
|
||||
|
||||
#define N_QUEUE_TIMES 30
|
||||
@ -201,8 +201,8 @@ static struct
|
||||
{
|
||||
uint32_t qtimes[N_QUEUE_TIMES + 1];
|
||||
uint32_t exectimes[N_QUEUE_TIMES + 1];
|
||||
ts_stats_t *maxqtime;
|
||||
ts_stats_t *maxexectime;
|
||||
ts_stats_t maxqtime;
|
||||
ts_stats_t maxexectime;
|
||||
} queueStats;
|
||||
|
||||
/**
|
||||
@ -230,7 +230,7 @@ poll_init()
|
||||
{
|
||||
n_threads = config_threadcount();
|
||||
|
||||
if (!(epoll_fd = MXS_MALLOC(sizeof(int) * n_threads)))
|
||||
if (!(epoll_fd = (int*)MXS_MALLOC(sizeof(int) * n_threads)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -245,17 +245,17 @@ poll_init()
|
||||
}
|
||||
}
|
||||
|
||||
if ((fake_events = MXS_CALLOC(n_threads, sizeof(fake_event_t*))) == NULL)
|
||||
if ((fake_events = (fake_event_t**)MXS_CALLOC(n_threads, sizeof(fake_event_t*))) == NULL)
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if ((fake_event_lock = MXS_CALLOC(n_threads, sizeof(SPINLOCK))) == NULL)
|
||||
if ((fake_event_lock = (SPINLOCK*)MXS_CALLOC(n_threads, sizeof(SPINLOCK))) == NULL)
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if ((poll_msg = MXS_CALLOC(n_threads, sizeof(int))) == NULL)
|
||||
if ((poll_msg = (int*)MXS_CALLOC(n_threads, sizeof(int))) == NULL)
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
@ -875,7 +875,7 @@ static int
|
||||
process_pollq(int thread_id, struct epoll_event *event)
|
||||
{
|
||||
uint32_t ev = event->events;
|
||||
DCB *dcb = event->data.ptr;
|
||||
DCB *dcb = (DCB*)event->data.ptr;
|
||||
ss_dassert(dcb->thread.id == thread_id || dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER);
|
||||
|
||||
/** Calculate event queue statistics */
|
||||
@ -1176,7 +1176,7 @@ event_to_string(uint32_t event)
|
||||
{
|
||||
char *str;
|
||||
|
||||
str = MXS_MALLOC(22); // 22 is max returned string length
|
||||
str = (char*)MXS_MALLOC(22); // 22 is max returned string length
|
||||
if (str == NULL)
|
||||
{
|
||||
return NULL;
|
||||
@ -1233,7 +1233,7 @@ void
|
||||
dShowThreads(DCB *dcb)
|
||||
{
|
||||
int i, j, n;
|
||||
char *state;
|
||||
const char *state;
|
||||
double avg1 = 0.0, avg5 = 0.0, avg15 = 0.0;
|
||||
double qavg1 = 0.0, qavg5 = 0.0, qavg15 = 0.0;
|
||||
|
||||
@ -1328,7 +1328,7 @@ dShowThreads(DCB *dcb)
|
||||
if (event_string == NULL)
|
||||
{
|
||||
from_heap = false;
|
||||
event_string = "??";
|
||||
event_string = (char*)"??";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1397,7 +1397,7 @@ static void poll_add_event_to_dcb(DCB* dcb,
|
||||
GWBUF* buf,
|
||||
uint32_t ev)
|
||||
{
|
||||
fake_event_t *event = MXS_MALLOC(sizeof(*event));
|
||||
fake_event_t *event = (fake_event_t*)MXS_MALLOC(sizeof(*event));
|
||||
|
||||
if (event)
|
||||
{
|
@ -11,11 +11,14 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include "maxscale/query_classifier.h"
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/platform.h>
|
||||
#include <maxscale/pcre2.h>
|
||||
#include <maxscale/utils.h>
|
||||
#include "maxscale/trxboundaryparser.hh"
|
||||
|
||||
#include "../core/maxscale/modules.h"
|
||||
|
||||
@ -34,10 +37,13 @@ struct type_name_info
|
||||
size_t name_len;
|
||||
};
|
||||
|
||||
static const char default_qc_name[] = "qc_sqlite";
|
||||
static const char DEFAULT_QC_NAME[] = "qc_sqlite";
|
||||
static const char QC_TRX_PARSE_USING[] = "QC_TRX_PARSE_USING";
|
||||
|
||||
static QUERY_CLASSIFIER* classifier;
|
||||
|
||||
static qc_trx_parse_using_t qc_trx_parse_using = QC_TRX_PARSE_USING_PARSER;
|
||||
|
||||
|
||||
bool qc_setup(const char* plugin_name, const char* plugin_args)
|
||||
{
|
||||
@ -46,8 +52,8 @@ bool qc_setup(const char* plugin_name, const char* plugin_args)
|
||||
|
||||
if (!plugin_name || (*plugin_name == 0))
|
||||
{
|
||||
MXS_NOTICE("No query classifier specified, using default '%s'.", default_qc_name);
|
||||
plugin_name = default_qc_name;
|
||||
MXS_NOTICE("No query classifier specified, using default '%s'.", DEFAULT_QC_NAME);
|
||||
plugin_name = DEFAULT_QC_NAME;
|
||||
}
|
||||
|
||||
int32_t rv = QC_RESULT_ERROR;
|
||||
@ -60,28 +66,67 @@ bool qc_setup(const char* plugin_name, const char* plugin_args)
|
||||
if (rv != QC_RESULT_OK)
|
||||
{
|
||||
qc_unload(classifier);
|
||||
classifier = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return (rv == QC_RESULT_OK) ? true : false;
|
||||
}
|
||||
|
||||
bool qc_process_init(void)
|
||||
bool qc_process_init(uint32_t kind)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
return classifier->qc_process_init() == 0;
|
||||
const char* parse_using = getenv(QC_TRX_PARSE_USING);
|
||||
|
||||
if (parse_using)
|
||||
{
|
||||
if (strcmp(parse_using, "QC_TRX_PARSE_USING_QC") == 0)
|
||||
{
|
||||
qc_trx_parse_using = QC_TRX_PARSE_USING_QC;
|
||||
MXS_NOTICE("Transaction detection using QC.");
|
||||
}
|
||||
else if (strcmp(parse_using, "QC_TRX_PARSE_USING_PARSER") == 0)
|
||||
{
|
||||
qc_trx_parse_using = QC_TRX_PARSE_USING_PARSER;
|
||||
MXS_NOTICE("Transaction detection using custom PARSER.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_NOTICE("QC_TRX_PARSE_USING set, but the value %s is not known. "
|
||||
"Parsing using QC.", parse_using);
|
||||
}
|
||||
}
|
||||
|
||||
bool rc = qc_thread_init(QC_INIT_SELF);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
if (kind & QC_INIT_PLUGIN)
|
||||
{
|
||||
rc = classifier->qc_process_init() == 0;
|
||||
|
||||
if (!rc)
|
||||
{
|
||||
qc_thread_end(QC_INIT_SELF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void qc_process_end(void)
|
||||
void qc_process_end(uint32_t kind)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
classifier->qc_process_end();
|
||||
classifier = NULL;
|
||||
if (kind & QC_INIT_PLUGIN)
|
||||
{
|
||||
classifier->qc_process_end();
|
||||
}
|
||||
|
||||
qc_thread_end(QC_INIT_SELF);
|
||||
}
|
||||
|
||||
QUERY_CLASSIFIER* qc_load(const char* plugin_name)
|
||||
@ -104,32 +149,43 @@ void qc_unload(QUERY_CLASSIFIER* classifier)
|
||||
{
|
||||
// TODO: The module loading/unloading needs an overhaul before we
|
||||
// TODO: actually can unload something.
|
||||
classifier = NULL;
|
||||
}
|
||||
|
||||
bool qc_thread_init(void)
|
||||
bool qc_thread_init(uint32_t kind)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
return classifier->qc_thread_init() == 0;
|
||||
bool rc = true;
|
||||
|
||||
if (kind & QC_INIT_PLUGIN)
|
||||
{
|
||||
rc = classifier->qc_thread_init() == 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void qc_thread_end(void)
|
||||
void qc_thread_end(uint32_t kind)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
return classifier->qc_thread_end();
|
||||
if (kind & QC_INIT_PLUGIN)
|
||||
{
|
||||
classifier->qc_thread_end();
|
||||
}
|
||||
}
|
||||
|
||||
qc_parse_result_t qc_parse(GWBUF* query)
|
||||
qc_parse_result_t qc_parse(GWBUF* query, uint32_t collect)
|
||||
{
|
||||
QC_TRACE();
|
||||
ss_dassert(classifier);
|
||||
|
||||
int32_t result = QC_QUERY_INVALID;
|
||||
|
||||
classifier->qc_parse(query, &result);
|
||||
classifier->qc_parse(query, collect, &result);
|
||||
|
||||
return (qc_parse_result_t)result;
|
||||
}
|
||||
@ -778,3 +834,70 @@ char* qc_typemask_to_string(uint32_t types)
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static uint32_t qc_get_trx_type_mask_using_qc(GWBUF* stmt)
|
||||
{
|
||||
uint32_t type_mask = qc_get_type_mask(stmt);
|
||||
|
||||
if (qc_query_is_type(type_mask, QUERY_TYPE_WRITE) &&
|
||||
qc_query_is_type(type_mask, QUERY_TYPE_COMMIT))
|
||||
{
|
||||
// This is a commit reported for "CREATE TABLE...",
|
||||
// "DROP TABLE...", etc. that cause an implicit commit.
|
||||
type_mask = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only START TRANSACTION can be explicitly READ or WRITE.
|
||||
if (!(type_mask & QUERY_TYPE_BEGIN_TRX))
|
||||
{
|
||||
// So, strip them away for everything else.
|
||||
type_mask &= ~(QUERY_TYPE_WRITE | QUERY_TYPE_READ);
|
||||
}
|
||||
|
||||
// Then leave only the bits related to transaction and
|
||||
// autocommit state.
|
||||
type_mask &= (QUERY_TYPE_BEGIN_TRX |
|
||||
QUERY_TYPE_WRITE |
|
||||
QUERY_TYPE_READ |
|
||||
QUERY_TYPE_COMMIT |
|
||||
QUERY_TYPE_ROLLBACK |
|
||||
QUERY_TYPE_ENABLE_AUTOCOMMIT |
|
||||
QUERY_TYPE_DISABLE_AUTOCOMMIT);
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
static uint32_t qc_get_trx_type_mask_using_parser(GWBUF* stmt)
|
||||
{
|
||||
maxscale::TrxBoundaryParser parser;
|
||||
|
||||
return parser.type_mask_of(stmt);
|
||||
}
|
||||
|
||||
uint32_t qc_get_trx_type_mask_using(GWBUF* stmt, qc_trx_parse_using_t use)
|
||||
{
|
||||
uint32_t type_mask = 0;
|
||||
|
||||
switch (use)
|
||||
{
|
||||
case QC_TRX_PARSE_USING_QC:
|
||||
type_mask = qc_get_trx_type_mask_using_qc(stmt);
|
||||
break;
|
||||
|
||||
case QC_TRX_PARSE_USING_PARSER:
|
||||
type_mask = qc_get_trx_type_mask_using_parser(stmt);
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
|
||||
return type_mask;
|
||||
}
|
||||
|
||||
uint32_t qc_get_trx_type_mask(GWBUF* stmt)
|
||||
{
|
||||
return qc_get_trx_type_mask_using(stmt, qc_trx_parse_using);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user