/* * 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: 2025-10-29 * * 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 #include #include #include #include #include #include #include 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; } }