Merge branch '2.2' into develop
This commit is contained in:
@ -39,7 +39,7 @@ For more details, please refer to:
|
||||
the master. There is also limited capability for rejoining nodes.
|
||||
|
||||
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.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)
|
||||
|
@ -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.roles_mapping 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/)
|
||||
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>
|
||||
#### `password`
|
||||
|
||||
|
38
Documentation/Release-Notes/MaxScale-2.2.15-Release-Notes.md
Normal file
38
Documentation/Release-Notes/MaxScale-2.2.15-Release-Notes.md
Normal 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).
|
@ -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`
|
||||
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.
|
||||
|
||||
```
|
||||
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'@'%';
|
||||
```
|
||||
user authentication data. These users were created at the start of the
|
||||
[MaxScale Tutorial](MaxScale-Tutorial.md).
|
||||
|
||||
**Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md).
|
||||
|
||||
|
@ -35,6 +35,9 @@ 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'@'%';
|
||||
|
||||
-- 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
|
||||
|
@ -72,16 +72,8 @@ or addresses of the servers.
|
||||
|
||||
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
|
||||
user authentication data. To create this user, execute the following SQL commands.
|
||||
|
||||
```
|
||||
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'@'%';
|
||||
```
|
||||
user authentication data. These users were created at the start of the
|
||||
[MaxScale Tutorial](MaxScale-Tutorial.md).
|
||||
|
||||
**Note:** For increased security [encrypt your passwords in the configuration file](Encrypting-Passwords.md).
|
||||
|
||||
|
@ -21,5 +21,8 @@ TimeoutStartSec=120
|
||||
LimitNOFILE=65535
|
||||
StartLimitBurst=0
|
||||
|
||||
# Only relevant when MaxScale is linked with -fsanitize=address
|
||||
Environment=ASAN_OPTIONS=abort_on_error=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -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
|
||||
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)
|
||||
|
@ -38,6 +38,7 @@ int main(int argc, char** argv)
|
||||
test.tprintf("Checking that both the master and slave are used");
|
||||
std::vector<MYSQL*> connections;
|
||||
|
||||
test.set_timeout(60);
|
||||
test.repl->connect();
|
||||
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'");
|
||||
@ -65,6 +66,7 @@ int main(int argc, char** argv)
|
||||
"SELECT COUNT(*) AS connections FROM information_schema.processlist WHERE user = 'mxs1743'";
|
||||
char master_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[1], query.c_str(), "connections", slave_connections);
|
||||
|
||||
|
172
maxscale-system-test/mxs2043_select_for_update.cpp
Normal file
172
maxscale-system-test/mxs2043_select_for_update.cpp
Normal 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;
|
||||
}
|
@ -2012,7 +2012,14 @@ public:
|
||||
}
|
||||
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;
|
||||
@ -3025,6 +3032,12 @@ public:
|
||||
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:
|
||||
QcSqliteInfo(uint32_t cllct)
|
||||
: m_refs(1)
|
||||
@ -3330,6 +3343,8 @@ extern "C"
|
||||
extern void maxscaleUse(Parse*, Token*);
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
switch (token)
|
||||
|
@ -131,6 +131,7 @@ extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
|
||||
extern void maxscaleUse(Parse*, Token*);
|
||||
|
||||
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
|
||||
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
||||
@ -966,6 +967,10 @@ cmd ::= select(X). {
|
||||
%destructor select {sqlite3SelectDelete(pParse->db, $$);}
|
||||
%type selectnowith {Select*}
|
||||
%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
|
||||
%ifdef MAXSCALE
|
||||
%type selectnowithsuffix {Select*}
|
||||
%destructor selectnowithsuffix {sqlite3SelectDelete(pParse->db, $$);}
|
||||
%endif
|
||||
%type oneselect {Select*}
|
||||
%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). {
|
||||
%endif
|
||||
Select *p = X;
|
||||
if( p ){
|
||||
p->pWith = W;
|
||||
@ -1004,6 +1014,15 @@ select(A) ::= with(W) selectnowith(X). {
|
||||
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;}
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
||||
|
@ -24,3 +24,4 @@ QUERY_TYPE_READ|QUERY_TYPE_WRITE
|
||||
QUERY_TYPE_READ|QUERY_TYPE_WRITE
|
||||
QUERY_TYPE_READ|QUERY_TYPE_WRITE
|
||||
QUERY_TYPE_DEALLOC_PREPARE
|
||||
QUERY_TYPE_WRITE
|
||||
|
@ -24,3 +24,4 @@ SELECT IS_FREE_LOCK('lock1');
|
||||
SELECT IS_USED_LOCK('lock1');
|
||||
SELECT RELEASE_LOCK('lock1');
|
||||
deallocate prepare select_stmt;
|
||||
SELECT a FROM tbl FOR UPDATE;
|
||||
|
@ -543,8 +543,7 @@ SRWBackend RWSplitSession::get_hinted_backend(char* name)
|
||||
|
||||
/** The server must be a valid slave, relay server, or master */
|
||||
if ((backend->in_use() || (can_recover_servers() && backend->can_connect()))
|
||||
&& strcasecmp(name, backend->name()) == 0
|
||||
&& (backend->is_slave() || backend->is_relay() || backend->is_master()))
|
||||
&& strcasecmp(name, backend->name()) == 0)
|
||||
{
|
||||
rval = backend;
|
||||
break;
|
||||
|
Reference in New Issue
Block a user