#include "MariaDbClient.h" #include #ifdef Q_OS_WIN #include #else #include #endif namespace { void* LoadNativeLibrary(const char* name) { #ifdef Q_OS_WIN return static_cast(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(handle)); #else dlclose(handle); #endif } void* ResolveSymbol(void* handle, const char* name) { #ifdef Q_OS_WIN return static_cast(GetProcAddress(static_cast(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( ResolveSymbol(m_library, "mysql_init")); m_realConnect = reinterpret_cast( ResolveSymbol(m_library, "mysql_real_connect")); m_query = reinterpret_cast( ResolveSymbol(m_library, "mysql_query")); m_storeResult = reinterpret_cast( ResolveSymbol(m_library, "mysql_store_result")); m_freeResult = reinterpret_cast( ResolveSymbol(m_library, "mysql_free_result")); m_fetchRow = reinterpret_cast( ResolveSymbol(m_library, "mysql_fetch_row")); m_numFields = reinterpret_cast( ResolveSymbol(m_library, "mysql_num_fields")); m_error = reinterpret_cast( ResolveSymbol(m_library, "mysql_error")); m_close = reinterpret_cast( ResolveSymbol(m_library, "mysql_close")); m_insertId = reinterpret_cast( ResolveSymbol(m_library, "mysql_insert_id")); m_fieldCount = reinterpret_cast( ResolveSymbol(m_library, "mysql_field_count")); m_setCharacterSet = reinterpret_cast( 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(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 操作失败"); }