2019-11-02 10:20:46 +00:00
# include "MySQLHandler.h"
2020-04-16 12:31:57 +00:00
2019-07-28 00:55:46 +00:00
# include <limits>
# include <ext/scope_guard.h>
2019-03-16 02:08:21 +00:00
# include <Columns/ColumnVector.h>
# include <Common/NetException.h>
2019-05-26 06:52:29 +00:00
# include <Common/OpenSSLHelpers.h>
2019-07-28 00:55:46 +00:00
# include <Core/MySQLProtocol.h>
# include <Core/NamesAndTypes.h>
# include <DataStreams/copyData.h>
# include <Interpreters/executeQuery.h>
# include <IO/ReadBufferFromPocoSocket.h>
# include <IO/ReadBufferFromString.h>
# include <IO/WriteBufferFromPocoSocket.h>
# include <Storages/IStorage.h>
2019-11-11 14:31:12 +00:00
# include <boost/algorithm/string/replace.hpp>
Enhanced compatibility with native mysql-connector-java(JDBC) (#10021)
* Skip the `/* comments */ SELECT @@variables ...` from mysql-connector-java setup for MySQL Handler #9336
mysql-connector setup query:
/* mysql-connector-java-5.1.38 ( Revision: ${revinfo.commit} ) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout...
ClickHouse side Error:
{} <Error> executeQuery: Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST, NULL...
Client side Exception:
java.sql.SQLException: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST...
* add repalce 'SHOW VARIABLES' for mysql-connector-java-5.1.34 #9336
* Add java client(JDBC) integration test to test_mysql_protocol
* shift out java tests from dbms
* Update MySQLHandler.cpp
* Update MySQLHandler.cpp
* test_mysql_protocol: add Test.java exit code 1 when expection
Co-authored-by: alexey-milovidov <milovidov@yandex-team.ru>
2020-04-08 21:52:19 +00:00
# include <regex>
2019-07-09 14:59:52 +00:00
2020-04-16 12:31:57 +00:00
# if !defined(ARCADIA_BUILD)
# include <Common / config_version.h>
# endif
2020-05-08 14:11:19 +00:00
# if USE_SSL
2020-04-16 12:31:57 +00:00
# include <Poco / Crypto / CipherFactory.h>
# include <Poco / Crypto / RSAKey.h>
# include <Poco / Net / SSLManager.h>
# include <Poco / Net / SecureStreamSocket.h>
2019-11-02 10:20:46 +00:00
# endif
2019-03-16 02:08:21 +00:00
namespace DB
{
2019-07-09 14:59:52 +00:00
2019-03-16 02:08:21 +00:00
using namespace MySQLProtocol ;
2019-07-09 14:59:52 +00:00
2020-05-08 14:11:19 +00:00
# if USE_SSL
2019-04-29 06:05:30 +00:00
using Poco : : Net : : SecureStreamSocket ;
using Poco : : Net : : SSLManager ;
2019-11-02 10:20:46 +00:00
# endif
2019-07-09 14:59:52 +00:00
2019-03-26 18:30:41 +00:00
namespace ErrorCodes
2019-03-16 02:08:21 +00:00
{
2020-02-25 18:02:41 +00:00
extern const int CANNOT_READ_ALL_DATA ;
extern const int NOT_IMPLEMENTED ;
2019-05-26 06:52:29 +00:00
extern const int MYSQL_CLIENT_INSUFFICIENT_CAPABILITIES ;
2019-11-02 10:20:46 +00:00
extern const int SUPPORT_IS_DISABLED ;
2019-03-16 02:08:21 +00:00
}
2020-07-06 02:07:38 +00:00
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 ) ;
2019-11-02 10:20:46 +00:00
MySQLHandler : : MySQLHandler ( IServer & server_ , const Poco : : Net : : StreamSocket & socket_ ,
bool ssl_enabled , size_t connection_id_ )
2019-05-26 06:52:29 +00:00
: Poco : : Net : : TCPServerConnection ( socket_ )
, server ( server_ )
, log ( & Poco : : Logger : : get ( " MySQLHandler " ) )
, connection_context ( server . context ( ) )
2019-08-03 11:02:40 +00:00
, connection_id ( connection_id_ )
2019-10-08 13:44:44 +00:00
, auth_plugin ( new MySQLProtocol : : Authentication : : Native41 ( ) )
2019-05-26 06:52:29 +00:00
{
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 )
server_capability_flags | = CLIENT_SSL ;
2020-07-06 02:07:38 +00:00
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 ) ;
2019-05-26 06:52:29 +00:00
}
2019-03-16 02:08:21 +00:00
2019-03-26 18:30:41 +00:00
void MySQLHandler : : run ( )
{
2019-07-08 00:51:43 +00:00
connection_context . makeSessionContext ( ) ;
2019-05-26 06:52:29 +00:00
connection_context . setDefaultFormat ( " MySQLWire " ) ;
2019-03-16 02:08:21 +00:00
2019-05-16 03:34:04 +00:00
in = std : : make_shared < ReadBufferFromPocoSocket > ( socket ( ) ) ;
out = std : : make_shared < WriteBufferFromPocoSocket > ( socket ( ) ) ;
2019-07-16 06:39:18 +00:00
packet_sender = std : : make_shared < PacketSender > ( * in , * out , connection_context . mysql . sequence_id ) ;
2019-03-16 02:08:21 +00:00
try
{
2019-07-28 13:12:26 +00:00
Handshake handshake ( server_capability_flags , connection_id , VERSION_STRING + String ( " - " ) + VERSION_NAME , auth_plugin - > getName ( ) , auth_plugin - > getAuthPluginData ( ) ) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sendPacket < Handshake > ( handshake , true ) ;
2019-03-26 18:30:41 +00:00
2020-05-23 22:24:01 +00:00
LOG_TRACE ( log , " Sent handshake " ) ;
2019-03-26 18:30:41 +00:00
2019-07-01 05:58:31 +00:00
HandshakeResponse handshake_response ;
finishHandshake ( handshake_response ) ;
2019-07-16 06:39:18 +00:00
connection_context . mysql . client_capabilities = handshake_response . capability_flags ;
2019-05-16 17:15:43 +00:00
if ( handshake_response . max_packet_size )
2019-07-16 06:39:18 +00:00
connection_context . mysql . max_packet_size = handshake_response . max_packet_size ;
if ( ! connection_context . mysql . max_packet_size )
connection_context . mysql . max_packet_size = MAX_PACKET_LENGTH ;
2019-03-26 18:30:41 +00:00
2020-05-23 22:24:01 +00:00
LOG_TRACE ( log ,
2020-05-23 22:21:29 +00:00
" Capabilities: {}, max_packet_size: {}, character_set: {}, user: {}, auth_response length: {}, database: {}, auth_plugin_name: {} " ,
handshake_response . capability_flags ,
handshake_response . max_packet_size ,
static_cast < int > ( handshake_response . character_set ) ,
handshake_response . username ,
handshake_response . auth_response . length ( ) ,
handshake_response . database ,
handshake_response . auth_plugin_name ) ;
2019-03-16 02:08:21 +00:00
2019-05-26 06:52:29 +00:00
client_capability_flags = handshake_response . capability_flags ;
if ( ! ( client_capability_flags & CLIENT_PROTOCOL_41 ) )
2019-03-26 18:30:41 +00:00
throw Exception ( " Required capability: CLIENT_PROTOCOL_41. " , ErrorCodes : : MYSQL_CLIENT_INSUFFICIENT_CAPABILITIES ) ;
2019-03-16 02:08:21 +00:00
2019-07-28 13:12:26 +00:00
authenticate ( handshake_response . username , handshake_response . auth_plugin_name , handshake_response . auth_response ) ;
try
{
if ( ! handshake_response . database . empty ( ) )
connection_context . setCurrentDatabase ( handshake_response . database ) ;
2020-07-06 01:02:02 +00:00
connection_context . setCurrentQueryId ( Poco : : format ( " mysql:%lu " , connection_id ) ) ;
2019-07-28 13:12:26 +00:00
}
catch ( const Exception & exc )
{
log - > log ( exc ) ;
packet_sender - > sendPacket ( ERR_Packet ( exc . code ( ) , " 00000 " , exc . message ( ) ) , true ) ;
}
2019-05-16 05:36:08 +00:00
OK_Packet ok_packet ( 0 , handshake_response . capability_flags , 0 , 0 , 0 ) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sendPacket ( ok_packet , true ) ;
2019-03-16 02:08:21 +00:00
2019-05-26 19:30:23 +00:00
while ( true )
2019-03-26 18:30:41 +00:00
{
2019-05-16 03:34:04 +00:00
packet_sender - > resetSequenceId ( ) ;
2019-07-14 22:13:56 +00:00
PacketPayloadReadBuffer payload = packet_sender - > getPayload ( ) ;
2019-07-19 18:29:39 +00:00
char command = 0 ;
2019-07-19 18:46:57 +00:00
payload . readStrict ( command ) ;
2019-07-14 22:13:56 +00:00
2019-07-19 17:50:42 +00:00
// For commands which are executed without MemoryTracker.
LimitReadBuffer limited_payload ( payload , 10000 , true , " too long MySQL packet. " ) ;
2020-05-23 22:24:01 +00:00
LOG_DEBUG ( log , " Received command: {}. Connection id: {}. " ,
2020-05-23 22:21:29 +00:00
static_cast < int > ( static_cast < unsigned char > ( command ) ) , connection_id ) ;
2019-03-26 18:30:41 +00:00
try
{
switch ( command )
{
2019-03-16 02:08:21 +00:00
case COM_QUIT :
return ;
case COM_INIT_DB :
2019-07-19 17:50:42 +00:00
comInitDB ( limited_payload ) ;
2019-03-16 02:08:21 +00:00
break ;
case COM_QUERY :
2019-07-14 22:13:56 +00:00
comQuery ( payload ) ;
2019-03-16 02:08:21 +00:00
break ;
case COM_FIELD_LIST :
2019-07-19 17:50:42 +00:00
comFieldList ( limited_payload ) ;
2019-03-16 02:08:21 +00:00
break ;
case COM_PING :
comPing ( ) ;
break ;
default :
throw Exception ( Poco : : format ( " Command %d is not implemented. " , command ) , ErrorCodes : : NOT_IMPLEMENTED ) ;
}
}
2019-03-26 18:30:41 +00:00
catch ( const NetException & exc )
{
2019-03-16 02:08:21 +00:00
log - > log ( exc ) ;
throw ;
}
2019-03-26 18:30:41 +00:00
catch ( const Exception & exc )
{
2019-03-16 02:08:21 +00:00
log - > log ( exc ) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sendPacket ( ERR_Packet ( exc . code ( ) , " 00000 " , exc . message ( ) ) , true ) ;
2019-03-16 02:08:21 +00:00
}
}
}
2019-07-19 17:50:42 +00:00
catch ( const Poco : : Exception & exc )
2019-03-16 02:08:21 +00:00
{
log - > log ( exc ) ;
}
}
2019-05-16 03:34:04 +00:00
/** Reads 3 bytes, finds out whether it is SSLRequest or HandshakeResponse packet, starts secure connection, if it is SSLRequest.
2019-05-16 17:15:43 +00:00
* Reading is performed from socket instead of ReadBuffer to prevent reading part of SSL handshake .
2019-05-16 03:34:04 +00:00
* If we read it from socket , it will be impossible to start SSL connection using Poco . Size of SSLRequest packet payload is 32 bytes , thus we can read at most 36 bytes .
*/
2019-07-01 05:58:31 +00:00
void MySQLHandler : : finishHandshake ( MySQLProtocol : : HandshakeResponse & packet )
2019-04-29 06:05:30 +00:00
{
2019-05-16 17:15:43 +00:00
size_t packet_size = PACKET_HEADER_SIZE + SSL_REQUEST_PAYLOAD_SIZE ;
/// Buffer for SSLRequest or part of HandshakeResponse.
char buf [ packet_size ] ;
2019-04-29 06:05:30 +00:00
size_t pos = 0 ;
2019-05-16 17:15:43 +00:00
/// Reads at least count and at most packet_size bytes.
auto read_bytes = [ this , & buf , & pos , & packet_size ] ( size_t count ) - > void {
while ( pos < count )
2019-04-29 06:37:39 +00:00
{
2019-05-16 17:15:43 +00:00
int ret = socket ( ) . receiveBytes ( buf + pos , packet_size - pos ) ;
if ( ret = = 0 )
{
throw Exception ( " Cannot read all data. Bytes read: " + std : : to_string ( pos ) + " . Bytes expected: 3. " , ErrorCodes : : CANNOT_READ_ALL_DATA ) ;
}
pos + = ret ;
2019-04-29 06:05:30 +00:00
}
2019-05-16 17:15:43 +00:00
} ;
read_bytes ( 3 ) ; /// We can find out whether it is SSLRequest of HandshakeResponse by first 3 bytes.
2019-04-29 06:05:30 +00:00
2019-05-26 06:52:29 +00:00
size_t payload_size = unalignedLoad < uint32_t > ( buf ) & 0xFFFFFFu ;
2020-05-23 22:24:01 +00:00
LOG_TRACE ( log , " payload size: {} " , payload_size ) ;
2019-05-16 03:34:04 +00:00
2019-05-16 17:15:43 +00:00
if ( payload_size = = SSL_REQUEST_PAYLOAD_SIZE )
2019-04-29 06:37:39 +00:00
{
2019-11-02 10:20:46 +00:00
finishHandshakeSSL ( packet_size , buf , pos , read_bytes , packet ) ;
2019-04-29 06:37:39 +00:00
}
else
{
2019-05-16 03:34:04 +00:00
/// Reading rest of HandshakeResponse.
2019-05-16 17:15:43 +00:00
packet_size = PACKET_HEADER_SIZE + payload_size ;
2019-07-14 22:13:56 +00:00
WriteBufferFromOwnString buf_for_handshake_response ;
2019-05-16 17:15:43 +00:00
buf_for_handshake_response . write ( buf , pos ) ;
copyData ( * packet_sender - > in , buf_for_handshake_response , packet_size - pos ) ;
2019-07-14 22:13:56 +00:00
ReadBufferFromString payload ( buf_for_handshake_response . str ( ) ) ;
payload . ignore ( PACKET_HEADER_SIZE ) ;
packet . readPayload ( payload ) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sequence_id + + ;
2019-04-29 06:05:30 +00:00
}
}
2019-04-22 10:57:50 +00:00
2019-07-28 13:12:26 +00:00
void MySQLHandler : : authenticate ( const String & user_name , const String & auth_plugin_name , const String & initial_auth_response )
2019-04-22 10:57:50 +00:00
{
2019-11-20 04:30:00 +00:00
try
{
2019-11-19 23:27:30 +00:00
// For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible (if password is specified using double SHA1). Otherwise SHA256 plugin is used.
2020-02-12 03:03:33 +00:00
auto user = connection_context . getAccessControlManager ( ) . read < User > ( user_name ) ;
2019-12-05 01:52:48 +00:00
const DB : : Authentication : : Type user_auth_type = user - > authentication . getType ( ) ;
2019-12-04 23:37:11 +00:00
if ( user_auth_type ! = DB : : Authentication : : DOUBLE_SHA1_PASSWORD & & user_auth_type ! = DB : : Authentication : : PLAINTEXT_PASSWORD & & user_auth_type ! = DB : : Authentication : : NO_PASSWORD )
2019-11-19 23:27:30 +00:00
{
authPluginSSL ( ) ;
}
2019-07-28 13:12:26 +00:00
std : : optional < String > auth_response = auth_plugin_name = = auth_plugin - > getName ( ) ? std : : make_optional < String > ( initial_auth_response ) : std : : nullopt ;
2019-11-12 19:57:39 +00:00
auth_plugin - > authenticate ( user_name , auth_response , connection_context , packet_sender , secure_connection , socket ( ) . peerAddress ( ) ) ;
2019-04-22 10:57:50 +00:00
}
catch ( const Exception & exc )
{
2020-05-23 22:24:01 +00:00
LOG_ERROR ( log , " Authentication for user {} failed. " , user_name ) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sendPacket ( ERR_Packet ( exc . code ( ) , " 00000 " , exc . message ( ) ) , true ) ;
2019-04-22 10:57:50 +00:00
throw ;
}
2020-05-23 22:24:01 +00:00
LOG_INFO ( log , " Authentication for user {} succeeded. " , user_name ) ;
2019-04-22 10:57:50 +00:00
}
2019-07-14 22:13:56 +00:00
void MySQLHandler : : comInitDB ( ReadBuffer & payload )
2019-03-16 02:08:21 +00:00
{
2019-07-14 22:13:56 +00:00
String database ;
readStringUntilEOF ( database , payload ) ;
2020-05-23 22:24:01 +00:00
LOG_DEBUG ( log , " Setting current database to {} " , database ) ;
2019-03-16 02:08:21 +00:00
connection_context . setCurrentDatabase ( database ) ;
2019-05-26 06:52:29 +00:00
packet_sender - > sendPacket ( OK_Packet ( 0 , client_capability_flags , 0 , 0 , 1 ) , true ) ;
2019-03-16 02:08:21 +00:00
}
2019-07-14 22:13:56 +00:00
void MySQLHandler : : comFieldList ( ReadBuffer & payload )
2019-03-26 18:30:41 +00:00
{
2019-03-16 02:08:21 +00:00
ComFieldList packet ;
2019-07-14 22:13:56 +00:00
packet . readPayload ( payload ) ;
2019-03-26 18:30:41 +00:00
String database = connection_context . getCurrentDatabase ( ) ;
2020-05-28 23:01:18 +00:00
StoragePtr table_ptr = DatabaseCatalog : : instance ( ) . getTable ( { database , packet . table } , connection_context ) ;
2020-06-17 16:39:58 +00:00
auto metadata_snapshot = table_ptr - > getInMemoryMetadataPtr ( ) ;
for ( const NameAndTypePair & column : metadata_snapshot - > getColumns ( ) . getAll ( ) )
2019-03-16 02:08:21 +00:00
{
ColumnDefinition column_definition (
2019-03-26 18:30:41 +00:00
database , packet . table , packet . table , column . name , column . name , CharacterSet : : binary , 100 , ColumnType : : MYSQL_TYPE_STRING , 0 , 0
2019-03-16 02:08:21 +00:00
) ;
2019-05-16 03:34:04 +00:00
packet_sender - > sendPacket ( column_definition ) ;
2019-03-16 02:08:21 +00:00
}
2019-05-26 06:52:29 +00:00
packet_sender - > sendPacket ( OK_Packet ( 0xfe , client_capability_flags , 0 , 0 , 0 ) , true ) ;
2019-03-16 02:08:21 +00:00
}
2019-03-26 18:30:41 +00:00
void MySQLHandler : : comPing ( )
{
2019-05-26 06:52:29 +00:00
packet_sender - > sendPacket ( OK_Packet ( 0x0 , client_capability_flags , 0 , 0 , 0 ) , true ) ;
2019-03-16 02:08:21 +00:00
}
Enhanced compatibility with native mysql-connector-java(JDBC) (#10021)
* Skip the `/* comments */ SELECT @@variables ...` from mysql-connector-java setup for MySQL Handler #9336
mysql-connector setup query:
/* mysql-connector-java-5.1.38 ( Revision: ${revinfo.commit} ) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout...
ClickHouse side Error:
{} <Error> executeQuery: Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST, NULL...
Client side Exception:
java.sql.SQLException: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST...
* add repalce 'SHOW VARIABLES' for mysql-connector-java-5.1.34 #9336
* Add java client(JDBC) integration test to test_mysql_protocol
* shift out java tests from dbms
* Update MySQLHandler.cpp
* Update MySQLHandler.cpp
* test_mysql_protocol: add Test.java exit code 1 when expection
Co-authored-by: alexey-milovidov <milovidov@yandex-team.ru>
2020-04-08 21:52:19 +00:00
static bool isFederatedServerSetupSetCommand ( const String & query ) ;
2019-11-28 14:43:52 +00:00
2019-07-14 22:13:56 +00:00
void MySQLHandler : : comQuery ( ReadBuffer & payload )
2019-03-26 18:30:41 +00:00
{
2019-11-28 14:43:52 +00:00
String query = String ( payload . position ( ) , payload . buffer ( ) . end ( ) ) ;
2019-07-16 07:11:59 +00:00
2019-11-11 14:31:12 +00:00
// This is a workaround in order to support adding ClickHouse to MySQL using federated server.
// As Clickhouse doesn't support these statements, we just send OK packet in response.
Enhanced compatibility with native mysql-connector-java(JDBC) (#10021)
* Skip the `/* comments */ SELECT @@variables ...` from mysql-connector-java setup for MySQL Handler #9336
mysql-connector setup query:
/* mysql-connector-java-5.1.38 ( Revision: ${revinfo.commit} ) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout...
ClickHouse side Error:
{} <Error> executeQuery: Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST, NULL...
Client side Exception:
java.sql.SQLException: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST...
* add repalce 'SHOW VARIABLES' for mysql-connector-java-5.1.34 #9336
* Add java client(JDBC) integration test to test_mysql_protocol
* shift out java tests from dbms
* Update MySQLHandler.cpp
* Update MySQLHandler.cpp
* test_mysql_protocol: add Test.java exit code 1 when expection
Co-authored-by: alexey-milovidov <milovidov@yandex-team.ru>
2020-04-08 21:52:19 +00:00
if ( isFederatedServerSetupSetCommand ( query ) )
2019-07-16 07:11:59 +00:00
{
2019-11-11 14:31:12 +00:00
packet_sender - > sendPacket ( OK_Packet ( 0x00 , client_capability_flags , 0 , 0 , 0 ) , true ) ;
2019-07-16 07:11:59 +00:00
}
2019-11-28 15:02:02 +00:00
else
2019-11-11 14:31:12 +00:00
{
2020-07-06 02:07:38 +00:00
String replacement_query ;
2019-11-11 14:31:12 +00:00
bool should_replace = false ;
2020-03-03 15:32:41 +00:00
bool with_output = false ;
2019-07-14 08:22:55 +00:00
2020-07-06 02:07:38 +00:00
for ( auto const & x : replacements )
2020-07-06 01:02:02 +00:00
{
2020-07-06 02:07:38 +00:00
if ( 0 = = strncasecmp ( x . first . c_str ( ) , query . c_str ( ) , x . first . size ( ) ) )
{
should_replace = true ;
replacement_query = x . second ( query ) ;
break ;
}
2019-11-11 14:31:12 +00:00
}
2019-07-16 07:11:59 +00:00
2019-11-11 14:31:12 +00:00
ReadBufferFromString replacement ( replacement_query ) ;
2019-07-14 08:22:55 +00:00
2019-11-11 14:31:12 +00:00
Context query_context = connection_context ;
2020-03-03 15:32:41 +00:00
executeQuery ( should_replace ? replacement : payload , * out , true , query_context ,
[ & with_output ] ( const String & , const String & , const String & , const String & )
{
with_output = true ;
}
) ;
2019-11-11 14:31:12 +00:00
if ( ! with_output )
packet_sender - > sendPacket ( OK_Packet ( 0x00 , client_capability_flags , 0 , 0 , 0 ) , true ) ;
}
2019-03-16 02:08:21 +00:00
}
2019-11-02 10:20:46 +00:00
void MySQLHandler : : authPluginSSL ( )
{
2019-11-19 23:27:30 +00:00
throw Exception ( " ClickHouse was built without SSL support. Try specifying password using double SHA1 in users.xml. " , ErrorCodes : : SUPPORT_IS_DISABLED ) ;
2019-11-02 10:20:46 +00:00
}
void MySQLHandler : : finishHandshakeSSL ( [[maybe_unused]] size_t packet_size, [[maybe_unused]] char * buf, [[maybe_unused]] size_t pos, [[maybe_unused]] std::function<void(size_t)> read_bytes, [[maybe_unused]] MySQLProtocol : : HandshakeResponse & packet )
{
2019-11-19 23:27:30 +00:00
throw Exception ( " Client requested SSL, while it is disabled. " , ErrorCodes : : SUPPORT_IS_DISABLED ) ;
2019-11-02 10:20:46 +00:00
}
2020-05-08 14:11:19 +00:00
# if USE_SSL
2019-11-02 10:20:46 +00:00
MySQLHandlerSSL : : MySQLHandlerSSL ( IServer & server_ , const Poco : : Net : : StreamSocket & socket_ , bool ssl_enabled , size_t connection_id_ , RSA & public_key_ , RSA & private_key_ )
: MySQLHandler ( server_ , socket_ , ssl_enabled , connection_id_ )
, public_key ( public_key_ )
, private_key ( private_key_ )
{ }
void MySQLHandlerSSL : : authPluginSSL ( )
{
auth_plugin = std : : make_unique < MySQLProtocol : : Authentication : : Sha256Password > ( public_key , private_key , log ) ;
}
void MySQLHandlerSSL : : finishHandshakeSSL ( size_t packet_size , char * buf , size_t pos , std : : function < void ( size_t ) > read_bytes , MySQLProtocol : : HandshakeResponse & packet )
{
read_bytes ( packet_size ) ; /// Reading rest SSLRequest.
SSLRequest ssl_request ;
ReadBufferFromMemory payload ( buf , pos ) ;
payload . ignore ( PACKET_HEADER_SIZE ) ;
ssl_request . readPayload ( payload ) ;
connection_context . mysql . client_capabilities = ssl_request . capability_flags ;
connection_context . mysql . max_packet_size = ssl_request . max_packet_size ? ssl_request . max_packet_size : MAX_PACKET_LENGTH ;
secure_connection = true ;
ss = std : : make_shared < SecureStreamSocket > ( SecureStreamSocket : : attach ( socket ( ) , SSLManager : : instance ( ) . defaultServerContext ( ) ) ) ;
in = std : : make_shared < ReadBufferFromPocoSocket > ( * ss ) ;
out = std : : make_shared < WriteBufferFromPocoSocket > ( * ss ) ;
connection_context . mysql . sequence_id = 2 ;
packet_sender = std : : make_shared < PacketSender > ( * in , * out , connection_context . mysql . sequence_id ) ;
packet_sender - > max_packet_size = connection_context . mysql . max_packet_size ;
packet_sender - > receivePacket ( packet ) ; /// Reading HandshakeResponse from secure socket.
2019-03-16 02:08:21 +00:00
}
2019-11-02 10:20:46 +00:00
2019-09-11 11:21:54 +00:00
# endif
2019-11-02 10:20:46 +00:00
Enhanced compatibility with native mysql-connector-java(JDBC) (#10021)
* Skip the `/* comments */ SELECT @@variables ...` from mysql-connector-java setup for MySQL Handler #9336
mysql-connector setup query:
/* mysql-connector-java-5.1.38 ( Revision: ${revinfo.commit} ) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout...
ClickHouse side Error:
{} <Error> executeQuery: Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST, NULL...
Client side Exception:
java.sql.SQLException: Syntax error: failed at position 74: @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_conn. Expected one of: CAST...
* add repalce 'SHOW VARIABLES' for mysql-connector-java-5.1.34 #9336
* Add java client(JDBC) integration test to test_mysql_protocol
* shift out java tests from dbms
* Update MySQLHandler.cpp
* Update MySQLHandler.cpp
* test_mysql_protocol: add Test.java exit code 1 when expection
Co-authored-by: alexey-milovidov <milovidov@yandex-team.ru>
2020-04-08 21:52:19 +00:00
static bool isFederatedServerSetupSetCommand ( const String & query )
{
static const std : : regex expr {
" (^(SET NAMES(.*))) "
" |(^(SET character_set_results(.*))) "
" |(^(SET FOREIGN_KEY_CHECKS(.*))) "
" |(^(SET AUTOCOMMIT(.*))) "
" |(^(SET sql_mode(.*))) "
" |(^(SET SESSION TRANSACTION ISOLATION LEVEL(.*))) "
, std : : regex : : icase } ;
return 1 = = std : : regex_match ( query , expr ) ;
}
2020-07-06 02:07:38 +00:00
/// Replace "[query(such as SHOW VARIABLES...)]" into "".
static String select_empty_replacement_query ( const String & query )
2020-07-06 01:02:02 +00:00
{
2020-07-06 02:07:38 +00:00
std : : ignore = query ;
return " select '' " ;
}
2020-07-06 01:02:02 +00:00
2020-07-06 02:07:38 +00:00
/// Replace "SHOW TABLE STATUS LIKE 'xx'" into "SELECT ... FROM system.tables WHERE name LIKE 'xx'".
static String show_table_status_replacement_query ( const String & query )
{
const String prefix = " SHOW TABLE STATUS LIKE " ;
if ( query . size ( ) > prefix . size ( ) )
2020-07-06 01:02:02 +00:00
{
2020-07-06 02:07:38 +00:00
String suffix = query . data ( ) + prefix . length ( ) ;
return (
" SELECT "
" name AS Name, "
" engine AS Engine, "
" '10' AS Version, "
" 'Dynamic' AS Row_format, "
" 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 ;
}
2020-07-06 01:02:02 +00:00
2020-07-06 02:07:38 +00:00
/// 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 ( ) ;
2020-07-06 01:02:02 +00:00
static const std : : regex expr { " ^[0-9] " } ;
2020-07-06 02:07:38 +00:00
if ( std : : regex_match ( suffix , expr ) )
2020-07-06 01:02:02 +00:00
{
2020-07-06 02:07:38 +00:00
String replacement = Poco : : format ( " KILL QUERY WHERE query_id = 'mysql:%s' " , suffix ) ;
2020-07-06 01:02:02 +00:00
return replacement ;
}
}
return query ;
2019-11-02 10:20:46 +00:00
}
2020-07-06 01:02:02 +00:00
}