Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2d611f19e6 | ||
![]() |
95f97501b8 | ||
![]() |
bf3ff37225 | ||
![]() |
f659497804 | ||
![]() |
9170d617db |
3
.gitignore
vendored
3
.gitignore
vendored
@ -11,4 +11,5 @@
|
||||
*.zip
|
||||
cmake-build-debug
|
||||
.idea
|
||||
.vs
|
||||
.vs
|
||||
.cache
|
18
README.md
18
README.md
@ -13,8 +13,9 @@ Download the latest module for your server's operating system and architecture u
|
||||
* [Linux (64-bit)](https://github.com/FredyH/MySQLOO/releases/latest/download/gmsv_mysqloo_linux64.dll)
|
||||
|
||||
### Notes
|
||||
* **If your server is using Windows**, you will need to install vcredist 2019, 2015, possibly 2008 and others for the module to load correctly. You can download them [here](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).
|
||||
For the 32-bit version of the module you will need the x86 redistributable, and for the 64 bit version the x64 redistributable.
|
||||
* If you're unsure of your server's operating system and architecture, type `lua_run print(jit.os, jit.arch)` into the server's console to find out. The output will be something similar to `Windows x86` (x86 is 32-bit, x64 is 64-bit).
|
||||
* If your server is using Windows, you will need to install vcredist 2019, 2015, possibly 2008 and others for the module to load correctly.
|
||||
* Previously you were required to place libmysqlclient.dll besides your srcds executable. This is not required anymore since MySQLOO now links statically against libmysqlclient.
|
||||
|
||||
# Documentation
|
||||
@ -57,6 +58,7 @@ Database:connect()
|
||||
Database:disconnect(shouldWait)
|
||||
-- Returns nothing
|
||||
-- disconnects from the database and waits for all queries to finish if shouldWait is true
|
||||
-- This function calls the onDisconnected callback if it existed on the database before the database was connected.
|
||||
|
||||
Database:query( sql )
|
||||
-- Returns [Query]
|
||||
@ -141,6 +143,15 @@ Database:setSSLSettings(key, cert, ca, capath, cipher)
|
||||
-- Every parameter is optional and can be omitted (set to nil) if not required.
|
||||
-- See https://dev.mysql.com/doc/c-api/8.0/en/mysql-ssl-set.html for the description of each parameter.
|
||||
|
||||
Database:setReadTimeout(timeout)
|
||||
Database:setWriteTimeout(timeout)
|
||||
Database:setConnectTimeout(timeout)
|
||||
-- Returns nothing
|
||||
-- Sets the corresponding timeout value in seconds for any queries operations started by this database instance.
|
||||
-- The timeout value needs to be at least 1. If this is not called, the default value is used.
|
||||
-- For information about the timeout values read the documentation here:
|
||||
-- https://dev.mysql.com/doc/c-api/8.0/en/mysql-options.html
|
||||
|
||||
-- Callbacks
|
||||
Database.onConnected( db )
|
||||
-- Called when the connection to the MySQL server is successful
|
||||
@ -148,6 +159,11 @@ Database.onConnected( db )
|
||||
Database.onConnectionFailed( db, err )
|
||||
-- Called when the connection to the MySQL server fails, [String] err is why.
|
||||
|
||||
Database.onDisconnected( db )
|
||||
-- Called after Database.disconnect has been called and all queries have finished executing
|
||||
-- Note: You have to set this callback before calling Database:connect() or it will not be called.
|
||||
|
||||
|
||||
-- Query/PreparedQuery object (transactions also inherit all functions, some have no effect though)
|
||||
|
||||
-- Functions
|
||||
|
@ -173,6 +173,7 @@ end
|
||||
function db:PrepareQuery(str, values, callback, ...)
|
||||
self.CachedStatements = self.CachedStatements or {}
|
||||
local preparedQuery = self.CachedStatements[str] or self:prepare(str)
|
||||
self.CachedStatements[str] = preparedQuery
|
||||
addQueryFunctions(preparedQuery, callback, ...)
|
||||
setPreparedQueryArguments(preparedQuery, values)
|
||||
preparedQuery:start()
|
||||
@ -226,4 +227,4 @@ function db:CreateTransaction()
|
||||
transaction._db = self
|
||||
setmetatable(transaction, transactionMT)
|
||||
return transaction
|
||||
end
|
||||
end
|
||||
|
@ -103,6 +103,12 @@ MYSQLOO_LUA_FUNCTION(connect) {
|
||||
LUA->Push(1);
|
||||
database->m_tableReference = LuaReferenceCreate(LUA);
|
||||
}
|
||||
|
||||
LUA->ReferencePush(database->m_tableReference);
|
||||
LUA->GetField(-1, "onDisconnected");
|
||||
database->m_hasOnDisconnected = LUA->IsType(-1, GarrysMod::Lua::Type::Function);
|
||||
LUA->Pop(2); // callback, table
|
||||
|
||||
database->m_database->connect();
|
||||
return 0;
|
||||
}
|
||||
@ -148,6 +154,36 @@ MYSQLOO_LUA_FUNCTION(setSSLSettings) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQLOO_LUA_FUNCTION(setReadTimeout) {
|
||||
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA);
|
||||
unsigned int timeout = (int) LUA->GetNumber(2);
|
||||
if (timeout == 0) {
|
||||
LUA->ThrowError("Timeout must be at least 1");
|
||||
}
|
||||
database->m_database->setReadTimeout(timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQLOO_LUA_FUNCTION(setWriteTimeout) {
|
||||
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA);
|
||||
unsigned int timeout = (int) LUA->GetNumber(2);
|
||||
if (timeout == 0) {
|
||||
LUA->ThrowError("Timeout must be at least 1");
|
||||
}
|
||||
database->m_database->setWriteTimeout(timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQLOO_LUA_FUNCTION(setConnectTimeout) {
|
||||
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA);
|
||||
unsigned int timeout = (int) LUA->GetNumber(2);
|
||||
if (timeout == 0) {
|
||||
LUA->ThrowError("Timeout must be at least 1");
|
||||
}
|
||||
database->m_database->setConnectTimeout(timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQLOO_LUA_FUNCTION(disconnect) {
|
||||
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA);
|
||||
bool wait = false;
|
||||
@ -252,6 +288,15 @@ void LuaDatabase::createMetaTable(ILuaBase *LUA) {
|
||||
LUA->PushCFunction(setSSLSettings);
|
||||
LUA->SetField(-2, "setSSLSettings");
|
||||
|
||||
LUA->PushCFunction(setReadTimeout);
|
||||
LUA->SetField(-2, "setReadTimeout");
|
||||
|
||||
LUA->PushCFunction(setWriteTimeout);
|
||||
LUA->SetField(-2, "setWriteTimeout");
|
||||
|
||||
LUA->PushCFunction(setConnectTimeout);
|
||||
LUA->SetField(-2, "setConnectTimeout");
|
||||
|
||||
LUA->PushCFunction(disconnect);
|
||||
LUA->SetField(-2, "disconnect");
|
||||
|
||||
@ -312,7 +357,6 @@ void LuaDatabase::think(ILuaBase *LUA) {
|
||||
LUA->ReferencePush(this->m_tableReference);
|
||||
pcallWithErrorReporter(LUA, 1);
|
||||
}
|
||||
LUA->Pop(); //Callback function
|
||||
} else {
|
||||
LUA->GetField(-1, "onConnectionFailed");
|
||||
if (LUA->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
@ -321,11 +365,15 @@ void LuaDatabase::think(ILuaBase *LUA) {
|
||||
LUA->PushString(error.c_str());
|
||||
pcallWithErrorReporter(LUA, 2);
|
||||
}
|
||||
LUA->Pop(); //Callback function
|
||||
}
|
||||
LUA->Pop(); // DB Table
|
||||
|
||||
LuaReferenceFree(LUA, this->m_tableReference);
|
||||
this->m_tableReference = 0;
|
||||
if (!this->m_hasOnDisconnected) {
|
||||
// Only free the table reference if we do not have an onDisconnected callback.
|
||||
// Otherwise, it will be freed after the onDisconnected callback was called.
|
||||
LuaReferenceFree(LUA, this->m_tableReference);
|
||||
this->m_tableReference = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Run callbacks of finished queries
|
||||
@ -333,6 +381,21 @@ void LuaDatabase::think(ILuaBase *LUA) {
|
||||
for (auto &pair: finishedQueries) {
|
||||
LuaQuery::runCallback(LUA, pair.first, pair.second);
|
||||
}
|
||||
|
||||
if (database->wasDisconnected() && this->m_hasOnDisconnected && this->m_tableReference != 0) {
|
||||
this->m_hasOnDisconnected = false;
|
||||
|
||||
LUA->ReferencePush(this->m_tableReference);
|
||||
|
||||
LUA->GetField(-1, "onDisconnected");
|
||||
if (LUA->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||
LUA->ReferencePush(this->m_tableReference);
|
||||
pcallWithErrorReporter(LUA, 1);
|
||||
}
|
||||
LUA->Pop(1); // DB Table
|
||||
|
||||
LuaReferenceFree(LUA, this->m_tableReference);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaDatabase::onDestroyedByLua(ILuaBase *LUA) {
|
||||
|
@ -17,6 +17,7 @@ public:
|
||||
void think(ILuaBase *LUA);
|
||||
|
||||
int m_tableReference = 0;
|
||||
bool m_hasOnDisconnected = false;
|
||||
std::shared_ptr<Database> m_database;
|
||||
bool m_dbCallbackRan = false;
|
||||
|
||||
|
@ -68,6 +68,11 @@ LUA_FUNCTION(errorReporter) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to LUA->PCall but also uses an error reporter and prints the
|
||||
* error to the console using ErrorNoHalt (if it exists).
|
||||
* Consumes the function and all nargs arguments on the stack, does not return any values.
|
||||
*/
|
||||
void LuaObject::pcallWithErrorReporter(ILuaBase *LUA, int nargs) {
|
||||
LUA->PushCFunction(errorReporter);
|
||||
int errorHandlerIndex = LUA->Top() - nargs - 1;
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "Database.h"
|
||||
#include "MySQLOOException.h"
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include "mysqld_error.h"
|
||||
@ -216,7 +215,13 @@ void Database::disconnect(bool wait) {
|
||||
if (wait && m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
disconnected = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true after the database has been fully disconnected and no more queries are in the queue.
|
||||
*/
|
||||
bool Database::wasDisconnected() {
|
||||
return disconnected;
|
||||
}
|
||||
|
||||
/* Returns the status of the database, constants can be found in GMModule
|
||||
@ -362,6 +367,7 @@ void Database::connectRun() {
|
||||
if (m_status == DATABASE_CONNECTED) {
|
||||
m_status = DATABASE_NOT_CONNECTED;
|
||||
}
|
||||
disconnected = true;
|
||||
});
|
||||
{
|
||||
auto connectionSignaler = finally([&] { m_connectWakeupVariable.notify_one(); });
|
||||
@ -374,6 +380,7 @@ void Database::connectRun() {
|
||||
m_status = DATABASE_CONNECTION_FAILED;
|
||||
return;
|
||||
}
|
||||
this->applyTimeoutSettings();
|
||||
this->customSSLSettings.applySSLSettings(this->m_sql);
|
||||
const char *socketStr = (this->socket.length() == 0) ? nullptr : this->socket.c_str();
|
||||
unsigned long clientFlag = (this->useMultiStatements) ? CLIENT_MULTI_STATEMENTS : 0;
|
||||
@ -406,6 +413,8 @@ void Database::connectRun() {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "misc-no-recursion"
|
||||
void Database::runQuery(const std::shared_ptr<IQuery>& query, const std::shared_ptr<IQueryData>& data, bool retry) {
|
||||
try {
|
||||
query->executeStatement(*this, this->m_sql, data);
|
||||
@ -427,6 +436,7 @@ void Database::runQuery(const std::shared_ptr<IQuery>& query, const std::shared_
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/* The run method of the thread of the database instance.
|
||||
*/
|
||||
@ -469,11 +479,34 @@ void Database::run() {
|
||||
bool Database::attemptReconnect() {
|
||||
bool success;
|
||||
my_bool reconnect = '1';
|
||||
mysql_optionsv(this->m_sql, MYSQL_OPT_RECONNECT, &reconnect);
|
||||
success = mariadb_reconnect(this->m_sql) == 0;
|
||||
reconnect = '0';
|
||||
mysql_optionsv(this->m_sql, MYSQL_OPT_RECONNECT, &reconnect);
|
||||
return success;
|
||||
}
|
||||
|
||||
void Database::setConnectTimeout(unsigned int timeout) {
|
||||
this->connectTimeout = timeout;
|
||||
}
|
||||
|
||||
void Database::setReadTimeout(unsigned int timeout) {
|
||||
this->readTimeout = timeout;
|
||||
}
|
||||
|
||||
void Database::setWriteTimeout(unsigned int timeout) {
|
||||
this->writeTimeout = timeout;
|
||||
}
|
||||
|
||||
void Database::applyTimeoutSettings() {
|
||||
if (this->connectTimeout > 0) {
|
||||
mysql_optionsv(this->m_sql, MYSQL_OPT_CONNECT_TIMEOUT, &this->connectTimeout);
|
||||
}
|
||||
if (this->readTimeout > 0) {
|
||||
mysql_optionsv(this->m_sql, MYSQL_OPT_READ_TIMEOUT, &this->readTimeout);
|
||||
}
|
||||
if (this->writeTimeout > 0) {
|
||||
mysql_optionsv(this->m_sql, MYSQL_OPT_WRITE_TIMEOUT, &this->writeTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
StatementHandle::StatementHandle(MYSQL_STMT *stmt, bool valid) : stmt(stmt), valid(valid) {}
|
||||
|
@ -95,6 +95,12 @@ public:
|
||||
|
||||
void disconnect(bool wait);
|
||||
|
||||
void setConnectTimeout(unsigned int timeout);
|
||||
|
||||
void setReadTimeout(unsigned int timeout);
|
||||
|
||||
void setWriteTimeout(unsigned int timeout);
|
||||
|
||||
void setSSLSettings(const SSLSettings &settings);
|
||||
|
||||
bool isConnectionDone() { return m_connectionDone; }
|
||||
@ -109,6 +115,7 @@ public:
|
||||
return finishedQueries.clear();
|
||||
}
|
||||
|
||||
bool wasDisconnected();
|
||||
private:
|
||||
Database(std::string host, std::string username, std::string pw, std::string database, unsigned int port,
|
||||
std::string unixSocket);
|
||||
@ -132,6 +139,8 @@ private:
|
||||
|
||||
void waitForQuery(const std::shared_ptr<IQuery> &query, const std::shared_ptr<IQueryData> &data);
|
||||
|
||||
void applyTimeoutSettings();
|
||||
|
||||
BlockingQueue<std::pair<std::shared_ptr<IQuery>, std::shared_ptr<IQueryData>>> finishedQueries{};
|
||||
BlockingQueue<std::pair<std::shared_ptr<IQuery>, std::shared_ptr<IQueryData>>> queryQueue{};
|
||||
std::unordered_set<std::shared_ptr<StatementHandle>> cachedStatements{};
|
||||
@ -150,10 +159,10 @@ private:
|
||||
bool shouldAutoReconnect = true;
|
||||
bool useMultiStatements = true;
|
||||
bool startedConnecting = false;
|
||||
bool disconnected = false;
|
||||
bool m_canWait = false;
|
||||
std::pair<std::shared_ptr<IQuery>, std::shared_ptr<IQueryData>> m_waitingQuery = {nullptr, nullptr};
|
||||
std::atomic<bool> m_success{true};
|
||||
std::atomic<bool> disconnected { false };
|
||||
std::atomic<bool> m_connectionDone{false};
|
||||
std::atomic<bool> cachePreparedStatements{true};
|
||||
std::condition_variable m_queryWakeupVariable{};
|
||||
@ -165,6 +174,9 @@ private:
|
||||
std::string socket;
|
||||
unsigned int port;
|
||||
SSLSettings customSSLSettings{};
|
||||
unsigned int readTimeout = 0;
|
||||
unsigned int writeTimeout = 0;
|
||||
unsigned int connectTimeout = 0;
|
||||
std::atomic<DatabaseStatus> m_status{DATABASE_NOT_CONNECTED};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user