mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
Merge pull request #12152 from BohuTANG/mysql_kill_query
Support MySQL 'KILL QUERY [connection_id]'
This commit is contained in:
commit
dce7709405
@ -45,6 +45,10 @@ namespace ErrorCodes
|
|||||||
extern const int SUPPORT_IS_DISABLED;
|
extern const int SUPPORT_IS_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String select_empty_replacement_query(const String & query);
|
||||||
|
static String show_table_status_replacement_query(const String & query);
|
||||||
|
static String kill_connection_id_replacement_query(const String & query);
|
||||||
|
|
||||||
MySQLHandler::MySQLHandler(IServer & server_, const Poco::Net::StreamSocket & socket_,
|
MySQLHandler::MySQLHandler(IServer & server_, const Poco::Net::StreamSocket & socket_,
|
||||||
bool ssl_enabled, size_t connection_id_)
|
bool ssl_enabled, size_t connection_id_)
|
||||||
: Poco::Net::TCPServerConnection(socket_)
|
: Poco::Net::TCPServerConnection(socket_)
|
||||||
@ -57,6 +61,10 @@ MySQLHandler::MySQLHandler(IServer & server_, const Poco::Net::StreamSocket & so
|
|||||||
server_capability_flags = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | CLIENT_CONNECT_WITH_DB | CLIENT_DEPRECATE_EOF;
|
server_capability_flags = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | CLIENT_CONNECT_WITH_DB | CLIENT_DEPRECATE_EOF;
|
||||||
if (ssl_enabled)
|
if (ssl_enabled)
|
||||||
server_capability_flags |= CLIENT_SSL;
|
server_capability_flags |= CLIENT_SSL;
|
||||||
|
|
||||||
|
replacements.emplace("KILL QUERY", kill_connection_id_replacement_query);
|
||||||
|
replacements.emplace("SHOW TABLE STATUS LIKE", show_table_status_replacement_query);
|
||||||
|
replacements.emplace("SHOW VARIABLES", select_empty_replacement_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQLHandler::run()
|
void MySQLHandler::run()
|
||||||
@ -103,7 +111,8 @@ void MySQLHandler::run()
|
|||||||
{
|
{
|
||||||
if (!handshake_response.database.empty())
|
if (!handshake_response.database.empty())
|
||||||
connection_context.setCurrentDatabase(handshake_response.database);
|
connection_context.setCurrentDatabase(handshake_response.database);
|
||||||
connection_context.setCurrentQueryId("");
|
connection_context.setCurrentQueryId(Poco::format("mysql:%lu", connection_id));
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (const Exception & exc)
|
catch (const Exception & exc)
|
||||||
{
|
{
|
||||||
@ -284,20 +293,18 @@ void MySQLHandler::comQuery(ReadBuffer & payload)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String replacement_query = "SELECT ''";
|
String replacement_query;
|
||||||
bool should_replace = false;
|
bool should_replace = false;
|
||||||
bool with_output = false;
|
bool with_output = false;
|
||||||
|
|
||||||
// This is a workaround in order to support adding ClickHouse to MySQL using federated server.
|
for (auto const & x : replacements)
|
||||||
if (0 == strncasecmp("SHOW TABLE STATUS LIKE", query.c_str(), 22))
|
|
||||||
{
|
{
|
||||||
should_replace = true;
|
if (0 == strncasecmp(x.first.c_str(), query.c_str(), x.first.size()))
|
||||||
replacement_query = boost::replace_all_copy(query, "SHOW TABLE STATUS LIKE ", show_table_status_replacement_query);
|
{
|
||||||
}
|
should_replace = true;
|
||||||
|
replacement_query = x.second(query);
|
||||||
if (0 == strncasecmp("SHOW VARIABLES", query.c_str(), 13))
|
break;
|
||||||
{
|
}
|
||||||
should_replace = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadBufferFromString replacement(replacement_query);
|
ReadBufferFromString replacement(replacement_query);
|
||||||
@ -372,26 +379,63 @@ static bool isFederatedServerSetupSetCommand(const String & query)
|
|||||||
return 1 == std::regex_match(query, expr);
|
return 1 == std::regex_match(query, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const String MySQLHandler::show_table_status_replacement_query("SELECT"
|
/// Replace "[query(such as SHOW VARIABLES...)]" into "".
|
||||||
" name AS Name,"
|
static String select_empty_replacement_query(const String & query)
|
||||||
" engine AS Engine,"
|
{
|
||||||
" '10' AS Version,"
|
std::ignore = query;
|
||||||
" 'Dynamic' AS Row_format,"
|
return "select ''";
|
||||||
" 0 AS Rows,"
|
}
|
||||||
" 0 AS Avg_row_length,"
|
|
||||||
" 0 AS Data_length,"
|
/// Replace "SHOW TABLE STATUS LIKE 'xx'" into "SELECT ... FROM system.tables WHERE name LIKE 'xx'".
|
||||||
" 0 AS Max_data_length,"
|
static String show_table_status_replacement_query(const String & query)
|
||||||
" 0 AS Index_length,"
|
{
|
||||||
" 0 AS Data_free,"
|
const String prefix = "SHOW TABLE STATUS LIKE ";
|
||||||
" 'NULL' AS Auto_increment,"
|
if (query.size() > prefix.size())
|
||||||
" metadata_modification_time AS Create_time,"
|
{
|
||||||
" metadata_modification_time AS Update_time,"
|
String suffix = query.data() + prefix.length();
|
||||||
" metadata_modification_time AS Check_time,"
|
return (
|
||||||
" 'utf8_bin' AS Collation,"
|
"SELECT"
|
||||||
" 'NULL' AS Checksum,"
|
" name AS Name,"
|
||||||
" '' AS Create_options,"
|
" engine AS Engine,"
|
||||||
" '' AS Comment"
|
" '10' AS Version,"
|
||||||
" FROM system.tables"
|
" 'Dynamic' AS Row_format,"
|
||||||
" WHERE name LIKE ");
|
" 0 AS Rows,"
|
||||||
|
" 0 AS Avg_row_length,"
|
||||||
|
" 0 AS Data_length,"
|
||||||
|
" 0 AS Max_data_length,"
|
||||||
|
" 0 AS Index_length,"
|
||||||
|
" 0 AS Data_free,"
|
||||||
|
" 'NULL' AS Auto_increment,"
|
||||||
|
" metadata_modification_time AS Create_time,"
|
||||||
|
" metadata_modification_time AS Update_time,"
|
||||||
|
" metadata_modification_time AS Check_time,"
|
||||||
|
" 'utf8_bin' AS Collation,"
|
||||||
|
" 'NULL' AS Checksum,"
|
||||||
|
" '' AS Create_options,"
|
||||||
|
" '' AS Comment"
|
||||||
|
" FROM system.tables"
|
||||||
|
" WHERE name LIKE "
|
||||||
|
+ suffix);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace "KILL QUERY [connection_id]" into "KILL QUERY WHERE query_id = 'mysql:[connection_id]'".
|
||||||
|
static String kill_connection_id_replacement_query(const String & query)
|
||||||
|
{
|
||||||
|
const String prefix = "KILL QUERY ";
|
||||||
|
if (query.size() > prefix.size())
|
||||||
|
{
|
||||||
|
String suffix = query.data() + prefix.length();
|
||||||
|
static const std::regex expr{"^[0-9]"};
|
||||||
|
if (std::regex_match(suffix, expr))
|
||||||
|
{
|
||||||
|
String replacement = Poco::format("KILL QUERY WHERE query_id = 'mysql:%s'", suffix);
|
||||||
|
return replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,9 @@ protected:
|
|||||||
bool secure_connection = false;
|
bool secure_connection = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const String show_table_status_replacement_query;
|
using ReplacementFn = std::function<String(const String & query)>;
|
||||||
|
using Replacements = std::unordered_map<std::string, ReplacementFn>;
|
||||||
|
Replacements replacements;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if USE_SSL
|
#if USE_SSL
|
||||||
|
@ -138,6 +138,34 @@ def test_mysql_client(mysql_client, server_address):
|
|||||||
assert stdout == '\n'.join(['column', '0', '0', '1', '1', '5', '5', 'tmp_column', '0', '1', ''])
|
assert stdout == '\n'.join(['column', '0', '0', '1', '1', '5', '5', 'tmp_column', '0', '1', ''])
|
||||||
|
|
||||||
|
|
||||||
|
# Show table status.
|
||||||
|
code, (stdout, stderr) = mysql_client.exec_run('''
|
||||||
|
mysql --protocol tcp -h {host} -P {port} default -u default
|
||||||
|
--password=123 -e "show table status like 'xx';"
|
||||||
|
'''.format(host=server_address, port=server_port), demux=True)
|
||||||
|
assert code == 0
|
||||||
|
|
||||||
|
# show variables.
|
||||||
|
code, (stdout, stderr) = mysql_client.exec_run('''
|
||||||
|
mysql --protocol tcp -h {host} -P {port} default -u default
|
||||||
|
--password=123 -e "show variables;"
|
||||||
|
'''.format(host=server_address, port=server_port), demux=True)
|
||||||
|
assert code == 0
|
||||||
|
|
||||||
|
# Kill query.
|
||||||
|
code, (stdout, stderr) = mysql_client.exec_run('''
|
||||||
|
mysql --protocol tcp -h {host} -P {port} default -u default
|
||||||
|
--password=123 -e "kill query 0;"
|
||||||
|
'''.format(host=server_address, port=server_port), demux=True)
|
||||||
|
assert code == 0
|
||||||
|
|
||||||
|
code, (stdout, stderr) = mysql_client.exec_run('''
|
||||||
|
mysql --protocol tcp -h {host} -P {port} default -u default
|
||||||
|
--password=123 -e "kill query where query_id='mysql:0';"
|
||||||
|
'''.format(host=server_address, port=server_port), demux=True)
|
||||||
|
assert code == 0
|
||||||
|
|
||||||
|
|
||||||
def test_mysql_federated(mysql_server, server_address):
|
def test_mysql_federated(mysql_server, server_address):
|
||||||
# For some reason it occasionally fails without retries.
|
# For some reason it occasionally fails without retries.
|
||||||
retries = 100
|
retries = 100
|
||||||
|
Loading…
Reference in New Issue
Block a user