944 lines
25 KiB
C++

/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of the
* License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
// Avoid warnings from includes of other project and protobuf
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#elif defined _MSC_VER
#pragma warning (push)
#pragma warning (disable : 4018 4996)
#endif
#include "ngs_common/protocol_protobuf.h"
#include "mysqlx_protocol.h"
#include "mysqlx_resultset.h"
#include "mysqlx_row.h"
#include "mysqlx_error.h"
#include "mysqlx_version.h"
#include "my_config.h"
#include "ngs_common/bind.h"
#ifdef MYSQLXTEST_STANDALONE
#include "mysqlx/auth_mysql41.h"
#else
#include "password_hasher.h"
namespace mysqlx {
std::string build_mysql41_authentication_response(const std::string &salt_data,
const std::string &user,
const std::string &password,
const std::string &schema)
{
std::string password_hash;
if (password.length())
password_hash = Password_hasher::get_password_from_salt(Password_hasher::scramble(salt_data.c_str(), password.c_str()));
std::string data;
data.append(schema).push_back('\0'); // authz
data.append(user).push_back('\0'); // authc
data.append(password_hash); // pass
return data;
}
}
#endif
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic pop
#elif defined _MSC_VER
#pragma warning (pop)
#endif
#include <iostream>
#ifndef WIN32
#include <netdb.h>
#include <sys/socket.h>
#endif // WIN32
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif // HAVE_SYS_UN_H
#include <string>
#include <iostream>
#include <limits>
#ifdef WIN32
# define snprintf _snprintf
# pragma push_macro("ERROR")
# undef ERROR
#endif
using namespace mysqlx;
bool mysqlx::parse_mysql_connstring(const std::string &connstring,
std::string &protocol, std::string &user, std::string &password,
std::string &host, int &port, std::string &sock,
std::string &db, int &pwd_found)
{
// format is [protocol://][user[:pass]]@host[:port][/db] or user[:pass]@::socket[/db], like what cmdline utilities use
pwd_found = 0;
std::string remaining = connstring;
std::string::size_type p;
p = remaining.find("://");
if (p != std::string::npos)
{
protocol = connstring.substr(0, p);
remaining = remaining.substr(p + 3);
}
std::string s = remaining;
p = remaining.find('/');
if (p != std::string::npos)
{
db = remaining.substr(p + 1);
s = remaining.substr(0, p);
}
p = s.rfind('@');
std::string user_part;
std::string server_part = (p == std::string::npos) ? s : s.substr(p + 1);
if (p == std::string::npos)
{
// by default, connect using the current OS username
#ifdef _WIN32
char tmp_buffer[1024];
char *tmp = tmp_buffer;
DWORD tmp_size = sizeof(tmp_buffer);
if (!GetUserNameA(tmp_buffer, &tmp_size))
{
tmp = NULL;
}
#else
const char *tmp = getenv("USER");
#endif
user_part = tmp ? tmp : "";
}
else
user_part = s.substr(0, p);
if ((p = user_part.find(':')) != std::string::npos)
{
user = user_part.substr(0, p);
password = user_part.substr(p + 1);
pwd_found = 1;
}
else
user = user_part;
p = server_part.find(':');
if (p != std::string::npos)
{
host = server_part.substr(0, p);
server_part = server_part.substr(p + 1);
p = server_part.find(':');
if (p != std::string::npos)
sock = server_part.substr(p + 1);
else
if (!sscanf(server_part.substr(0, p).c_str(), "%i", &port))
return false;
}
else
host = server_part;
return true;
}
static void throw_server_error(const Mysqlx::Error &error)
{
throw Error(error.code(), error.msg());
}
XProtocol::XProtocol(const Ssl_config &ssl_config,
const std::size_t timeout,
const bool dont_wait_for_disconnect,
const Internet_protocol ip_mode)
: m_sync_connection(ssl_config.key, ssl_config.ca, ssl_config.ca_path,
ssl_config.cert, ssl_config.cipher, ssl_config.tls_version, timeout),
m_client_id(0),
m_trace_packets(false), m_closed(true),
m_dont_wait_for_disconnect(dont_wait_for_disconnect),
m_ip_mode(ip_mode)
{
if (getenv("MYSQLX_TRACE_CONNECTION"))
m_trace_packets = true;
}
XProtocol::~XProtocol()
{
try
{
close();
}
catch (Error &)
{
// ignore close errors
}
}
void XProtocol::connect(const std::string &uri, const std::string &pass, const bool cap_expired_password)
{
std::string protocol, host, schema, user, password;
std::string sock;
int pwd_found = 0;
int port = MYSQLX_TCP_PORT;
if (!parse_mysql_connstring(uri, protocol, user, password, host, port, sock, schema, pwd_found))
throw Error(CR_WRONG_HOST_INFO, "Unable to parse connection string");
if (protocol != "mysqlx" && !protocol.empty())
throw Error(CR_WRONG_HOST_INFO, "Unsupported protocol "+protocol);
if (!pass.empty())
password = pass;
connect(host, port);
if (cap_expired_password)
setup_capability("client.pwd_expire_ok", true);
authenticate(user, pass.empty() ? password : pass, schema);
}
void XProtocol::connect(const std::string &host, int port)
{
struct addrinfo *res_lst, hints, *t_res;
int gai_errno;
Error error;
char port_buf[NI_MAXSERV];
snprintf(port_buf, NI_MAXSERV, "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype= SOCK_STREAM;
hints.ai_protocol= IPPROTO_TCP;
hints.ai_family= AF_UNSPEC;
if (IPv6 == m_ip_mode)
hints.ai_family = AF_INET6;
else if (IPv4 == m_ip_mode)
hints.ai_family = AF_INET;
gai_errno= getaddrinfo(host.c_str(), port_buf, &hints, &res_lst);
if (gai_errno != 0)
throw Error(CR_UNKNOWN_HOST, "No such host is known '" + host + "'");
for (t_res= res_lst; t_res; t_res= t_res->ai_next)
{
error = m_sync_connection.connect((sockaddr*)t_res->ai_addr, t_res->ai_addrlen);
if (!error)
break;
}
freeaddrinfo(res_lst);
if (error)
{
std::string error_description = error.what();
throw Error(CR_CONNECTION_ERROR, error_description + " connecting to " + host + ":" + port_buf);
}
m_closed = false;
}
void XProtocol::connect_to_localhost(const std::string &unix_socket_or_named_pipe)
{
Error error = m_sync_connection.connect_to_localhost(unix_socket_or_named_pipe);
if (error)
{
std::string error_description = error.what();
throw Error(CR_CONNECTION_ERROR, error_description + ", while connecting to "+unix_socket_or_named_pipe);
}
m_closed = false;
}
void XProtocol::authenticate(const std::string &user, const std::string &pass, const std::string &schema)
{
if (m_sync_connection.supports_ssl())
{
setup_capability("tls", true);
enable_tls();
authenticate_plain(user, pass, schema);
}
else
authenticate_mysql41(user, pass, schema);
}
void XProtocol::fetch_capabilities()
{
send(Mysqlx::Connection::CapabilitiesGet());
int mid;
ngs::unique_ptr<Message> message(recv_raw(mid));
if (mid != Mysqlx::ServerMessages::CONN_CAPABILITIES)
throw Error(CR_COMMANDS_OUT_OF_SYNC, "Unexpected response received from server");
m_capabilities = *static_cast<Mysqlx::Connection::Capabilities*>(message.get());
}
void XProtocol::enable_tls()
{
Error ec = m_sync_connection.activate_tls();
if (ec)
{
// If ssl activation failed then
// server and client are in different states
// lets force disconnect
set_closed();
throw ec;
}
}
void XProtocol::set_closed()
{
m_closed = true;
}
void XProtocol::close()
{
if (!m_closed)
{
if (m_last_result)
m_last_result->buffer();
send(Mysqlx::Session::Close());
m_closed = true;
int mid;
try
{
ngs::unique_ptr<Message> message(recv_raw(mid));
if (mid != Mysqlx::ServerMessages::OK)
throw Error(CR_COMMANDS_OUT_OF_SYNC, "Unexpected message received in response to Session.Close");
perform_close();
}
catch (...)
{
m_sync_connection.close();
throw;
}
}
}
unsigned long XProtocol::get_received_msg_counter(const std::string &id) const
{
std::map<std::string, unsigned long>::const_iterator i =
m_received_msg_counters.find(id);
return i == m_received_msg_counters.end() ? 0ul : i->second;
}
void XProtocol::perform_close()
{
if (m_dont_wait_for_disconnect)
{
m_sync_connection.close();
return;
}
int mid;
ngs::unique_ptr<Message> message(recv_raw(mid));
std::stringstream s;
s << "Unexpected message received with id:" << mid << " while waiting for disconnection";
throw Error(CR_COMMANDS_OUT_OF_SYNC, s.str());
}
ngs::shared_ptr<Result> XProtocol::recv_result()
{
return new_result(true);
}
ngs::shared_ptr<Result> XProtocol::new_empty_result()
{
ngs::shared_ptr<Result> empty_result(new Result(shared_from_this(), false, false));
return empty_result;
}
ngs::shared_ptr<Result> XProtocol::execute_sql(const std::string &sql)
{
{
Mysqlx::Sql::StmtExecute exec;
exec.set_namespace_("sql");
exec.set_stmt(sql);
send(exec);
}
return new_result(true);
}
ngs::shared_ptr<Result> XProtocol::execute_stmt(const std::string &ns, const std::string &sql, const std::vector<ArgumentValue> &args)
{
{
Mysqlx::Sql::StmtExecute exec;
exec.set_namespace_(ns);
exec.set_stmt(sql);
for (std::vector<ArgumentValue>::const_iterator iter = args.begin();
iter != args.end(); ++iter)
{
Mysqlx::Datatypes::Any *any = exec.mutable_args()->Add();
any->set_type(Mysqlx::Datatypes::Any::SCALAR);
switch (iter->type())
{
case ArgumentValue::TInteger:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_SINT);
any->mutable_scalar()->set_v_signed_int(*iter);
break;
case ArgumentValue::TUInteger:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_UINT);
any->mutable_scalar()->set_v_unsigned_int(*iter);
break;
case ArgumentValue::TNull:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_NULL);
break;
case ArgumentValue::TDouble:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_DOUBLE);
any->mutable_scalar()->set_v_double(*iter);
break;
case ArgumentValue::TFloat:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_FLOAT);
any->mutable_scalar()->set_v_float(*iter);
break;
case ArgumentValue::TBool:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_BOOL);
any->mutable_scalar()->set_v_bool(*iter);
break;
case ArgumentValue::TString:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_STRING);
any->mutable_scalar()->mutable_v_string()->set_value(*iter);
break;
case ArgumentValue::TOctets:
any->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_OCTETS);
any->mutable_scalar()->mutable_v_octets()->set_value(*iter);
break;
}
}
send(exec);
}
return new_result(true);
}
ngs::shared_ptr<Result> XProtocol::execute_find(const Mysqlx::Crud::Find &m)
{
send(m);
return new_result(true);
}
ngs::shared_ptr<Result> XProtocol::execute_update(const Mysqlx::Crud::Update &m)
{
send(m);
return new_result(false);
}
ngs::shared_ptr<Result> XProtocol::execute_insert(const Mysqlx::Crud::Insert &m)
{
send(m);
return new_result(false);
}
ngs::shared_ptr<Result> XProtocol::execute_delete(const Mysqlx::Crud::Delete &m)
{
send(m);
return new_result(false);
}
void XProtocol::setup_capability(const std::string &name, const bool value)
{
Mysqlx::Connection::CapabilitiesSet capSet;
Mysqlx::Connection::Capability *cap = capSet.mutable_capabilities()->add_capabilities();
::Mysqlx::Datatypes::Scalar *scalar = cap->mutable_value()->mutable_scalar();
cap->set_name(name);
cap->mutable_value()->set_type(Mysqlx::Datatypes::Any_Type_SCALAR);
scalar->set_type(Mysqlx::Datatypes::Scalar_Type_V_BOOL);
scalar->set_v_bool(value);
send(capSet);
if (m_last_result)
m_last_result->buffer();
int mid;
ngs::unique_ptr<Message> msg(recv_raw(mid));
if (Mysqlx::ServerMessages::ERROR == mid)
throw_server_error(*(Mysqlx::Error*)msg.get());
if (Mysqlx::ServerMessages::OK != mid)
{
if (getenv("MYSQLX_DEBUG"))
{
std::string out;
google::protobuf::TextFormat::PrintToString(*msg, &out);
std::cout << out << "\n";
}
throw Error(CR_MALFORMED_PACKET, "Unexpected message received from server during handshake");
}
}
void XProtocol::authenticate_mysql41(const std::string &user, const std::string &pass, const std::string &db)
{
{
Mysqlx::Session::AuthenticateStart auth;
auth.set_mech_name("MYSQL41");
send(Mysqlx::ClientMessages::SESS_AUTHENTICATE_START, auth);
}
{
int mid;
ngs::unique_ptr<Message> message(recv_raw(mid));
switch (mid)
{
case Mysqlx::ServerMessages::SESS_AUTHENTICATE_CONTINUE:
{
Mysqlx::Session::AuthenticateContinue &auth_continue = *static_cast<Mysqlx::Session::AuthenticateContinue*>(message.get());
std::string data;
if (!auth_continue.has_auth_data())
throw Error(CR_MALFORMED_PACKET, "Missing authentication data");
std::string password_hash;
Mysqlx::Session::AuthenticateContinue auth_continue_response;
#ifdef MYSQLXTEST_STANDALONE
auth_continue_response.set_auth_data(build_mysql41_authentication_response(auth_continue.auth_data(), user, pass, db));
#else
if (pass.length())
{
password_hash = Password_hasher::scramble(auth_continue.auth_data().c_str(), pass.c_str());
password_hash = Password_hasher::get_password_from_salt(password_hash);
}
data.append(db).push_back('\0'); // authz
data.append(user).push_back('\0'); // authc
data.append(password_hash); // pass
auth_continue_response.set_auth_data(data);
#endif
send(Mysqlx::ClientMessages::SESS_AUTHENTICATE_CONTINUE, auth_continue_response);
}
break;
case Mysqlx::ServerMessages::NOTICE:
dispatch_notice(static_cast<Mysqlx::Notice::Frame*>(message.get()));
break;
case Mysqlx::ServerMessages::ERROR:
throw_server_error(*static_cast<Mysqlx::Error*>(message.get()));
break;
default:
throw Error(CR_MALFORMED_PACKET, "Unexpected message received from server during authentication");
break;
}
}
bool done = false;
while (!done)
{
int mid;
ngs::unique_ptr<Message> message(recv_raw(mid));
switch (mid)
{
case Mysqlx::ServerMessages::SESS_AUTHENTICATE_OK:
done = true;
break;
case Mysqlx::ServerMessages::ERROR:
throw_server_error(*static_cast<Mysqlx::Error*>(message.get()));
break;
case Mysqlx::ServerMessages::NOTICE:
dispatch_notice(static_cast<Mysqlx::Notice::Frame*>(message.get()));
break;
default:
throw Error(CR_MALFORMED_PACKET, "Unexpected message received from server during authentication");
break;
}
}
}
void XProtocol::authenticate_plain(const std::string &user, const std::string &pass, const std::string &db)
{
{
Mysqlx::Session::AuthenticateStart auth;
auth.set_mech_name("PLAIN");
std::string data;
data.append(db).push_back('\0'); // authz
data.append(user).push_back('\0'); // authc
data.append(pass); // pass
auth.set_auth_data(data);
send(Mysqlx::ClientMessages::SESS_AUTHENTICATE_START, auth);
}
bool done = false;
while (!done)
{
int mid;
ngs::unique_ptr<Message> message(recv_raw(mid));
switch (mid)
{
case Mysqlx::ServerMessages::SESS_AUTHENTICATE_OK:
done = true;
break;
case Mysqlx::ServerMessages::ERROR:
throw_server_error(*static_cast<Mysqlx::Error*>(message.get()));
break;
case Mysqlx::ServerMessages::NOTICE:
dispatch_notice(static_cast<Mysqlx::Notice::Frame*>(message.get()));
break;
default:
throw Error(CR_MALFORMED_PACKET, "Unexpected message received from server during authentication");
break;
}
}
}
void XProtocol::send_bytes(const std::string &data)
{
Error error = m_sync_connection.write(data.data(), data.size());
throw_mysqlx_error(error);
}
void XProtocol::send(int mid, const Message &msg)
{
Error error;
union
{
uint8_t buf[5]; // Must be properly aligned
longlong dummy;
};
/*
Use dummy, otherwise g++ 4.4 reports: unused variable 'dummy'
MY_ATTRIBUTE((unused)) did not work, so we must use it.
*/
dummy= 0;
uint32_t *buf_ptr = (uint32_t *)buf;
*buf_ptr = msg.ByteSize() + 1;
#ifdef WORDS_BIGENDIAN
std::swap(buf[0], buf[3]);
std::swap(buf[1], buf[2]);
#endif
buf[4] = mid;
if (m_trace_packets)
{
std::string out;
google::protobuf::TextFormat::Printer p;
p.SetInitialIndentLevel(1);
p.PrintToString(msg, &out);
std::cout << ">>>> SEND " << msg.ByteSize()+1 << " " << msg.GetDescriptor()->full_name() << " {\n" << out << "}\n";
}
error = m_sync_connection.write(buf, 5);
if (!error)
{
std::string mbuf;
msg.SerializeToString(&mbuf);
if (0 != mbuf.length())
error = m_sync_connection.write(mbuf.data(), mbuf.length());
}
throw_mysqlx_error(error);
}
void XProtocol::push_local_notice_handler(Local_notice_handler handler)
{
m_local_notice_handlers.push_back(handler);
}
void XProtocol::pop_local_notice_handler()
{
m_local_notice_handlers.pop_back();
}
void XProtocol::dispatch_notice(Mysqlx::Notice::Frame *frame)
{
if (frame->scope() == Mysqlx::Notice::Frame::LOCAL)
{
for (std::list<Local_notice_handler>::iterator iter = m_local_notice_handlers.begin();
iter != m_local_notice_handlers.end(); ++iter)
if ((*iter)(frame->type(), frame->payload())) // handler returns true if the notice was handled
return;
{
if (frame->type() == 3)
{
Mysqlx::Notice::SessionStateChanged change;
change.ParseFromString(frame->payload());
if (!change.IsInitialized())
std::cerr << "Invalid notice received from server " << change.InitializationErrorString() << "\n";
else
{
if (change.param() == Mysqlx::Notice::SessionStateChanged::ACCOUNT_EXPIRED)
{
std::cout << "NOTICE: Account password expired\n";
return;
}
else if (change.param() == Mysqlx::Notice::SessionStateChanged::CLIENT_ID_ASSIGNED)
{
if (!change.has_value() || change.value().type() != Mysqlx::Datatypes::Scalar::V_UINT)
std::cerr << "Invalid notice received from server. Client_id is of the wrong type\n";
else
m_client_id = change.value().v_unsigned_int();
return;
}
}
}
std::cout << "Unhandled local notice\n";
}
}
else
{
std::cout << "Unhandled global notice\n";
}
}
Message *XProtocol::recv_next(int &mid)
{
for (;;)
{
Message *msg = recv_raw(mid);
if (mid != Mysqlx::ServerMessages::NOTICE)
return msg;
dispatch_notice(static_cast<Mysqlx::Notice::Frame*>(msg));
delete msg;
}
}
Message *XProtocol::recv_raw_with_deadline(int &mid, const int deadline_milliseconds)
{
char header_buffer[5];
std::size_t data = sizeof(header_buffer);
Error error = m_sync_connection.read_with_timeout(header_buffer, data, deadline_milliseconds);
if (0 == data)
{
m_closed = true;
return NULL;
}
throw_mysqlx_error(error);
return recv_message_with_header(mid, header_buffer, sizeof(header_buffer));
}
Message *XProtocol::recv_payload(const int mid, const std::size_t msglen)
{
Error error;
Message* ret_val = NULL;
char *mbuf = new char[msglen];
if (0 < msglen)
error = m_sync_connection.read(mbuf, msglen);
if (!error)
{
switch (mid)
{
case Mysqlx::ServerMessages::OK:
ret_val = new Mysqlx::Ok();
break;
case Mysqlx::ServerMessages::ERROR:
ret_val = new Mysqlx::Error();
break;
case Mysqlx::ServerMessages::NOTICE:
ret_val = new Mysqlx::Notice::Frame();
break;
case Mysqlx::ServerMessages::CONN_CAPABILITIES:
ret_val = new Mysqlx::Connection::Capabilities();
break;
case Mysqlx::ServerMessages::SESS_AUTHENTICATE_CONTINUE:
ret_val = new Mysqlx::Session::AuthenticateContinue();
break;
case Mysqlx::ServerMessages::SESS_AUTHENTICATE_OK:
ret_val = new Mysqlx::Session::AuthenticateOk();
break;
case Mysqlx::ServerMessages::RESULTSET_COLUMN_META_DATA:
ret_val = new Mysqlx::Resultset::ColumnMetaData();
break;
case Mysqlx::ServerMessages::RESULTSET_ROW:
ret_val = new Mysqlx::Resultset::Row();
break;
case Mysqlx::ServerMessages::RESULTSET_FETCH_DONE:
ret_val = new Mysqlx::Resultset::FetchDone();
break;
case Mysqlx::ServerMessages::RESULTSET_FETCH_DONE_MORE_RESULTSETS:
ret_val = new Mysqlx::Resultset::FetchDoneMoreResultsets();
break;
case Mysqlx::ServerMessages::SQL_STMT_EXECUTE_OK:
ret_val = new Mysqlx::Sql::StmtExecuteOk();
break;
}
if (!ret_val)
{
delete[] mbuf;
std::stringstream ss;
ss << "Unknown message received from server ";
ss << mid;
throw Error(CR_MALFORMED_PACKET, ss.str());
}
// Parses the received message
ret_val->ParseFromString(std::string(mbuf, msglen));
if (m_trace_packets)
{
std::string out;
google::protobuf::TextFormat::Printer p;
p.SetInitialIndentLevel(1);
p.PrintToString(*ret_val, &out);
std::cout << "<<<< RECEIVE " << msglen << " " << ret_val->GetDescriptor()->full_name() << " {\n" << out << "}\n";
}
if (!ret_val->IsInitialized())
{
std::string err("Message is not properly initialized: ");
err += ret_val->InitializationErrorString();
delete[] mbuf;
delete ret_val;
throw Error(CR_MALFORMED_PACKET, err);
}
}
else
{
delete[] mbuf;
throw_mysqlx_error(error);
}
delete[] mbuf;
update_received_msg_counter(ret_val);
return ret_val;
}
Message *XProtocol::recv_raw(int &mid)
{
union
{
char buf[5]; // Must be properly aligned
longlong dummy;
};
/*
Use dummy, otherwise g++ 4.4 reports: unused variable 'dummy'
MY_ATTRIBUTE((unused)) did not work, so we must use it.
*/
dummy= 0;
mid = 0;
return recv_message_with_header(mid, buf, 0);
}
Message *XProtocol::recv_message_with_header(int &mid, char (&header_buffer)[5], const std::size_t header_offset)
{
Message* ret_val = NULL;
Error error;
error = m_sync_connection.read(header_buffer + header_offset, 5 - header_offset);
#ifdef WORDS_BIGENDIAN
std::swap(header_buffer[0], header_buffer[3]);
std::swap(header_buffer[1], header_buffer[2]);
#endif
if (!error)
{
uint32_t msglen = *(uint32_t*)header_buffer - 1;
mid = header_buffer[4];
ret_val = recv_payload(mid, msglen);
}
else
{
throw_mysqlx_error(error);
}
return ret_val;
}
void XProtocol::throw_mysqlx_error(const Error &error)
{
if (!error)
return;
throw error;
}
ngs::shared_ptr<Result> XProtocol::new_result(bool expect_data)
{
if (m_last_result)
m_last_result->buffer();
m_last_result.reset(new Result(shared_from_this(), expect_data));
return m_last_result;
}
void XProtocol::update_received_msg_counter(const Message* msg)
{
const std::string &id = msg->GetDescriptor()->full_name();
++m_received_msg_counters[id];
if (id != Mysqlx::Notice::Frame::descriptor()->full_name()) return;
static const std::string *notice_type_id[] = {
&Mysqlx::Notice::Warning::descriptor()->full_name(),
&Mysqlx::Notice::SessionVariableChanged::descriptor()->full_name(),
&Mysqlx::Notice::SessionStateChanged::descriptor()->full_name()};
static const unsigned notice_type_id_size =
sizeof(notice_type_id) / sizeof(notice_type_id[0]);
const ::google::protobuf::uint32 notice_type =
static_cast<const Mysqlx::Notice::Frame *>(msg)->type() - 1u;
if (notice_type < notice_type_id_size)
++m_received_msg_counters[*notice_type_id[notice_type]];
}
#ifdef WIN32
# pragma pop_macro("ERROR")
#endif