2019-09-11 11:21:54 +00:00
# include <Common/config.h>
2019-07-28 00:55:46 +00:00
2019-11-02 10:20:46 +00:00
# include "MySQLHandler.h"
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/config_version.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>
2019-07-09 14:59:52 +00:00
2019-11-02 10:20:46 +00:00
# if USE_POCO_NETSSL
# include <Poco/Net/SecureStreamSocket.h>
# include <Poco/Net/SSLManager.h>
# include <Poco/Crypto/CipherFactory.h>
# include <Poco/Crypto/RSAKey.h>
# 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
2019-11-02 10:20:46 +00:00
# if USE_POCO_NETSSL
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
{
2019-05-26 06:52:29 +00:00
extern const int MYSQL_CLIENT_INSUFFICIENT_CAPABILITIES ;
extern const int OPENSSL_ERROR ;
2019-11-02 10:20:46 +00:00
extern const int SUPPORT_IS_DISABLED ;
2019-03-16 02:08:21 +00:00
}
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 ;
}
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
LOG_TRACE ( log , " Sent handshake " ) ;
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-01-19 00:34:00 +00:00
LOG_TRACE ( log , " Capabilities: " < < handshake_response . capability_flags
< < " , max_packet_size: "
2019-03-26 18:30:41 +00:00
< < handshake_response . max_packet_size
2020-01-19 00:34:00 +00:00
< < " , character_set: "
< < static_cast < int > ( handshake_response . character_set )
< < " , user: "
2019-03-26 18:30:41 +00:00
< < handshake_response . username
2020-01-19 00:34:00 +00:00
< < " , auth_response length: "
2019-03-26 18:30:41 +00:00
< < handshake_response . auth_response . length ( )
2020-01-19 00:34:00 +00:00
< < " , database: "
2019-03-26 18:30:41 +00:00
< < handshake_response . database
2020-01-19 00:34:00 +00:00
< < " , auth_plugin_name: "
< < 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 ) ;
connection_context . setCurrentQueryId ( " " ) ;
}
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. " ) ;
2019-07-19 18:29:39 +00:00
LOG_DEBUG ( log , " Received command: " < < static_cast < int > ( static_cast < unsigned char > ( command ) ) < < " . Connection id: " < < 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 ;
2019-05-16 17:15:43 +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.
auto user = connection_context . getUser ( 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 )
{
2019-07-28 13:12:26 +00:00
LOG_ERROR ( log , " Authentication for user " < < user_name < < " failed. " ) ;
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 ;
}
2019-07-28 13:12:26 +00:00
LOG_INFO ( log , " Authentication for user " < < user_name < < " succeeded. " ) ;
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 ) ;
2019-03-16 02:08:21 +00:00
LOG_DEBUG ( log , " Setting current database to " < < database ) ;
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 ( ) ;
StoragePtr tablePtr = connection_context . getTable ( database , packet . table ) ;
2019-03-16 02:08:21 +00:00
for ( const NameAndTypePair & column : tablePtr - > getColumns ( ) . getAll ( ) )
{
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
}
2019-12-02 23:18:19 +00:00
static bool isFederatedServerSetupCommand ( 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.
2019-11-28 14:43:52 +00:00
if ( isFederatedServerSetupCommand ( 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
{
bool with_output = false ;
2020-01-22 17:22:04 +00:00
std : : function < void ( const String & , const String & ) > set_content_type_and_format = [ & with_output ] ( const String & , const String & ) - > void
{
2019-11-11 14:31:12 +00:00
with_output = true ;
} ;
2019-07-16 07:11:59 +00:00
2019-11-26 15:39:36 +00:00
String replacement_query = " select '' " ;
2019-11-11 14:31:12 +00:00
bool should_replace = false ;
2019-07-14 08:22:55 +00:00
2019-11-11 14:31:12 +00:00
// Translate query from MySQL to ClickHouse.
// This is a temporary workaround until ClickHouse supports the syntax "@@var_name".
if ( query = = " select @@version_comment limit 1 " ) // MariaDB client starts session with that query
{
should_replace = true ;
}
// This is a workaround in order to support adding ClickHouse to MySQL using federated server.
if ( 0 = = strncasecmp ( " SHOW TABLE STATUS LIKE " , query . c_str ( ) , 22 ) )
{
should_replace = true ;
2019-11-29 13:37:13 +00:00
replacement_query = boost : : replace_all_copy ( query , " SHOW TABLE STATUS LIKE " , show_table_status_replacement_query ) ;
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-01-21 23:33:57 +00:00
executeQuery ( should_replace ? replacement : payload , * out , true , query_context , set_content_type_and_format , { } ) ;
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
}
# if USE_SSL && USE_POCO_NETSSL
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
2019-12-02 23:18:19 +00:00
static bool isFederatedServerSetupCommand ( const String & query )
2019-12-02 11:32:45 +00:00
{
return 0 = = strncasecmp ( " SET NAMES " , query . c_str ( ) , 9 ) | | 0 = = strncasecmp ( " SET character_set_results " , query . c_str ( ) , 25 )
| | 0 = = strncasecmp ( " SET FOREIGN_KEY_CHECKS " , query . c_str ( ) , 22 ) | | 0 = = strncasecmp ( " SET AUTOCOMMIT " , query . c_str ( ) , 14 )
| | 0 = = strncasecmp ( " SET SESSION TRANSACTION ISOLATION LEVEL " , query . c_str ( ) , 39 ) ;
}
const String MySQLHandler : : show_table_status_replacement_query ( " 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 "
2019-12-02 23:20:58 +00:00
" WHERE name LIKE " ) ;
2019-12-02 11:32:45 +00:00
2019-11-02 10:20:46 +00:00
}