Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä
2018-09-27 11:40:14 +03:00
15 changed files with 281 additions and 28 deletions

View File

@ -39,7 +39,7 @@ For more details, please refer to:
the master. There is also limited capability for rejoining nodes. the master. There is also limited capability for rejoining nodes.
For more details, please refer to: For more details, please refer to:
* [MariaDB MaxScale 2.2.14 Release Notes](Release-Notes/MaxScale-2.2.14-Release-Notes.md) * [MariaDB MaxScale 2.2.15 Release Notes](Release-Notes/MaxScale-2.2.15-Release-Notes.md)
* [MariaDB MaxScale 2.2.13 Release Notes](Release-Notes/MaxScale-2.2.13-Release-Notes.md) * [MariaDB MaxScale 2.2.13 Release Notes](Release-Notes/MaxScale-2.2.13-Release-Notes.md)
* [MariaDB MaxScale 2.2.12 Release Notes](Release-Notes/MaxScale-2.2.12-Release-Notes.md) * [MariaDB MaxScale 2.2.12 Release Notes](Release-Notes/MaxScale-2.2.12-Release-Notes.md)
* [MariaDB MaxScale 2.2.11 Release Notes](Release-Notes/MaxScale-2.2.11-Release-Notes.md) * [MariaDB MaxScale 2.2.11 Release Notes](Release-Notes/MaxScale-2.2.11-Release-Notes.md)

View File

@ -989,15 +989,18 @@ GRANT SELECT ON mysql.db TO 'maxscale'@'maxscalehost';
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'maxscalehost'; GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'maxscalehost';
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'maxscalehost'; GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'maxscalehost';
GRANT SHOW DATABASES ON *.* TO 'maxscale'@'maxscalehost'; GRANT SHOW DATABASES ON *.* TO 'maxscale'@'maxscalehost';
-- MariaDB from 10.2.2 to 10.2.10 requires extra grants
GRANT SELECT ON mysql.* TO 'maxscale'@'maxscalehost';
``` ```
**Note:** MariaDB versions 10.2.10 and older require a `SELECT` grant on
`mysql.*` in addition to the normal grants. This is to work around MDEV-13453
which was fixed in MariaDB 10.2.11.
See [MaxScale Troubleshooting](https://mariadb.com/kb/en/mariadb-enterprise/maxscale-troubleshooting/) See [MaxScale Troubleshooting](https://mariadb.com/kb/en/mariadb-enterprise/maxscale-troubleshooting/)
for more information on how to troubleshoot authentication related problems. for more information on how to troubleshoot authentication related problems.
**Note:** Due to a bug in MariaDB 10.2.9, if you see a
`SELECT command denied to user ... for table 'users'`
error, grant `SELECT ON mysql.*` to this user.
<a id="passwd"></a> <a id="passwd"></a>
#### `password` #### `password`

View File

@ -0,0 +1,38 @@
# MariaDB MaxScale 2.2.15 Release Notes
Release 2.2.15 is a GA release.
This document describes the changes in release 2.2.15, when compared to the
previous release in the same series.
For any problems you encounter, please consider submitting a bug
report on [our Jira](https://jira.mariadb.org/projects/MXS).
## Bug fixes
* [MXS-2066](https://jira.mariadb.org/browse/MXS-2066) Result buffering is not always disabled
* [MXS-2064](https://jira.mariadb.org/browse/MXS-2064) Loading of users with MariaDB 10.2.10 fails
* [MXS-2060](https://jira.mariadb.org/browse/MXS-2060) Users are loaded from servers in maintenance
* [MXS-2052](https://jira.mariadb.org/browse/MXS-2052) readwritesplit doesn't explain why session closes
* [MXS-2045](https://jira.mariadb.org/browse/MXS-2045) Avrorouter tutorial is not good enough
* [MXS-2043](https://jira.mariadb.org/browse/MXS-2043) Error "The MariaDB server is running with the --read-only option" for "select for update"
## 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 supported the Linux distributions.
Packages can be downloaded [here](https://mariadb.com/downloads/mariadb-tx/maxscale).
## 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 `maxscale-X.Y.Z`. Further, the default branch is always the latest GA version
of MaxScale.
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).

View File

@ -90,16 +90,8 @@ for the read service we use the _slave_ type.
The final part of the service configuration is the `user` and `password` The final part of the service configuration is the `user` and `password`
parameters that define the credentials that the service will use to populate the parameters that define the credentials that the service will use to populate the
user authentication data. To create this user, execute the following SQL commands. user authentication data. These users were created at the start of the
[MaxScale Tutorial](MaxScale-Tutorial.md).
```
CREATE USER 'maxscale'@'%' IDENTIFIED BY 'maxscale_pw';
GRANT SELECT ON mysql.user TO 'maxscale'@'%';
GRANT SELECT ON mysql.db TO 'maxscale'@'%';
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%';
GRANT SHOW DATABASES ON *.* TO 'maxscale'@'%';
```
**Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md). **Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md).

View File

@ -35,6 +35,9 @@ GRANT SELECT ON mysql.db TO 'maxscale'@'%';
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%'; GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%'; GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%';
GRANT SHOW DATABASES ON *.* TO 'maxscale'@'%'; GRANT SHOW DATABASES ON *.* TO 'maxscale'@'%';
-- MariaDB from 10.2.2 to 10.2.10 requires extra grants
GRANT SELECT ON mysql.* TO 'maxscale'@'%';
``` ```
These credentials will be used by the services in MaxScale to populate the user These credentials will be used by the services in MaxScale to populate the user

View File

@ -72,16 +72,8 @@ or addresses of the servers.
The final part of the service configuration is the `user` and `password` The final part of the service configuration is the `user` and `password`
parameters that define the credentials that the service will use to populate the parameters that define the credentials that the service will use to populate the
user authentication data. To create this user, execute the following SQL commands. user authentication data. These users were created at the start of the
[MaxScale Tutorial](MaxScale-Tutorial.md).
```
CREATE USER 'maxscale'@'%' IDENTIFIED BY 'maxscale_pw';
GRANT SELECT ON mysql.user TO 'maxscale'@'%';
GRANT SELECT ON mysql.db TO 'maxscale'@'%';
GRANT SELECT ON mysql.tables_priv TO 'maxscale'@'%';
GRANT SELECT ON mysql.roles_mapping TO 'maxscale'@'%';
GRANT SHOW DATABASES ON *.* TO 'maxscale'@'%';
```
**Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md). **Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md).

View File

@ -21,5 +21,8 @@ TimeoutStartSec=120
LimitNOFILE=65535 LimitNOFILE=65535
StartLimitBurst=0 StartLimitBurst=0
# Only relevant when MaxScale is linked with -fsanitize=address
Environment=ASAN_OPTIONS=abort_on_error=1
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1076,4 +1076,9 @@ add_test_executable(mxs2047_blr_gtid_path.cpp mxs2047_blr_gtid_path mxs2047_blr_
# https://jira.mariadb.org/browse/MXS-701 # https://jira.mariadb.org/browse/MXS-701
add_test_executable(mxs701_binlog_filter.cpp mxs701_binlog_filter mxs701_binlog_filter LABELS bilogrouter REPL_BACKEND) add_test_executable(mxs701_binlog_filter.cpp mxs701_binlog_filter mxs701_binlog_filter LABELS bilogrouter REPL_BACKEND)
# MXS-2043: Error "The MariaDB server is running with the --read-only option"
# for "select for update"
# https://jira.mariadb.org/browse/MXS-2043
add_test_executable(mxs2043_select_for_update.cpp mxs2043_select_for_update replication LABELS REPL_BACKEND)
configure_file(templates.h.in ${CMAKE_CURRENT_BINARY_DIR}/templates.h @ONLY) configure_file(templates.h.in ${CMAKE_CURRENT_BINARY_DIR}/templates.h @ONLY)

View File

@ -38,6 +38,7 @@ int main(int argc, char** argv)
test.tprintf("Checking that both the master and slave are used"); test.tprintf("Checking that both the master and slave are used");
std::vector<MYSQL*> connections; std::vector<MYSQL*> connections;
test.set_timeout(60);
test.repl->connect(); test.repl->connect();
execute_query_silent(test.repl->nodes[0], "DROP USER IF EXISTS 'mxs1743'@'%'"); execute_query_silent(test.repl->nodes[0], "DROP USER IF EXISTS 'mxs1743'@'%'");
test.try_query(test.repl->nodes[0], "%s", "CREATE USER 'mxs1743'@'%' IDENTIFIED BY 'mxs1743'"); test.try_query(test.repl->nodes[0], "%s", "CREATE USER 'mxs1743'@'%' IDENTIFIED BY 'mxs1743'");
@ -65,6 +66,7 @@ int main(int argc, char** argv)
"SELECT COUNT(*) AS connections FROM information_schema.processlist WHERE user = 'mxs1743'"; "SELECT COUNT(*) AS connections FROM information_schema.processlist WHERE user = 'mxs1743'";
char master_connections[1024]; char master_connections[1024];
char slave_connections[1024]; char slave_connections[1024];
test.set_timeout(60);
find_field(test.repl->nodes[0], query.c_str(), "connections", master_connections); find_field(test.repl->nodes[0], query.c_str(), "connections", master_connections);
find_field(test.repl->nodes[1], query.c_str(), "connections", slave_connections); find_field(test.repl->nodes[1], query.c_str(), "connections", slave_connections);

View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2018 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: 2022-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include "testconnections.h"
//
// This test makes all slaves read_only and then executes
//
// SELECT ... FOR UPDATE
//
// first using the default system test user (that has super privileges)
// and then using a custom user that only has SELECT and UPDATE grants.
//
// Before MXS-2043, a "SELECT ... FOR UPDATE" was classified as
// QUERY_TYPE_READ, which caused the statement to be sent to a slave.
//
// With autocommit==1 and no transaction active there should be no problem
// as FOR UPDATE should have no effect unless autocommit==0 or a transaction
// is active (https://mariadb.com/kb/en/library/for-update/), but apparently
// the server checks the read_only state first and rejects the query.
//
// After MXS-2043, a "SELECT ... FOR UPDATE" statement is classified as
// QUERY_TYPE_WRITE, which unconditionally causes it to be sent to the master.
//
namespace
{
const char* ZUSER = "mxs2043_user";
const char* ZPASSWORD = "mxs2043_user";
const char* ZTABLE = "test.mxs2043";
const char* ZCOLUMN = "col";
void drop_table(TestConnections& test, MYSQL* pMysql, bool silent = false)
{
if (!silent)
{
test.tprintf("Dropping table.");
}
test.try_query(pMysql, "DROP TABLE IF EXISTS %s", ZTABLE);
}
bool create_table(TestConnections& test, MYSQL* pMysql)
{
test.tprintf("Creating table.");
drop_table(test, pMysql, true);
test.try_query(pMysql, "CREATE TABLE %s (%s INT)", ZTABLE, ZCOLUMN);
return test.global_result == 0;
}
void drop_user(TestConnections& test, MYSQL* pMysql, bool silent = false)
{
if (!silent)
{
test.tprintf("Dropping user.");
}
test.try_query(pMysql, "DROP USER IF EXISTS '%s'@'%%'", ZUSER);
}
bool create_user(TestConnections& test, MYSQL* pMysql)
{
test.tprintf("Creating user.");
drop_user(test, pMysql, true);
test.try_query(pMysql, "CREATE USER '%s' IDENTIFIED by '%s'", ZUSER, ZPASSWORD);
test.try_query(pMysql, "GRANT SELECT, UPDATE ON %s TO '%s'@'%%'", ZTABLE, ZUSER);
return test.global_result == 0;
}
bool set_read_only_on_slaves(TestConnections& test, bool set)
{
test.tprintf("%s read only on slaves.", set ? "Setting" : "Removing");
Mariadb_nodes& ms = *test.repl;
for (int i = 0; i < ms.N; ++i)
{
if (i != ms.master)
{
test.try_query(ms.nodes[i], "set global read_only=%d", set ? 1 : 0);
}
}
return test.global_result == 0;
}
void select_for_update(TestConnections& test, MYSQL* pMysql)
{
test.try_query(pMysql, "SELECT %s FROM %s FOR UPDATE", ZCOLUMN, ZTABLE);
}
void run_test(TestConnections& test)
{
// The default user has super privileges, so this should succeed
// whether or not MaxScale sends the query to the master or to
// some slave.
test.tprintf("Running test with default user.");
select_for_update(test, test.maxscales->conn_rwsplit[0]);
Maxscales& maxscales = *test.maxscales;
MYSQL* pMysql = open_conn(maxscales.rwsplit_port[0], maxscales.IP[0],
ZUSER, ZPASSWORD);
test.expect(pMysql, "Could not open connections for %s.", ZUSER);
if (pMysql)
{
test.tprintf("Running test with created user.");
// The created user does not have super privileges, so this should
// fail unless MaxScale routes the query to the master.
select_for_update(test, pMysql);
mysql_close(pMysql);
}
}
}
int main(int argc, char* argv[])
{
TestConnections test(argc, argv);
Maxscales& maxscales = *test.maxscales;
maxscales.connect();
MYSQL* pMysql = maxscales.conn_rwsplit[0];
if (create_table(test, pMysql))
{
if (create_user(test, pMysql))
{
int rv = test.repl->connect();
test.expect(rv == 0, "Could not connect to MS.");
if (rv == 0)
{
if (set_read_only_on_slaves(test, true))
{
run_test(test);
}
set_read_only_on_slaves(test, false);
}
drop_user(test, pMysql);
}
drop_table(test, pMysql);
}
return test.global_result;
}

View File

@ -2012,7 +2012,14 @@ public:
} }
else else
{ {
m_type_mask = QUERY_TYPE_READ; // Only if the type has explicitly been set to QUERY_TYPE_WRITE
// we don't force it to QUERY_TYPE_READ but with other bits we do.
// This is something of kludge to ensure continued compatibility
// with qc_mysqlembedded.
if (m_type_mask != QUERY_TYPE_WRITE)
{
m_type_mask = QUERY_TYPE_READ;
}
} }
QcAliases aliases; QcAliases aliases;
@ -3025,6 +3032,12 @@ public:
m_operation = QUERY_OP_CHANGE_DB; m_operation = QUERY_OP_CHANGE_DB;
} }
void set_type_mask(uint32_t type_mask)
{
ss_dassert(this_thread.initialized);
m_type_mask = type_mask;
}
private: private:
QcSqliteInfo(uint32_t cllct) QcSqliteInfo(uint32_t cllct)
: m_refs(1) : m_refs(1)
@ -3330,6 +3343,8 @@ extern "C"
extern void maxscaleUse(Parse*, Token*); extern void maxscaleUse(Parse*, Token*);
extern void maxscale_update_function_info(const char* name, const Expr* pExpr); extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
// 'unsigned int' and not 'uint32_t' because 'uint32_t' is unknown in sqlite3 context.
extern void maxscale_set_type_mask(unsigned int type_mask);
extern void maxscaleComment(); extern void maxscaleComment();
extern int maxscaleKeyword(int token); extern int maxscaleKeyword(int token);
@ -3716,6 +3731,14 @@ extern void maxscale_update_function_info(const char* name, const Expr* pExpr)
pInfo->update_function_info(NULL, name, pExpr, NULL); pInfo->update_function_info(NULL, name, pExpr, NULL);
} }
extern void maxscale_set_type_mask(unsigned int type_mask)
{
QcSqliteInfo* pInfo = this_thread.pInfo;
ss_dassert(pInfo);
pInfo->set_type_mask(type_mask);
}
static const char* get_token_symbol(int token) static const char* get_token_symbol(int token)
{ {
switch (token) switch (token)

View File

@ -131,6 +131,7 @@ extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
extern void maxscaleUse(Parse*, Token*); extern void maxscaleUse(Parse*, Token*);
extern void maxscale_update_function_info(const char* name, const Expr* pExpr); extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
extern void maxscale_set_type_mask(unsigned int type_mask);
// Exposed utility functions // Exposed utility functions
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr) void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
@ -966,6 +967,10 @@ cmd ::= select(X). {
%destructor select {sqlite3SelectDelete(pParse->db, $$);} %destructor select {sqlite3SelectDelete(pParse->db, $$);}
%type selectnowith {Select*} %type selectnowith {Select*}
%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);} %destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
%ifdef MAXSCALE
%type selectnowithsuffix {Select*}
%destructor selectnowithsuffix {sqlite3SelectDelete(pParse->db, $$);}
%endif
%type oneselect {Select*} %type oneselect {Select*}
%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);} %destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
@ -993,7 +998,12 @@ cmd ::= select(X). {
} }
} }
%ifdef MAXSCALE
select(A) ::= with(W) selectnowithsuffix(X). {
%endif
%ifndef MAXSCALE
select(A) ::= with(W) selectnowith(X). { select(A) ::= with(W) selectnowith(X). {
%endif
Select *p = X; Select *p = X;
if( p ){ if( p ){
p->pWith = W; p->pWith = W;
@ -1004,6 +1014,15 @@ select(A) ::= with(W) selectnowith(X). {
A = p; A = p;
} }
%ifdef MAXSCALE
selectnowithsuffix(A) ::= selectnowith(X). {A = X;}
selectnowithsuffix(A) ::= selectnowith(X) FOR UPDATE. {
A = X;
maxscale_set_type_mask(QUERY_TYPE_WRITE);
}
%endif
selectnowith(A) ::= oneselect(X). {A = X;} selectnowith(A) ::= oneselect(X). {A = X;}
%ifndef SQLITE_OMIT_COMPOUND_SELECT %ifndef SQLITE_OMIT_COMPOUND_SELECT
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). { selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {

View File

@ -24,3 +24,4 @@ QUERY_TYPE_READ|QUERY_TYPE_WRITE
QUERY_TYPE_READ|QUERY_TYPE_WRITE QUERY_TYPE_READ|QUERY_TYPE_WRITE
QUERY_TYPE_READ|QUERY_TYPE_WRITE QUERY_TYPE_READ|QUERY_TYPE_WRITE
QUERY_TYPE_DEALLOC_PREPARE QUERY_TYPE_DEALLOC_PREPARE
QUERY_TYPE_WRITE

View File

@ -24,3 +24,4 @@ SELECT IS_FREE_LOCK('lock1');
SELECT IS_USED_LOCK('lock1'); SELECT IS_USED_LOCK('lock1');
SELECT RELEASE_LOCK('lock1'); SELECT RELEASE_LOCK('lock1');
deallocate prepare select_stmt; deallocate prepare select_stmt;
SELECT a FROM tbl FOR UPDATE;

View File

@ -543,8 +543,7 @@ SRWBackend RWSplitSession::get_hinted_backend(char* name)
/** The server must be a valid slave, relay server, or master */ /** The server must be a valid slave, relay server, or master */
if ((backend->in_use() || (can_recover_servers() && backend->can_connect())) if ((backend->in_use() || (can_recover_servers() && backend->can_connect()))
&& strcasecmp(name, backend->name()) == 0 && strcasecmp(name, backend->name()) == 0)
&& (backend->is_slave() || backend->is_relay() || backend->is_master()))
{ {
rval = backend; rval = backend;
break; break;