278 lines
7.2 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "MariaDbClient.h"
#include <QtGlobal>
#ifdef Q_OS_WIN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
namespace {
void* LoadNativeLibrary(const char* name)
{
#ifdef Q_OS_WIN
return static_cast<void*>(LoadLibraryA(name));
#else
return dlopen(name, RTLD_NOW | RTLD_GLOBAL);
#endif
}
void CloseNativeLibrary(void* handle)
{
if (!handle) {
return;
}
#ifdef Q_OS_WIN
FreeLibrary(static_cast<HMODULE>(handle));
#else
dlclose(handle);
#endif
}
void* ResolveSymbol(void* handle, const char* name)
{
#ifdef Q_OS_WIN
return static_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name));
#else
return dlsym(handle, name);
#endif
}
} // namespace
MariaDbClient::MariaDbClient()
{
LoadClientLibrary();
}
MariaDbClient::~MariaDbClient()
{
Close();
if (m_library) {
CloseNativeLibrary(m_library);
m_library = nullptr;
}
}
bool MariaDbClient::IsLibraryAvailable() const
{
return m_library != nullptr && m_init != nullptr && m_realConnect != nullptr;
}
bool MariaDbClient::IsConnected() const
{
return m_connected && m_mysql != nullptr;
}
QString MariaDbClient::LastError() const
{
return m_lastError;
}
bool MariaDbClient::LoadClientLibrary()
{
if (m_library && m_init) {
return true;
}
const char* names[] = {
#ifdef Q_OS_WIN
"libmariadb.dll",
"libmysql.dll",
#else
"libmariadb.so.3",
"libmariadb.so",
"libmysqlclient.so.21",
"libmysqlclient.so",
#endif
nullptr
};
for (int i = 0; names[i]; ++i) {
m_library = LoadNativeLibrary(names[i]);
if (m_library) {
break;
}
}
if (!m_library) {
SetError(QStringLiteral("未找到 libmariadb,请先安装离线 MariaDB 包"));
return false;
}
if (!BindSymbols()) {
CloseNativeLibrary(m_library);
m_library = nullptr;
return false;
}
return true;
}
bool MariaDbClient::BindSymbols()
{
m_init = reinterpret_cast<void* (*)(void*)>(
ResolveSymbol(m_library, "mysql_init"));
m_realConnect = reinterpret_cast<void* (*)(void*,
const char*,
const char*,
const char*,
const char*,
unsigned int,
const char*,
unsigned long)>(
ResolveSymbol(m_library, "mysql_real_connect"));
m_query = reinterpret_cast<int (*)(void*, const char*)>(
ResolveSymbol(m_library, "mysql_query"));
m_storeResult = reinterpret_cast<void* (*)(void*)>(
ResolveSymbol(m_library, "mysql_store_result"));
m_freeResult = reinterpret_cast<void (*)(void*)>(
ResolveSymbol(m_library, "mysql_free_result"));
m_fetchRow = reinterpret_cast<char** (*)(void*)>(
ResolveSymbol(m_library, "mysql_fetch_row"));
m_numFields = reinterpret_cast<unsigned int (*)(void*)>(
ResolveSymbol(m_library, "mysql_num_fields"));
m_error = reinterpret_cast<const char* (*)(void*)>(
ResolveSymbol(m_library, "mysql_error"));
m_close = reinterpret_cast<void (*)(void*)>(
ResolveSymbol(m_library, "mysql_close"));
m_insertId = reinterpret_cast<unsigned long long (*)(void*)>(
ResolveSymbol(m_library, "mysql_insert_id"));
m_fieldCount = reinterpret_cast<unsigned int (*)(void*)>(
ResolveSymbol(m_library, "mysql_field_count"));
m_setCharacterSet = reinterpret_cast<int (*)(void*, const char*)>(
ResolveSymbol(m_library, "mysql_set_character_set"));
if (!m_init || !m_realConnect || !m_query || !m_storeResult ||
!m_freeResult || !m_fetchRow || !m_numFields || !m_error ||
!m_close || !m_insertId || !m_fieldCount) {
SetError(QStringLiteral("libmariadb 符号不完整"));
return false;
}
return true;
}
bool MariaDbClient::Connect(const QString& host,
int port,
const QString& user,
const QString& password,
const QString& database)
{
Close();
if (!LoadClientLibrary()) {
return false;
}
m_mysql = m_init(nullptr);
if (!m_mysql) {
SetError(QStringLiteral("mysql_init 失败"));
return false;
}
const QByteArray hostUtf8 = host.toUtf8();
const QByteArray userUtf8 = user.toUtf8();
const QByteArray passwordUtf8 = password.toUtf8();
const QByteArray databaseUtf8 = database.toUtf8();
// 连接失败时不影响引导主链路,调用方只记日志。
if (!m_realConnect(m_mysql,
hostUtf8.constData(),
userUtf8.constData(),
passwordUtf8.constData(),
databaseUtf8.constData(),
static_cast<unsigned int>(port),
nullptr,
0)) {
SetError(ConnectionError());
m_close(m_mysql);
m_mysql = nullptr;
return false;
}
if (m_setCharacterSet) {
m_setCharacterSet(m_mysql, "utf8mb4");
}
m_connected = true;
m_lastError.clear();
return true;
}
void MariaDbClient::Close()
{
if (m_mysql && m_close) {
m_close(m_mysql);
}
m_mysql = nullptr;
m_connected = false;
}
bool MariaDbClient::Exec(const QString& sql)
{
ResultSet unused;
return Query(sql, unused);
}
bool MariaDbClient::Query(const QString& sql, ResultSet& rows)
{
rows.clear();
if (!IsConnected()) {
SetError(QStringLiteral("数据库未连接"));
return false;
}
const QByteArray sqlUtf8 = sql.toUtf8();
if (m_query(m_mysql, sqlUtf8.constData()) != 0) {
SetError(ConnectionError());
return false;
}
void* result = m_storeResult(m_mysql);
if (!result) {
if (m_fieldCount(m_mysql) != 0) {
SetError(ConnectionError());
return false;
}
m_lastError.clear();
return true;
}
const unsigned int fieldCount = m_numFields(result);
while (char** row = m_fetchRow(result)) {
Row values;
for (unsigned int i = 0; i < fieldCount; ++i) {
values.append(row[i] ? QString::fromUtf8(row[i]) : QString());
}
rows.append(values);
}
m_freeResult(result);
m_lastError.clear();
return true;
}
unsigned long long MariaDbClient::LastInsertId() const
{
if (!IsConnected() || !m_insertId) {
return 0;
}
return m_insertId(m_mysql);
}
QString MariaDbClient::Escape(const QString& value)
{
QString escaped = value;
escaped.replace(QLatin1Char('\\'), QStringLiteral("\\\\"));
escaped.replace(QLatin1Char('\''), QStringLiteral("''"));
return escaped;
}
void MariaDbClient::SetError(const QString& message)
{
m_lastError = message;
}
QString MariaDbClient::ConnectionError() const
{
if (m_mysql && m_error) {
const char* text = m_error(m_mysql);
if (text && text[0] != '\0') {
return QString::fromUtf8(text);
}
}
return QStringLiteral("MariaDB 操作失败");
}