//TODO Fix memory leak if transaction is never started int Transaction::addQuery(lua_State* state) { GarrysMod::Lua::ILuaBase* LUA = state->luabase; LUA->SetState(state); Transaction* transaction = dynamic_cast<Transaction*>(unpackSelf(LUA, TYPE_QUERY)); if (transaction == nullptr) { LUA->ThrowError("Tried to pass wrong self"); } IQuery* iQuery = (IQuery*)unpackLuaObject(LUA, 2, TYPE_QUERY, false); Query* query = dynamic_cast<Query*>(iQuery); if (query == nullptr) { LUA->ThrowError("Tried to pass non query to addQuery()"); } //This is all very ugly LUA->Push(1); LUA->GetField(-1, "__queries"); if (LUA->IsType(-1, GarrysMod::Lua::Type::NIL)) { LUA->Pop(); LUA->CreateTable(); LUA->SetField(-2, "__queries"); LUA->GetField(-1, "__queries"); } int tblIndex = LUA->Top(); LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB); LUA->GetField(-1, "table"); LUA->GetField(-1, "insert"); LUA->Push(tblIndex); LUA->Push(2); LUA->Call(2, 0); LUA->Push(4); return 0; }
/* Returns the status of the database, constants can be found in GMModule */ int Database::status(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); LUA->PushNumber(object->m_status); return 1; }
/* Creates and returns a PreparedQuery instance and enqueues it into the queue of accepted queries. */ int Database::createTransaction(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); Transaction* transactionObject = new Transaction(object, state); transactionObject->pushTableReference(state); return 1; }
/* Returns the amount of queued querys in the database instance * If a query is currently being processed, it does not count towards the queue size */ int Database::queueSize(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); std::unique_lock<std::mutex> qlck(object->m_queryQueueMutex); LUA->PushNumber(object->queryQueue.size()); return 1; }
int Transaction::getQueries(lua_State* state) { GarrysMod::Lua::ILuaBase* LUA = state->luabase; LUA->SetState(state); Transaction* transaction = dynamic_cast<Transaction*>(unpackSelf(LUA, TYPE_QUERY)); if (transaction == nullptr) { LUA->ThrowError("Tried to pass wrong self"); } LUA->Push(1); LUA->GetField(-1, "__queries"); return 1; }
/* Returns a string of the hostname connected to as well as the connection type * Only works as soon as the connection has been established */ int Database::hostInfo(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); if (!object->m_connectionDone) { LUA->ThrowError("Tried to get server info when client is not connected to server yet!"); } LUA->PushString(object->m_hostInfo.c_str()); return 1; }
/* Returns the server version as a formatted integer (XYYZZ, X= major-, Y=minor, Z=sub-version) * Only works as soon as the connection has been established */ int Database::serverVersion(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); if (!object->m_connectionDone) { LUA->ThrowError("Tried to get server version when client is not connected to server yet!"); } LUA->PushNumber(object->m_serverVersion); return 1; }
/* Creates and returns a PreparedQuery instance and enqueues it into the queue of accepted queries. */ int Database::prepare(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); LUA->CheckType(2, GarrysMod::Lua::Type::STRING); unsigned int outLen = 0; const char* query = LUA->GetString(2, &outLen); PreparedQuery* queryObject = new PreparedQuery(object, state); queryObject->setQuery(std::string(query, outLen)); queryObject->pushTableReference(state); return 1; }
int Database::setMultiStatements(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE, true); if (object->m_status != DATABASE_NOT_CONNECTED || object->startedConnecting) { LUA->ThrowError("Database already connected."); } LUA->CheckType(2, GarrysMod::Lua::Type::BOOL); object->useMultiStatements = LUA->GetBool(2); return 0; }
/* Starts the thread that connects to the database and then handles queries. */ int Database::connect(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE, true); if (object->m_status != DATABASE_NOT_CONNECTED || object->startedConnecting) { LUA->ThrowError("Database already connected."); } object->startedConnecting = true; object->m_status = DATABASE_CONNECTING; object->m_thread = std::thread(&Database::connectRun, object); return 0; }
/* Waits for the connection of the database to finish by blocking the current thread until the connect thread finished. * Callbacks are going to be called before this function returns */ int Database::wait(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); if (!object->startedConnecting) { LUA->ThrowError("Tried to wait for database connection to finish without starting the connection!"); } std::unique_lock<std::mutex> lck(object->m_connectMutex); while (!object->m_connectionDone) object->m_connectWakeupVariable.wait(lck); object->think(state); return 0; }
/* Aborts all queries that are in the queue of started queries and returns the number of successfully aborted queries. * Does not abort queries that are already taken from the queue and being processed. */ int Database::abortAllQueries(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); std::lock_guard<std::mutex> lock(object->m_queryQueueMutex); for (auto& query : object->queryQueue) { query->m_status = QUERY_ABORTED; query->unreference(state); } LUA->PushNumber((double)object->queryQueue.size()); object->queryQueue.clear(); return 1; }
/* Escapes an unescaped string using the database taking into account the characterset of the database. * This might break if the characterset of the database is changed after the connection was done */ int Database::escape(lua_State* state) { LOG_CURRENT_FUNCTIONCALL Database* object = (Database*)unpackSelf(state, TYPE_DATABASE); std::lock_guard<std::mutex>(object->m_connectMutex); //No query mutex needed since this doesn't use the connection at all if (!object->m_connectionDone || object->m_sql == nullptr) return 0; LUA->CheckType(2, GarrysMod::Lua::Type::STRING); const char* sQuery = LUA->GetString(2); size_t nQueryLength = strlen(sQuery); //escaped string can be twice as big as original string //source: http://dev.mysql.com/doc/refman/5.1/en/mysql-real-escape-string.html std::vector<char> escapedQuery(nQueryLength * 2 + 1); mysql_real_escape_string(object->m_sql, escapedQuery.data(), sQuery, nQueryLength); LUA->PushString(escapedQuery.data()); return 1; }