diff --git a/dlls/mysqlx/basic_sql.cpp b/dlls/mysqlx/basic_sql.cpp index 8d0c4e4c..0695f72d 100644 --- a/dlls/mysqlx/basic_sql.cpp +++ b/dlls/mysqlx/basic_sql.cpp @@ -6,7 +6,6 @@ using namespace SourceMod; using namespace SourceHook; MysqlDriver g_Mysql; -List g_ConnectionInfo; void FreeConnection(void *p, unsigned int num) { @@ -17,8 +16,6 @@ void FreeConnection(void *p, unsigned int num) free(cn->pass); free(cn->db); - g_ConnectionInfo.remove(num); - delete cn; } @@ -60,8 +57,6 @@ static cell AMX_NATIVE_CALL SQL_MakeDbTuple(AMX *amx, cell *params) unsigned int num = MakeHandle(sql, Handle_Connection, FreeConnection); - g_ConnectionInfo.push_back(num); - return num; } diff --git a/dlls/mysqlx/handles.cpp b/dlls/mysqlx/handles.cpp index 65f38d79..bee22626 100644 --- a/dlls/mysqlx/handles.cpp +++ b/dlls/mysqlx/handles.cpp @@ -78,3 +78,30 @@ bool FreeHandle(unsigned int num) return true; } + +void FreeAllHandles(HandleType type) +{ + QHandle *q; + for (size_t i = 0; i < g_Handles.size(); i++) + { + q = g_Handles[i]; + if (q && !q->isfree && q->type == type) + { + FreeHandle((unsigned int)i); + } + } +} + +void FreeHandleTable() +{ + QHandle *q; + for (size_t i = 0; i < g_Handles.size(); i++) + { + q = g_Handles[i]; + if (q && !q->isfree) + FreeHandle((unsigned int)i); + } + g_Handles.clear(); + while (!g_FreeHandles.empty()) + g_FreeHandles.pop(); +} diff --git a/dlls/mysqlx/module.cpp b/dlls/mysqlx/module.cpp index 035d371e..ddc2064e 100644 --- a/dlls/mysqlx/module.cpp +++ b/dlls/mysqlx/module.cpp @@ -5,4 +5,16 @@ void OnAmxxAttach() { MF_AddNatives(g_BaseSqlNatives); MF_AddNatives(g_ThreadSqlNatives); + MF_RegisterFunction(&g_Mysql, "GetSqlDriver"); + if (!MF_RequestFunction("GetDbDriver")) + { + MF_AddNatives(g_OldCompatNatives); + } +} + +void ServerDeactivate_Post() +{ + FreeAllHandles(Handle_OldResult); + FreeAllHandles(Handle_OldDb); + FreeAllHandles(Handle_Connection); } diff --git a/dlls/mysqlx/mysql2.vcproj b/dlls/mysqlx/mysql2.vcproj index 246346f5..c8e05058 100644 --- a/dlls/mysqlx/mysql2.vcproj +++ b/dlls/mysqlx/mysql2.vcproj @@ -126,6 +126,9 @@ + + diff --git a/dlls/mysqlx/mysql2_header.h b/dlls/mysqlx/mysql2_header.h index 1a473115..50737462 100644 --- a/dlls/mysqlx/mysql2_header.h +++ b/dlls/mysqlx/mysql2_header.h @@ -21,7 +21,8 @@ enum HandleType Handle_Connection = 0, Handle_Database, Handle_Query, - Handle_ThreadQuery, + Handle_OldDb, + Handle_OldResult, }; struct SQL_Connection @@ -38,12 +39,14 @@ typedef void (*FREEHANDLE)(void *, unsigned int); unsigned int MakeHandle(void *ptr, HandleType type, FREEHANDLE f); void *GetHandle(unsigned int num, HandleType type); bool FreeHandle(unsigned int num); +void FreeAllHandles(HandleType type); +void FreeHandleTable(); extern AMX_NATIVE_INFO g_BaseSqlNatives[]; extern AMX_NATIVE_INFO g_ThreadSqlNatives[]; +extern AMX_NATIVE_INFO g_OldCompatNatives[]; extern MainThreader g_Threader; extern ThreadWorker *g_pWorker; extern SourceMod::MysqlDriver g_Mysql; #endif //_INCLUDE_AMXMODX_MYSQL2_HEADER_H - diff --git a/dlls/mysqlx/oldcompat_sql.cpp b/dlls/mysqlx/oldcompat_sql.cpp new file mode 100644 index 00000000..52b2d728 --- /dev/null +++ b/dlls/mysqlx/oldcompat_sql.cpp @@ -0,0 +1,429 @@ +#include "mysql2_header.h" + +struct olddb_s +{ + IDatabase *pDatabase; + char error[255]; + int errcode; +}; + +struct oldresult_s +{ + IQuery *pQuery; + QueryInfo info; + bool firstCall; +}; + +void FreeOldDb(void *ptr, unsigned int hndl) +{ + olddb_s *old = (olddb_s *)ptr; + + if (old->pDatabase) + { + old->pDatabase->FreeHandle(); + old->pDatabase = NULL; + } + + delete old; +} + +void FreeOldResult(void *ptr, unsigned int hndl) +{ + oldresult_s *oldres = (oldresult_s *)ptr; + + if (oldres->pQuery) + { + oldres->pQuery->FreeHandle(); + oldres->pQuery = NULL; + } + + delete oldres; +} + +//native Sql:dbi_connect(_host[], _user[], _pass[], _dbname[], _error[]="", _maxlength=0); +static cell AMX_NATIVE_CALL dbi_connect(AMX *amx, cell *params) +{ + int len; + DatabaseInfo info; + char *host = MF_GetAmxString(amx, params[1], 0, &len); + char *user = MF_GetAmxString(amx, params[2], 1, &len); + char *pass = MF_GetAmxString(amx, params[3], 2, &len); + char *name = MF_GetAmxString(amx, params[4], 3, &len); + + char *p = strchr(host, ':'); + if (p) + { + info.port = atoi(p+1); + *p = '\0'; + } else { + info.port = 0; + } + + info.host = host; + info.user = user; + info.pass = pass; + info.database = name; + + int err; + char error[512]; + IDatabase *pDatabase = g_Mysql.Connect(&info, &err, error, sizeof(error)-1); + if (!pDatabase) + { + MF_SetAmxString(amx, params[5], error, params[6]); + return 0; + } + + olddb_s *old = new olddb_s; + int hndl; + + old->pDatabase = pDatabase; + hndl = MakeHandle(old, Handle_OldDb, FreeOldDb); + + return hndl; +} + +//native Result:dbi_query(Sql:_sql, _query[], {Float,_}:...); +static cell AMX_NATIVE_CALL dbi_query(AMX *amx, cell *params) +{ + olddb_s *old = (olddb_s *)GetHandle(params[1], Handle_OldDb); + if (!old) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI handle %d", params[1]); + return -1; + } + + int len; + char *queryString = MF_FormatAmxString(amx, params, 2, &len); + + IQuery *pQuery = old->pDatabase->PrepareQuery(queryString); + QueryInfo info; + + old->error[0] = '\0'; + old->errcode = 0; + + if (!pQuery->Execute(&info, old->error, 254)) + { + old->errcode = info.errorcode; + return -1; + } else { + if (info.rs && info.rs->RowCount()) + { + oldresult_s *oldrs = new oldresult_s; + int hndl; + + oldrs->info = info; + oldrs->pQuery = pQuery; + hndl = MakeHandle(oldrs, Handle_OldResult, FreeOldResult); + return hndl; + } else { + pQuery->FreeHandle(); + return 0; + } + } + + /** never reach here */ + return 0; +} + +//native Result:dbi_query2(Sql:_sql, &rows, _query[], {Float,_}:...); +static cell AMX_NATIVE_CALL dbi_query2(AMX *amx, cell *params) +{ + olddb_s *old = (olddb_s *)GetHandle(params[1], Handle_OldDb); + if (!old) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI handle %d", params[1]); + return -1; + } + + int len; + char *queryString = MF_FormatAmxString(amx, params, 3, &len); + + IQuery *pQuery = old->pDatabase->PrepareQuery(queryString); + QueryInfo info; + + old->error[0] = '\0'; + old->errcode = 0; + + if (!pQuery->Execute(&info, old->error, 254)) + { + old->errcode = info.errorcode; + return -1; + } else { + cell *addr = MF_GetAmxAddr(amx, params[2]); + *addr = static_cast(info.affected_rows); + if (info.rs && info.rs->RowCount()) + { + oldresult_s *oldrs = new oldresult_s; + int hndl; + + oldrs->info = info; + oldrs->pQuery = pQuery; + oldrs->firstCall = true; + hndl = MakeHandle(oldrs, Handle_OldResult, FreeOldResult); + return hndl; + } else { + pQuery->FreeHandle(); + return 0; + } + } + + /** never reach here */ + return 0; +} + +//native dbi_nextrow(Result:_result); +static cell AMX_NATIVE_CALL dbi_nextrow(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + if (oldrs->firstCall) + { + oldrs->firstCall = false; + return (oldrs->info.rs->IsDone() ? 0 : 1); + } else { + oldrs->info.rs->NextRow(); + return (oldrs->info.rs->IsDone() ? 0 : 1); + } +} + +//native dbi_field(Result:_result, _fieldnum, {Float,_}:... ); +static cell AMX_NATIVE_CALL dbi_field(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + IResultSet *rs = oldrs->info.rs; + IResultRow *rr = rs->GetRow(); + unsigned int num = (unsigned int)params[2] - 1; + if (num >= rs->FieldCount()) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid column %d", params[2]); + return 0; + } + + cell stype = params[0] / sizeof(cell); + const char *data = rr->GetString(num); + if (!data) + data = ""; + + switch (stype) + { + case 2: + { + return atoi(data); + break; + } + case 3: + { + cell *destaddr = MF_GetAmxAddr(amx, params[3]); + REAL fdata = atof(data); + *destaddr = amx_ftoc(fdata); + return 1; + break; + } + case 4: + { + return MF_SetAmxString(amx, params[3], data, params[4]); + break; + } + } + + /** never reach here */ + return 0; +} + +//native dbi_result(Result:_result, _field[], {Float,_}:... ); +static cell AMX_NATIVE_CALL dbi_result(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + IResultSet *rs = oldrs->info.rs; + IResultRow *rr = rs->GetRow(); + unsigned int num; + bool found = false; + unsigned int fields = rs->FieldCount(); + int len; + char *field = MF_GetAmxString(amx, params[2], 0, &len); + num = -1; + for (unsigned int i=0; iFieldNumToName(i)) == 0) + { + num = i; + found = true; + break; + } + } + + if (!found) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Unknown column \"%s\"", field); + return 0; + } + + cell stype = params[0] / sizeof(cell); + const char *data = rr->GetString(num); + if (!data) + data = ""; + + switch (stype) + { + case 2: + { + return atoi(data); + break; + } + case 3: + { + cell *destaddr = MF_GetAmxAddr(amx, params[3]); + REAL fdata = atof(data); + *destaddr = amx_ftoc(fdata); + return 1; + break; + } + case 4: + { + return MF_SetAmxString(amx, params[3], data, params[4]); + break; + } + } + + /** never reach here */ + return 0; +} + +//native dbi_num_rows(Result:_result); +static cell AMX_NATIVE_CALL dbi_num_rows(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + return oldrs->info.rs->RowCount(); +} + +//native dbi_free_result(&Result:result); +static cell AMX_NATIVE_CALL dbi_free_result(AMX *amx, cell *params) +{ + cell *_r = MF_GetAmxAddr(amx, params[1]); + cell num = *_r; + oldresult_s *oldrs = (oldresult_s *)GetHandle(num, Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", num); + return 0; + } + + FreeHandle(num); + + *_r = 0; + + return 1; +} + +//native dbi_close(&Sql:_sql); +static cell AMX_NATIVE_CALL dbi_close(AMX *amx, cell *params) +{ + cell *_r = MF_GetAmxAddr(amx, params[1]); + cell num = *_r; + olddb_s *old = (olddb_s *)GetHandle(num, Handle_OldDb); + if (!old) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI handle %d", num); + return -1; + } + + FreeHandle(num); + + *_r = 0; + + return 1; +} + +//native dbi_error(Sql:_sql, _error[], _len); +static cell AMX_NATIVE_CALL dbi_error(AMX *amx, cell *params) +{ + olddb_s *old = (olddb_s *)GetHandle(params[1], Handle_OldDb); + if (!old) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI handle %d", params[1]); + return -1; + } + + MF_SetAmxString(amx, params[2], old->error, params[3]); + return old->errcode; +} + +//native dbi_type(_type[], _len); +static cell AMX_NATIVE_CALL dbi_type(AMX *amx, cell *params) +{ + return MF_SetAmxString(amx, params[1], "mysql", params[2]); +} + +//native dbi_num_fields(Result:result); +static cell AMX_NATIVE_CALL dbi_num_fields(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + return oldrs->info.rs->FieldCount(); +} + +//native dbi_field_name(Result:result, field, name[], maxLength); +static cell AMX_NATIVE_CALL dbi_field_name(AMX *amx, cell *params) +{ + oldresult_s *oldrs = (oldresult_s *)GetHandle(params[1], Handle_OldResult); + if (!oldrs) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid DBI result handle %d", params[1]); + return 0; + } + + const char *name = oldrs->info.rs->FieldNumToName(static_cast(params[2]-1)); + if (!name) + return 0; + + MF_SetAmxString(amx, params[3], name, params[4]); + + return 1; +} + +AMX_NATIVE_INFO g_OldCompatNatives[] = +{ + { "dbi_connect", dbi_connect }, + { "dbi_query", dbi_query }, + { "dbi_query2", dbi_query2 }, + { "dbi_field", dbi_field }, + { "dbi_nextrow", dbi_nextrow }, + { "dbi_close", dbi_close }, + { "dbi_error", dbi_error }, + { "dbi_type", dbi_type }, + { "dbi_free_result", dbi_free_result }, + { "dbi_num_rows", dbi_num_rows }, + { "dbi_result", dbi_result }, + { "dbi_num_fields", dbi_num_fields }, + { "dbi_field_name", dbi_field_name }, + + { NULL, NULL } +}; diff --git a/dlls/mysqlx/sdk/moduleconfig.h b/dlls/mysqlx/sdk/moduleconfig.h index 6a6abcbb..3acf2c38 100755 --- a/dlls/mysqlx/sdk/moduleconfig.h +++ b/dlls/mysqlx/sdk/moduleconfig.h @@ -137,7 +137,7 @@ // #define FN_ClientCommand_Post ClientCommand_Post // #define FN_ClientUserInfoChanged_Post ClientUserInfoChanged_Post // #define FN_ServerActivate_Post ServerActivate_Post -// #define FN_ServerDeactivate_Post ServerDeactivate_Post +#define FN_ServerDeactivate_Post ServerDeactivate_Post // #define FN_PlayerPreThink_Post PlayerPreThink_Post // #define FN_PlayerPostThink_Post PlayerPostThink_Post // #define FN_StartFrame_Post StartFrame_Post diff --git a/dlls/mysqlx/sqlxtest.sma b/dlls/mysqlx/sqlxtest.sma index a496d468..25324520 100644 --- a/dlls/mysqlx/sqlxtest.sma +++ b/dlls/mysqlx/sqlxtest.sma @@ -1,6 +1,7 @@ #include #include #include +#include new Handle:g_DbInfo new g_QueryNum @@ -10,6 +11,7 @@ public plugin_init() register_plugin("SQLX Test", "1.0", "BAILOPAN") register_srvcmd("sqlx_test_normal", "SqlxTest_Normal") register_srvcmd("sqlx_test_thread", "SqlxTest_Thread") + register_srvcmd("sqlx_test_old1", "SqlxTest_Old1") new configsDir[64] get_configsdir(configsDir, 63) @@ -33,6 +35,9 @@ public plugin_cfg() g_DbInfo = SQL_MakeDbTuple(host, user, pass, db) } +/** + * Note that this function works for both threaded and non-threaded queries. + */ PrintQueryData(Handle:query) { new columns = SQL_NumColumns(query) @@ -56,6 +61,9 @@ PrintQueryData(Handle:query) } } +/** + * Handler for when a threaded query is resolved. + */ public GetMyStuff(failstate, Handle:query, error[], errnum, data[], size) { server_print("Resolved query %d at: %f", data[0], get_gametime()) @@ -73,6 +81,9 @@ public GetMyStuff(failstate, Handle:query, error[], errnum, data[], size) } } +/** + * Starts a threaded query. + */ public SqlxTest_Thread() { new query[512] @@ -87,6 +98,9 @@ public SqlxTest_Thread() g_QueryNum++ } +/** + * Does a normal query. + */ public SqlxTest_Normal() { new errnum, error[255] @@ -114,6 +128,69 @@ public SqlxTest_Normal() SQL_FreeHandle(db) } +/** + * Wrapper for an old-style connection. + */ +Sql:OldInitDatabase() +{ + new host[64] + new user[64] + new pass[64] + new db[64] + + get_cvar_string("amx_sql_host", host, 63) + get_cvar_string("amx_sql_user", user, 63) + get_cvar_string("amx_sql_pass", pass, 63) + get_cvar_string("amx_sql_db", db, 63) + + new error[255] + new Sql:sql = dbi_connect(host, user, pass, db, error, 254) + if (sql < SQL_OK) + { + server_print("Connection failure: %s", error) + return SQL_FAILED + } + + return sql +} + +/** + * Tests index-based lookup + */ +public SqlxTest_Old1() +{ + new Sql:sql = OldInitDatabase() + if (sql < SQL_OK) + return + + new Result:res = dbi_query(sql, "SELECT * FROM gaben") + + if (res == RESULT_FAILED) + { + new error[255] + new code = dbi_error(sql, error, 254) + server_print("Result failed! [%d]: %s", code, error) + } else if (res == RESULT_NONE) { + server_print("No result set returned.") + } else { + new cols[2][32] + new str[32] + dbi_field_name(res, 1, cols[0], 31) + dbi_field_name(res, 2, cols[1], 31) + new row, num + while (dbi_nextrow(res) > 0) + { + num = dbi_field(res, 1) + dbi_field(res, 2, str, 31) + server_print("[%d]: %s=%d, %s=%s", row, cols[0], num, cols[1], str) + row++ + } + dbi_free_result(res) + } + + dbi_close(sql) +} + public plugin_end() { SQL_FreeHandle(g_DbInfo) diff --git a/dlls/mysqlx/threading.cpp b/dlls/mysqlx/threading.cpp index bfddbc2a..72d6b795 100644 --- a/dlls/mysqlx/threading.cpp +++ b/dlls/mysqlx/threading.cpp @@ -36,6 +36,8 @@ void OnAmxxDetach() } g_QueueLock->Unlock(); g_QueueLock->DestroyThis(); + + FreeHandleTable(); } //public QueryHandler(state, Handle:query, error[], errnum, data[], size)