Files
MaxScale/server/core/queryclassifier.cc

162 lines
6.5 KiB
C++

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/queryclassifier.hh>
#include <maxscale/query_classifier.h>
#include <maxscale/protocol/mysql.h>
namespace maxscale
{
QueryClassifier::QueryClassifier(MXS_SESSION* pSession,
mxs_target_t use_sql_variables_in)
: m_pSession(pSession)
, m_use_sql_variables_in(use_sql_variables_in)
, m_load_data_state(LOAD_DATA_INACTIVE)
, m_have_tmp_tables(false)
, m_large_query(false)
{
}
uint32_t QueryClassifier::get_route_target(uint8_t command, uint32_t qtype)
{
bool trx_active = session_trx_is_active(m_pSession);
uint32_t target = TARGET_UNDEFINED;
bool load_active = (m_load_data_state != LOAD_DATA_INACTIVE);
/**
* Prepared statements preparations should go to all servers
*/
if (qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
command == MXS_COM_STMT_CLOSE ||
command == MXS_COM_STMT_RESET)
{
target = TARGET_ALL;
}
/**
* These queries should be routed to all servers
*/
else if (!load_active &&
(qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
/** Configured to allow writing user variables to all nodes */
(m_use_sql_variables_in == TYPE_ALL &&
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE)) ||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) ||
/** enable or disable autocommit are always routed to all */
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)))
{
/**
* This is problematic query because it would be routed to all
* backends but since this is SELECT that is not possible:
* 1. response set is not handled correctly in clientReply and
* 2. multiple results can degrade performance.
*
* Prepared statements are an exception to this since they do not
* actually do anything but only prepare the statement to be used.
* They can be safely routed to all backends since the execution
* is done later.
*
* With prepared statement caching the task of routing
* the execution of the prepared statements to the right server would be
* an easy one. Currently this is not supported.
*/
if (qc_query_is_type(qtype, QUERY_TYPE_READ))
{
MXS_WARNING("The query can't be routed to all "
"backend servers because it includes SELECT and "
"SQL variable modifications which is not supported. "
"Set use_sql_variables_in=master or split the "
"query to two, where SQL variable modifications "
"are done in the first and the SELECT in the "
"second one.");
target = TARGET_MASTER;
}
target |= TARGET_ALL;
}
/**
* Hints may affect on routing of the following queries
*/
else if (!trx_active && !load_active &&
!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) &&
!qc_query_is_type(qtype, QUERY_TYPE_WRITE) &&
(qc_query_is_type(qtype, QUERY_TYPE_READ) ||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) ||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)))
{
if (qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ))
{
if (m_use_sql_variables_in == TYPE_ALL)
{
target = TARGET_SLAVE;
}
}
else if (qc_query_is_type(qtype, QUERY_TYPE_READ) || // Normal read
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || // SHOW TABLES
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || // System variable
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)) // Global system variable
{
target = TARGET_SLAVE;
}
/** If nothing matches then choose the master */
if ((target & (TARGET_ALL | TARGET_SLAVE | TARGET_MASTER)) == 0)
{
target = TARGET_MASTER;
}
}
else if (session_trx_is_read_only(m_pSession))
{
/* Force TARGET_SLAVE for READ ONLY transaction (active or ending) */
target = TARGET_SLAVE;
}
else
{
ss_dassert(trx_active || load_active ||
(qc_query_is_type(qtype, QUERY_TYPE_WRITE) ||
qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) &&
m_use_sql_variables_in == TYPE_MASTER) ||
(qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) &&
m_use_sql_variables_in == TYPE_MASTER) ||
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ) &&
m_use_sql_variables_in == TYPE_MASTER) ||
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) &&
m_use_sql_variables_in == TYPE_MASTER) ||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE) &&
m_use_sql_variables_in == TYPE_MASTER) ||
qc_query_is_type(qtype, QUERY_TYPE_BEGIN_TRX) ||
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) ||
qc_query_is_type(qtype, QUERY_TYPE_ROLLBACK) ||
qc_query_is_type(qtype, QUERY_TYPE_COMMIT) ||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
qc_query_is_type(qtype, QUERY_TYPE_CREATE_TMP_TABLE) ||
qc_query_is_type(qtype, QUERY_TYPE_READ_TMP_TABLE) ||
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)) ||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT));
target = TARGET_MASTER;
}
return target;
}
}