fix(db): MariaDB 10.6+ server and connector compatibility
- Parse real MariaDB version after MySQL 5.5.5 compatibility prefix for DatabaseIncompatibleVersion checks. - Relax client checks when building against MariaDB C connector (MARIADB_VERSION_ID); keep Oracle MySQL 8.0+ rules otherwise. - Use mysql_stmt_bind_param on MariaDB; mysql_stmt_bind_named_param only for MySQL 8.3+ without MariaDB headers. - SSL: MYSQL_OPT_SSL_ENFORCE on MariaDB connector, MYSQL_OPT_SSL_MODE elsewhere. - Track custom_script_loader.cpp so static script link succeeds; CMake find_package(MySQL) requires COMPONENTS lib on CMake 3.16.
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
/data/sql/custom/*
|
/data/sql/custom/*
|
||||||
/src/server/scripts/Custom/*
|
/src/server/scripts/Custom/*
|
||||||
!/src/server/scripts/Custom/README.md
|
!/src/server/scripts/Custom/README.md
|
||||||
|
!/src/server/scripts/Custom/custom_script_loader.cpp
|
||||||
|
|
||||||
/*.override.yml
|
/*.override.yml
|
||||||
/*.override.yaml
|
/*.override.yaml
|
||||||
|
|||||||
+1
-1
@@ -106,7 +106,7 @@ include(ConfigInstall)
|
|||||||
CU_RUN_HOOK("AFTER_LOAD_CMAKE_MODULES")
|
CU_RUN_HOOK("AFTER_LOAD_CMAKE_MODULES")
|
||||||
|
|
||||||
find_package(PCHSupport)
|
find_package(PCHSupport)
|
||||||
find_package(MySQL REQUIRED)
|
find_package(MySQL REQUIRED COMPONENTS lib)
|
||||||
|
|
||||||
if(UNIX AND WITH_PERFTOOLS)
|
if(UNIX AND WITH_PERFTOOLS)
|
||||||
find_package(Gperftools)
|
find_package(Gperftools)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "SQLOperation.h"
|
#include "SQLOperation.h"
|
||||||
#include "Transaction.h"
|
#include "Transaction.h"
|
||||||
#include "WorldDatabase.h"
|
#include "WorldDatabase.h"
|
||||||
|
#include <array>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <mysqld_error.h>
|
#include <mysqld_error.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -59,10 +60,18 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
|||||||
{
|
{
|
||||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||||
|
|
||||||
bool isSupportClientDB = mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION;
|
bool isSupportClientDB;
|
||||||
bool isSameClientDB = mysql_get_client_version() == MYSQL_VERSION_ID;
|
bool isSameClientDB;
|
||||||
|
#ifdef MARIADB_VERSION_ID
|
||||||
|
isSupportClientDB = (MARIADB_VERSION_ID >= MIN_MARIADB_CLIENT_VERSION_ID);
|
||||||
|
// MariaDB packages often differ between build host and runtime; strict id match is too brittle.
|
||||||
|
isSameClientDB = true;
|
||||||
|
#else
|
||||||
|
isSupportClientDB = mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION;
|
||||||
|
isSameClientDB = mysql_get_client_version() == MYSQL_VERSION_ID;
|
||||||
|
#endif
|
||||||
|
|
||||||
WPFatal(isSupportClientDB, "AzerothCore does not support MySQL versions below 8.0\n\nFound version: {} / {}. Server compiled with: {}.\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00043).",
|
WPFatal(isSupportClientDB, "AzerothCore does not support this database client library.\n\nFound version: {} / {}. Server compiled with: {}.\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00043).",
|
||||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||||
WPFatal(isSameClientDB, "Used MySQL library version ({} id {}) does not match the version id used to compile AzerothCore (id {}).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00046).",
|
WPFatal(isSameClientDB, "Used MySQL library version ({} id {}) does not match the version id used to compile AzerothCore (id {}).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00046).",
|
||||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||||
@@ -386,32 +395,60 @@ void DatabaseWorkerPool<T>::KeepAlive()
|
|||||||
*/
|
*/
|
||||||
bool DatabaseIncompatibleVersion(std::string const mysqlVersion)
|
bool DatabaseIncompatibleVersion(std::string const mysqlVersion)
|
||||||
{
|
{
|
||||||
// anon func to turn a version string into an array of uint8
|
auto parseTriplet = [](std::string const& input) -> std::array<unsigned, 3>
|
||||||
// "1.2.3" => [1, 2, 3]
|
|
||||||
auto parse = [](std::string const& input)
|
|
||||||
{
|
{
|
||||||
std::vector<uint8> result;
|
std::array<unsigned, 3> v = { 0, 0, 0 };
|
||||||
std::istringstream parser(input);
|
size_t idx = 0;
|
||||||
result.push_back(parser.get());
|
std::string num;
|
||||||
for (int i = 1; i < 3; i++)
|
for (char ch : input)
|
||||||
{
|
{
|
||||||
// Skip period
|
if (ch >= '0' && ch <= '9')
|
||||||
parser.get();
|
num.push_back(ch);
|
||||||
// Append int from parser to output
|
else if (ch == '.')
|
||||||
result.push_back(parser.get());
|
{
|
||||||
|
if (!num.empty() && idx < 3)
|
||||||
|
{
|
||||||
|
v[idx++] = static_cast<unsigned>(std::stoul(num));
|
||||||
|
num.clear();
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!num.empty() && idx < 3)
|
||||||
|
v[idx] = static_cast<unsigned>(std::stoul(num));
|
||||||
|
return v;
|
||||||
};
|
};
|
||||||
|
|
||||||
// default to values for MySQL
|
auto compareVersion = [](std::array<unsigned, 3> const& a, std::array<unsigned, 3> const& b) -> int
|
||||||
uint8 offset = 0;
|
{
|
||||||
|
if (a[0] != b[0])
|
||||||
|
return (a[0] < b[0]) ? -1 : 1;
|
||||||
|
if (a[1] != b[1])
|
||||||
|
return (a[1] < b[1]) ? -1 : 1;
|
||||||
|
if (a[2] != b[2])
|
||||||
|
return (a[2] < b[2]) ? -1 : 1;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string ver = mysqlVersion;
|
||||||
std::string minVersion = MIN_MYSQL_SERVER_VERSION;
|
std::string minVersion = MIN_MYSQL_SERVER_VERSION;
|
||||||
|
|
||||||
auto parsedMySQLVersion = parse(mysqlVersion.substr(offset));
|
// MariaDB: mysql_get_server_info() often starts with MySQL-compat "5.5.5-" then real version, e.g.
|
||||||
auto parsedMinVersion = parse(minVersion);
|
// "5.5.5-10.6.11-MariaDB-1:10.6.11+maria~ubu2004"
|
||||||
|
if (ver.find("MariaDB") != std::string::npos)
|
||||||
|
{
|
||||||
|
size_t const firstDash = ver.find('-');
|
||||||
|
if (firstDash != std::string::npos)
|
||||||
|
{
|
||||||
|
size_t const secondDash = ver.find('-', firstDash + 1);
|
||||||
|
if (secondDash != std::string::npos)
|
||||||
|
ver = ver.substr(firstDash + 1, secondDash - firstDash - 1);
|
||||||
|
}
|
||||||
|
minVersion = MIN_MARIADB_SERVER_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
return std::lexicographical_compare(parsedMySQLVersion.begin(), parsedMySQLVersion.end(),
|
return compareVersion(parseTriplet(ver), parseTriplet(minVersion)) < 0;
|
||||||
parsedMinVersion.begin(), parsedMinVersion.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@@ -442,7 +479,7 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne
|
|||||||
}
|
}
|
||||||
else if (DatabaseIncompatibleVersion(connection->GetServerInfo()))
|
else if (DatabaseIncompatibleVersion(connection->GetServerInfo()))
|
||||||
{
|
{
|
||||||
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 8.0\n\nFound server version: {}. Server compiled with: {}.",
|
LOG_ERROR("sql.driver", "Database server version is too old.\n\nFound server version: {}. Client library compile id: {}.",
|
||||||
connection->GetServerInfo(), MYSQL_VERSION_ID);
|
connection->GetServerInfo(), MYSQL_VERSION_ID);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,24 @@
|
|||||||
*/
|
*/
|
||||||
#define MIN_MYSQL_CLIENT_VERSION 80000u
|
#define MIN_MYSQL_CLIENT_VERSION 80000u
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MIN_MARIADB_CLIENT_VERSION_ID
|
||||||
|
* Minimum MariaDB connector MARIADB_VERSION_ID (10.4.0 => 100400)
|
||||||
|
*/
|
||||||
|
#define MIN_MARIADB_CLIENT_VERSION_ID 100400u
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MIN_MYSQL_SERVER_VERSION
|
* @def MIN_MYSQL_SERVER_VERSION
|
||||||
* The minimum MySQL Server Version
|
* The minimum MySQL Server Version
|
||||||
*/
|
*/
|
||||||
#define MIN_MYSQL_SERVER_VERSION "8.0.0"
|
#define MIN_MYSQL_SERVER_VERSION "8.0.0"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MIN_MARIADB_SERVER_VERSION
|
||||||
|
* Minimum MariaDB server (version triplet after the 5.5.5- compatibility prefix)
|
||||||
|
*/
|
||||||
|
#define MIN_MARIADB_SERVER_VERSION "10.4.0"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ProducerConsumerQueue;
|
class ProducerConsumerQueue;
|
||||||
|
|
||||||
|
|||||||
@@ -129,13 +129,17 @@ uint32 MySQLConnection::Open()
|
|||||||
|
|
||||||
if (m_connectionInfo.ssl != "")
|
if (m_connectionInfo.ssl != "")
|
||||||
{
|
{
|
||||||
|
#ifdef MARIADB_VERSION_ID
|
||||||
|
my_bool const ssl_enforce = (m_connectionInfo.ssl == "ssl") ? 1 : 0;
|
||||||
|
if (ssl_enforce)
|
||||||
|
mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, &ssl_enforce);
|
||||||
|
#else
|
||||||
mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
|
mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
|
||||||
if (m_connectionInfo.ssl == "ssl")
|
if (m_connectionInfo.ssl == "ssl")
|
||||||
{
|
|
||||||
opt_use_ssl = SSL_MODE_REQUIRED;
|
opt_use_ssl = SSL_MODE_REQUIRED;
|
||||||
}
|
|
||||||
|
|
||||||
mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
|
mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
|
m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
|
||||||
@@ -216,7 +220,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
|
|||||||
|
|
||||||
uint32 _s = getMSTime();
|
uint32 _s = getMSTime();
|
||||||
|
|
||||||
#if MYSQL_VERSION_ID >= 80300
|
#if MYSQL_VERSION_ID >= 80300 && !defined(MARIADB_VERSION_ID)
|
||||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||||
#else
|
#else
|
||||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||||
@@ -268,7 +272,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
|
|||||||
|
|
||||||
uint32 _s = getMSTime();
|
uint32 _s = getMSTime();
|
||||||
|
|
||||||
#if MYSQL_VERSION_ID >= 80300
|
#if MYSQL_VERSION_ID >= 80300 && !defined(MARIADB_VERSION_ID)
|
||||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||||
#else
|
#else
|
||||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is where scripts' loading functions should be declared:
|
||||||
|
// void MyExampleScript()
|
||||||
|
|
||||||
|
// The name of this function should match:
|
||||||
|
// void Add${NameOfDirectory}Scripts()
|
||||||
|
void AddCustomScripts()
|
||||||
|
{
|
||||||
|
// MyExampleScript()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user