217 lines
5.1 KiB
C++
217 lines
5.1 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: 2024-11-16
|
|
*
|
|
* 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 <maxsql/mariadb.hh>
|
|
#include <string.h>
|
|
#include <errmsg.h>
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <maxbase/alloc.h>
|
|
#include <maxbase/assert.h>
|
|
#include <maxbase/format.hh>
|
|
|
|
using std::string;
|
|
|
|
namespace
|
|
{
|
|
struct THIS_UNIT
|
|
{
|
|
bool log_statements; // Should all statements sent to server be logged?
|
|
};
|
|
|
|
static THIS_UNIT this_unit =
|
|
{
|
|
false
|
|
};
|
|
}
|
|
|
|
namespace maxsql
|
|
{
|
|
|
|
int mysql_query_ex(MYSQL* conn, const std::string& query, int query_retries, time_t query_retry_timeout)
|
|
{
|
|
const char* query_cstr = query.c_str();
|
|
time_t start = time(NULL);
|
|
int rc = mysql_query(conn, query_cstr);
|
|
|
|
for (int n = 0; rc != 0 && n < query_retries && mysql_is_net_error(mysql_errno(conn))
|
|
&& time(NULL) - start < query_retry_timeout; n++)
|
|
{
|
|
if (n > 0)
|
|
{
|
|
// The first reconnection didn't work, wait for one second before attempting again. This
|
|
// should reduce the likelihood of transient problems causing state changes due to too many
|
|
// reconnection attemps in a short period of time.
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
|
|
rc = mysql_query(conn, query_cstr);
|
|
}
|
|
|
|
if (this_unit.log_statements)
|
|
{
|
|
const char* host = "0.0.0.0";
|
|
unsigned int port = 0;
|
|
MXB_AT_DEBUG(int rc1 = ) mariadb_get_info(conn, MARIADB_CONNECTION_HOST, &host);
|
|
MXB_AT_DEBUG(int rc2 = ) mariadb_get_info(conn, MARIADB_CONNECTION_PORT, &port);
|
|
mxb_assert(!rc1 && !rc2);
|
|
MXB_NOTICE("SQL([%s]:%u): %d, \"%s\"", host, port, rc, query_cstr);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool mysql_is_net_error(unsigned int errcode)
|
|
{
|
|
switch (errcode)
|
|
{
|
|
case CR_SOCKET_CREATE_ERROR:
|
|
case CR_CONNECTION_ERROR:
|
|
case CR_CONN_HOST_ERROR:
|
|
case CR_IPSOCK_ERROR:
|
|
case CR_SERVER_GONE_ERROR:
|
|
case CR_TCP_CONNECTION:
|
|
case CR_SERVER_LOST:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void mysql_set_log_statements(bool enable)
|
|
{
|
|
this_unit.log_statements = enable;
|
|
}
|
|
|
|
bool mysql_get_log_statements()
|
|
{
|
|
return this_unit.log_statements;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate the length of a length-encoded integer in bytes
|
|
*
|
|
* @param ptr Start of the length encoded value
|
|
* @return Number of bytes before the actual value
|
|
*/
|
|
size_t leint_bytes(const uint8_t* ptr)
|
|
{
|
|
uint8_t val = *ptr;
|
|
if (val < 0xfb)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (val == 0xfc)
|
|
{
|
|
return 3;
|
|
}
|
|
else if (val == 0xfd)
|
|
{
|
|
return 4;
|
|
}
|
|
else
|
|
{
|
|
return 9;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Converts a length-encoded integer to @c uint64_t
|
|
*
|
|
* @see https://dev.mysql.com/doc/internals/en/integer.html
|
|
* @param c Pointer to the first byte of a length-encoded integer
|
|
* @return The value converted to a standard unsigned integer
|
|
*/
|
|
uint64_t leint_value(const uint8_t* c)
|
|
{
|
|
uint64_t sz = 0;
|
|
if (*c < 0xfb)
|
|
{
|
|
sz = *c;
|
|
}
|
|
else if (*c == 0xfc)
|
|
{
|
|
memcpy(&sz, c + 1, 2);
|
|
}
|
|
else if (*c == 0xfd)
|
|
{
|
|
memcpy(&sz, c + 1, 3);
|
|
}
|
|
else if (*c == 0xfe)
|
|
{
|
|
memcpy(&sz, c + 1, 8);
|
|
}
|
|
else
|
|
{
|
|
mxb_assert(*c == 0xff);
|
|
MXB_ERROR("Unexpected length encoding '%x' encountered when reading length-encoded integer.", *c);
|
|
}
|
|
return sz;
|
|
}
|
|
|
|
/**
|
|
* Converts a length-encoded integer into a standard unsigned integer
|
|
* and advances the pointer to the next unrelated byte.
|
|
*
|
|
* @param c Pointer to the first byte of a length-encoded integer
|
|
*/
|
|
uint64_t leint_consume(uint8_t** c)
|
|
{
|
|
uint64_t rval = leint_value(*c);
|
|
*c += leint_bytes(*c);
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* @brief Consume and duplicate a length-encoded string
|
|
*
|
|
* Converts a length-encoded string to a C string and advances the pointer to
|
|
* the first byte after the string. The caller is responsible for freeing
|
|
* the returned string.
|
|
* @param c Pointer to the first byte of a valid packet.
|
|
* @return The newly allocated string or NULL if memory allocation failed
|
|
*/
|
|
char* lestr_consume_dup(uint8_t** c)
|
|
{
|
|
uint64_t slen = leint_consume(c);
|
|
char* str = (char*)MXS_MALLOC((slen + 1) * sizeof(char));
|
|
|
|
if (str)
|
|
{
|
|
memcpy(str, *c, slen);
|
|
str[slen] = '\0';
|
|
*c += slen;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* @brief Consume a length-encoded string
|
|
*
|
|
* Converts length-encoded strings to character strings and advanced
|
|
* the pointer to the next unrelated byte.
|
|
* @param c Pointer to the start of the length-encoded string
|
|
* @param size Pointer to a variable where the size of the string is stored
|
|
* @return Pointer to the start of the string
|
|
*/
|
|
char* lestr_consume(uint8_t** c, size_t* size)
|
|
{
|
|
uint64_t slen = leint_consume(c);
|
|
*size = slen;
|
|
char* start = (char*) *c;
|
|
*c += slen;
|
|
return start;
|
|
}
|
|
}
|