// vim: set ts=4 sw=4 tw=99 noet: // // AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). // Copyright (C) The AMX Mod X Development Team. // // This software is licensed under the GNU General Public License, version 3 or higher. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // https://alliedmods.net/amxmodx-license // // MySQL Module // #include #include "mysql2_header.h" #include "sqlheaders.h" using namespace SourceMod; MysqlDriver g_Mysql; void FreeConnection(void *p, unsigned int num) { SQL_Connection *cn = (SQL_Connection *)p; free(cn->host); free(cn->user); free(cn->pass); free(cn->db); free(cn->charset); delete cn; } void FreeQuery(void *p, unsigned int num) { AmxQueryInfo *qry = (AmxQueryInfo *)p; qry->pQuery->FreeHandle(); delete qry; } void FreeDatabase(void *p, unsigned int num) { IDatabase *db = (IDatabase *)p; db->FreeHandle(); } static cell AMX_NATIVE_CALL SQL_MakeDbTuple(AMX *amx, cell *params) { SQL_Connection *sql = new SQL_Connection; int len; char *host = strdup(MF_GetAmxString(amx, params[1], 0, &len)); char *p = strchr(host, ':'); if (p) { sql->port = atoi(p+1); *p = '\0'; } else { sql->port = 0; } sql->host = host; sql->user = strdup(MF_GetAmxString(amx, params[2], 0, &len)); sql->pass = strdup(MF_GetAmxString(amx, params[3], 0, &len)); sql->db = strdup(MF_GetAmxString(amx, params[4], 0, &len)); if (params[0] / sizeof(cell) >= 5) { sql->max_timeout = static_cast(params[5]); } sql->charset = NULL; unsigned int num = MakeHandle(sql, Handle_Connection, FreeConnection); return num; } static cell AMX_NATIVE_CALL SQL_FreeHandle(AMX *amx, cell *params) { if (!FreeHandle(params[1])) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid handle: %d", params[1]); return 0; } return 1; } static cell AMX_NATIVE_CALL SQL_Connect(AMX *amx, cell *params) { SQL_Connection *sql = (SQL_Connection *)GetHandle(params[1], Handle_Connection); if (!sql) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid info tuple handle: %d", params[1]); return 0; } DatabaseInfo nfo; nfo.database = sql->db; nfo.user = sql->user; nfo.pass = sql->pass; nfo.port = sql->port; nfo.host = sql->host; nfo.max_timeout = sql->max_timeout; nfo.charset = sql->charset; char buffer[512]; int errcode; IDatabase *pDb = g_Mysql.Connect2(&nfo, &errcode, buffer, sizeof(buffer)-1); if (!pDb) { cell *c_err = MF_GetAmxAddr(amx, params[2]); *c_err = errcode; MF_SetAmxString(amx, params[3], buffer, params[4]); return 0; } return MakeHandle(pDb, Handle_Database, FreeDatabase); } static cell AMX_NATIVE_CALL SQL_PrepareQuery(AMX *amx, cell *params) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); if (!pDb) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid database handle: %d", params[1]); return 0; } int len; char *fmt = MF_FormatAmxString(amx, params, 2, &len); IQuery *pQuery = pDb->PrepareQuery(fmt); if (!pQuery) return 0; AmxQueryInfo *qinfo = new AmxQueryInfo; qinfo->pQuery = pQuery; memset(&qinfo->info, 0, sizeof(QueryInfo)); return MakeHandle(qinfo, Handle_Query, FreeQuery); } static cell AMX_NATIVE_CALL SQL_Execute(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } qInfo->error[0] = '\0'; memset(&qInfo->info, 0, sizeof(QueryInfo)); if (!qInfo->pQuery->Execute2(&qInfo->info, qInfo->error, 254)) { return 0; } return 1; } static cell AMX_NATIVE_CALL SQL_QueryError(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } MF_SetAmxString(amx, params[2], qInfo->error, params[3]); return qInfo->info.errorcode; } static cell AMX_NATIVE_CALL SQL_MoreResults(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } if (!qInfo->info.rs) return 0; return (qInfo->info.rs->IsDone() ? 0 : 1); } static cell AMX_NATIVE_CALL SQL_IsNull(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs || rs->IsDone()) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } unsigned int col = static_cast(params[2]); if (col >= rs->FieldCount()) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid column: %d", col); return 0; } IResultRow *rr = rs->GetRow(); return rr->IsNull(col) ? 1 : 0; } static cell AMX_NATIVE_CALL SQL_ReadResult(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs || rs->IsDone()) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } IResultRow *row = rs->GetRow(); unsigned int col = static_cast(params[2]); if (col >= rs->FieldCount()) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid column: %d", col); return 0; } cell numparams = params[0] / sizeof(cell); switch (numparams) { case 4: { const char *str = row->GetString(col); if (!str) str = ""; cell *len = MF_GetAmxAddr(amx, params[4]); MF_SetAmxString(amx, params[3], str, (int)*len); break; } case 3: { REAL num = row->GetFloat(col); cell *addr = MF_GetAmxAddr(amx, params[3]); *addr = amx_ftoc(num); break; } case 2: { int num = row->GetInt(col); return num; break; } default: { MF_LogError(amx, AMX_ERR_NATIVE, "Bad number of arguments passed."); break; } } return 1; } static cell AMX_NATIVE_CALL SQL_NextRow(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs || rs->IsDone()) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } rs->NextRow(); return 1; } static cell AMX_NATIVE_CALL SQL_AffectedRows(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } return static_cast(qInfo->info.affected_rows); } static cell AMX_NATIVE_CALL SQL_NumResults(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { return 0; } return rs->RowCount(); } static cell AMX_NATIVE_CALL SQL_NumColumns(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } return rs->FieldCount(); } static cell AMX_NATIVE_CALL SQL_FieldNumToName(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } unsigned int col = static_cast(params[2]); const char *namewa = rs->FieldNumToName(col); if (!namewa) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid column: %d", col); return 0; } MF_SetAmxString(amx, params[3], namewa, params[4]); return 1; } static cell AMX_NATIVE_CALL SQL_GetQueryString(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo || (!qInfo->pQuery && !qInfo->opt_ptr)) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } const char *ptr = qInfo->pQuery ? qInfo->pQuery->GetQueryString() : qInfo->opt_ptr; return MF_SetAmxString(amx, params[2], ptr, params[3]); } static cell AMX_NATIVE_CALL SQL_FieldNameToNum(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } int len; char *namewa = MF_GetAmxString(amx, params[2], 0, &len); unsigned int columnId; if (!rs->FieldNameToNum(namewa, &columnId)) { return -1; } return columnId; } static cell AMX_NATIVE_CALL SQL_GetInsertId(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } return qInfo->info.insert_id; } static cell AMX_NATIVE_CALL SQL_GetAffinity(AMX *amx, cell *params) { return MF_SetAmxString(amx, params[1], g_Mysql.NameString(), params[2]); } static cell AMX_NATIVE_CALL SQL_SetAffinity(AMX *amx, cell *params) { int len; char *str = MF_GetAmxString(amx, params[1], 0, &len); if (!str[0]) { return 1; } if (stricmp(str, g_Mysql.NameString()) == 0) { return 1; } SqlFunctions *pFuncs = (SqlFunctions *)MF_RequestFunction(SQL_DRIVER_FUNC); while (pFuncs) { if (pFuncs->driver->IsCompatDriver(str)) { return pFuncs->set_affinity(amx); } pFuncs = pFuncs->prev; } return 0; } static cell AMX_NATIVE_CALL SQL_Rewind(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } rs->Rewind(); return 1; } static cell AMX_NATIVE_CALL SQL_NextResultSet(AMX *amx, cell *params) { AmxQueryInfo *qInfo = (AmxQueryInfo *)GetHandle(params[1], Handle_Query); if (!qInfo) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid query handle: %d", params[1]); return 0; } IResultSet *rs = qInfo->info.rs; if (!rs) { MF_LogError(amx, AMX_ERR_NATIVE, "No result set in this query!"); return 0; } if (rs->NextResultSet()) { return 1; } else { qInfo->info.rs = NULL; return 0; } } static cell AMX_NATIVE_CALL SQL_QuoteString(AMX *amx, cell *params) { int len; char *str = MF_GetAmxString(amx, params[4], 0, &len); size_t newsize; static char buffer[8192]; if (params[1] != 0) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); if (!pDb) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid database handle: %d", params[1]); return 0; } if (pDb->QuoteString(str, buffer, sizeof(buffer)-1, &newsize) == 0) { MF_SetAmxString(amx, params[2], buffer, params[3]); return newsize; } else { return -1; } } else { if (g_Mysql.QuoteString(str, buffer, sizeof(buffer)-1, &newsize) == 0) { MF_SetAmxString(amx, params[2], buffer, params[3]); return newsize; } else { return -1; } } } static cell AMX_NATIVE_CALL SQL_QuoteStringFmt(AMX *amx, cell *params) { int len; char *str = MF_FormatAmxString(amx, params, 4, &len); size_t newsize; static char buffer[8192]; if (params[1] != 0) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); if (!pDb) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid database handle: %d", params[1]); return 0; } if (pDb->QuoteString(str, buffer, sizeof(buffer)-1, &newsize) == 0) { MF_SetAmxString(amx, params[2], buffer, params[3]); return newsize; } else { return -1; } } else { if (g_Mysql.QuoteString(str, buffer, sizeof(buffer)-1, &newsize) == 0) { MF_SetAmxString(amx, params[2], buffer, params[3]); return newsize; } else { return -1; } } } static cell AMX_NATIVE_CALL SQL_SetCharset(AMX *amx, cell *params) { SQL_Connection *sql = (SQL_Connection *)GetHandle(params[1], Handle_Connection); if (!sql) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); if (!pDb) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid info tuple or database handle: %d", params[1]); return 0; } int len; return pDb->SetCharacterSet(MF_GetAmxString(amx, params[2], 0, &len)); } else { int len; const char *charset = MF_GetAmxString(amx, params[2], 0, &len); if (!sql->charset || stricmp(charset, sql->charset)) { sql->charset = strdup(charset); } return 1; } return 0; } AMX_NATIVE_INFO g_BaseSqlNatives[] = { {"SQL_MakeDbTuple", SQL_MakeDbTuple}, {"SQL_FreeHandle", SQL_FreeHandle}, {"SQL_Connect", SQL_Connect}, {"SQL_PrepareQuery", SQL_PrepareQuery}, {"SQL_Execute", SQL_Execute}, {"SQL_QueryError", SQL_QueryError}, {"SQL_MoreResults", SQL_MoreResults}, {"SQL_IsNull", SQL_IsNull}, {"SQL_ReadResult", SQL_ReadResult}, {"SQL_NextRow", SQL_NextRow}, {"SQL_AffectedRows", SQL_AffectedRows}, {"SQL_NumResults", SQL_NumResults}, {"SQL_NumColumns", SQL_NumColumns}, {"SQL_FieldNumToName", SQL_FieldNumToName}, {"SQL_FieldNameToNum", SQL_FieldNameToNum}, {"SQL_GetAffinity", SQL_GetAffinity}, {"SQL_SetAffinity", SQL_SetAffinity}, {"SQL_GetInsertId", SQL_GetInsertId}, {"SQL_GetQueryString", SQL_GetQueryString}, {"SQL_Rewind", SQL_Rewind}, {"SQL_QuoteString", SQL_QuoteString}, {"SQL_QuoteStringFmt", SQL_QuoteStringFmt}, {"SQL_NextResultSet", SQL_NextResultSet}, {"SQL_SetCharset", SQL_SetCharset}, {NULL, NULL}, };