diff --git a/dlls/mysqlx/basic_sql.cpp b/dlls/mysqlx/basic_sql.cpp index 073a2ae0..30aab78b 100644 --- a/dlls/mysqlx/basic_sql.cpp +++ b/dlls/mysqlx/basic_sql.cpp @@ -489,6 +489,34 @@ static cell AMX_NATIVE_CALL SQL_Rewind(AMX *amx, cell *params) 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) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); @@ -559,6 +587,7 @@ AMX_NATIVE_INFO g_BaseSqlNatives[] = {"SQL_Rewind", SQL_Rewind}, {"SQL_QuoteString", SQL_QuoteString}, {"SQL_QuoteStringFmt", SQL_QuoteStringFmt}, + {"SQL_NextResultSet", SQL_NextResultSet}, {NULL, NULL}, }; diff --git a/dlls/mysqlx/mysql/ISQLDriver.h b/dlls/mysqlx/mysql/ISQLDriver.h index d1a8144a..20fc7eb7 100644 --- a/dlls/mysqlx/mysql/ISQLDriver.h +++ b/dlls/mysqlx/mysql/ISQLDriver.h @@ -61,6 +61,10 @@ namespace SourceMod * Rewinds to the first row. */ virtual void Rewind() =0; + /** + * Transitions to the next result set, if any is available. + */ + virtual bool NextResultSet() =0; }; struct QueryInfo diff --git a/dlls/mysqlx/mysql/MysqlDriver.cpp b/dlls/mysqlx/mysql/MysqlDriver.cpp index 3c28780f..76b3c7cf 100644 --- a/dlls/mysqlx/mysql/MysqlDriver.cpp +++ b/dlls/mysqlx/mysql/MysqlDriver.cpp @@ -57,7 +57,7 @@ IDatabase *MysqlDriver::_Connect(DatabaseInfo *info, int *errcode, char *error, info->database, info->port, NULL, - 0) == NULL) + CLIENT_MULTI_STATEMENTS) == NULL) { if (errcode) { diff --git a/dlls/mysqlx/mysql/MysqlHeaders.h b/dlls/mysqlx/mysql/MysqlHeaders.h index c2fbef19..413c582c 100644 --- a/dlls/mysqlx/mysql/MysqlHeaders.h +++ b/dlls/mysqlx/mysql/MysqlHeaders.h @@ -19,6 +19,7 @@ #if defined WIN32 || defined _WIN32 #include #endif +typedef unsigned long ulong; #include #endif //_INCLUDE_SOURCEMOD_MYSQL_HEADERS_H diff --git a/dlls/mysqlx/mysql/MysqlQuery.cpp b/dlls/mysqlx/mysql/MysqlQuery.cpp index 9c3b9edd..cf446fd1 100644 --- a/dlls/mysqlx/mysql/MysqlQuery.cpp +++ b/dlls/mysqlx/mysql/MysqlQuery.cpp @@ -84,7 +84,9 @@ bool MysqlQuery::ExecuteR(QueryInfo *info, char *error, size_t maxlength) { snprintf(error, maxlength, "%s", mysql_error(m_pDatabase->m_pMysql)); } - } else { + } + else + { MYSQL_RES *res = mysql_store_result(m_pDatabase->m_pMysql); if (!res) { @@ -105,7 +107,7 @@ bool MysqlQuery::ExecuteR(QueryInfo *info, char *error, size_t maxlength) info->errorcode = 0; info->success = true; info->affected_rows = mysql_affected_rows(m_pDatabase->m_pMysql); - MysqlResultSet *rs = new MysqlResultSet(res); + MysqlResultSet *rs = new MysqlResultSet(res, m_pDatabase->m_pMysql); info->rs = rs; } } diff --git a/dlls/mysqlx/mysql/MysqlResultSet.cpp b/dlls/mysqlx/mysql/MysqlResultSet.cpp index d632ae13..f5181464 100644 --- a/dlls/mysqlx/mysql/MysqlResultSet.cpp +++ b/dlls/mysqlx/mysql/MysqlResultSet.cpp @@ -61,11 +61,12 @@ int MysqlResultRow::GetInt(unsigned int columnId) return atoi(GetStringSafe(columnId)); } -MysqlResultSet::MysqlResultSet(MYSQL_RES *res) : +MysqlResultSet::MysqlResultSet(MYSQL_RES *res, MYSQL *mysql) : m_pRes(res) { m_Rows = (unsigned int)mysql_num_rows(res); m_Columns = (unsigned int)mysql_num_fields(res); + m_pMySQL = mysql; if (m_Rows > 0) { @@ -77,7 +78,49 @@ MysqlResultSet::MysqlResultSet(MYSQL_RES *res) : MysqlResultSet::~MysqlResultSet() { + if (m_pRes == NULL) + { + return; + } + mysql_free_result(m_pRes); + while (mysql_next_result(m_pMySQL) == 0) + { + m_pRes = mysql_store_result(m_pMySQL); + if (m_pRes != NULL) + { + mysql_free_result(m_pRes); + } + } +} + +bool MysqlResultSet::NextResultSet() +{ + if (!mysql_more_results(m_pMySQL)) + { + return false; + } + + mysql_free_result(m_pRes); + if (mysql_next_result(m_pMySQL) != 0 + || (m_pRes = mysql_store_result(m_pMySQL)) == NULL) + { + m_Rows = 0; + m_pRes = NULL; + m_Columns = 0; + m_kRow.m_CurRow = NULL; + return false; + } + + m_Rows = (unsigned int)mysql_num_rows(m_pRes); + m_Columns = (unsigned int)mysql_num_fields(m_pRes); + + if (m_Rows > 0) + { + NextRow(); + } + + m_kRow.m_Columns = m_Columns; } void MysqlResultSet::FreeHandle() diff --git a/dlls/mysqlx/mysql/MysqlResultSet.h b/dlls/mysqlx/mysql/MysqlResultSet.h index b4aa8da1..5257b6c5 100644 --- a/dlls/mysqlx/mysql/MysqlResultSet.h +++ b/dlls/mysqlx/mysql/MysqlResultSet.h @@ -30,7 +30,7 @@ namespace SourceMod class MysqlResultSet : public IResultSet { public: - MysqlResultSet(MYSQL_RES *res); + MysqlResultSet(MYSQL_RES *res, MYSQL *mysql); ~MysqlResultSet(); public: void FreeHandle(); @@ -44,7 +44,9 @@ namespace SourceMod IResultRow *GetRow(); void NextRow(); void Rewind(); + bool NextResultSet(); private: + MYSQL *m_pMySQL; MYSQL_RES *m_pRes; MysqlResultRow m_kRow; unsigned int m_Columns; diff --git a/dlls/mysqlx/threading.cpp b/dlls/mysqlx/threading.cpp index e3b77741..30e6f066 100644 --- a/dlls/mysqlx/threading.cpp +++ b/dlls/mysqlx/threading.cpp @@ -566,6 +566,11 @@ void AtomicResult::Rewind() m_CurRow = 1; } +bool AtomicResult::NextResultSet() +{ + return false; +} + AMX_NATIVE_INFO g_ThreadSqlNatives[] = { {"SQL_ThreadQuery", SQL_ThreadQuery}, diff --git a/dlls/mysqlx/threading.h b/dlls/mysqlx/threading.h index 6a74f7af..b34e7eba 100644 --- a/dlls/mysqlx/threading.h +++ b/dlls/mysqlx/threading.h @@ -34,6 +34,7 @@ public: virtual IResultRow *GetRow(); virtual void NextRow(); virtual void Rewind(); + virtual bool NextResultSet(); public: virtual const char *GetString(unsigned int columnId); virtual const char *GetStringSafe(unsigned int columnId); diff --git a/dlls/sqlite/basic_sql.cpp b/dlls/sqlite/basic_sql.cpp index 54dfb961..eff42156 100644 --- a/dlls/sqlite/basic_sql.cpp +++ b/dlls/sqlite/basic_sql.cpp @@ -489,6 +489,34 @@ static cell AMX_NATIVE_CALL SQL_Rewind(AMX *amx, cell *params) 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) { IDatabase *pDb = (IDatabase *)GetHandle(params[1], Handle_Database); @@ -559,6 +587,7 @@ AMX_NATIVE_INFO g_BaseSqlNatives[] = {"SQL_Rewind", SQL_Rewind}, {"SQL_QuoteString", SQL_QuoteString}, {"SQL_QuoteStringFmt", SQL_QuoteStringFmt}, + {"SQL_NextResultSet", SQL_NextResultSet}, {NULL, NULL}, }; diff --git a/dlls/sqlite/sqlitepp/ISQLDriver.h b/dlls/sqlite/sqlitepp/ISQLDriver.h index 224feaf5..f0cb6a15 100644 --- a/dlls/sqlite/sqlitepp/ISQLDriver.h +++ b/dlls/sqlite/sqlitepp/ISQLDriver.h @@ -61,6 +61,9 @@ namespace SourceMod * Resets back to the first row. */ virtual void Rewind() =0; + + /* Always returns false in Sqlite */ + virtual bool NextResultSet() =0; }; struct QueryInfo diff --git a/dlls/sqlite/sqlitepp/SqliteResultSet.cpp b/dlls/sqlite/sqlitepp/SqliteResultSet.cpp index abeea73a..3cc81bd7 100644 --- a/dlls/sqlite/sqlitepp/SqliteResultSet.cpp +++ b/dlls/sqlite/sqlitepp/SqliteResultSet.cpp @@ -164,3 +164,8 @@ void SqliteResultSet::Rewind() m_CurRow = 1; m_CurIndex = (m_CurRow * m_Columns); } + +bool SqliteResultSet::NextResultSet() +{ + return false; +} diff --git a/dlls/sqlite/sqlitepp/SqliteResultSet.h b/dlls/sqlite/sqlitepp/SqliteResultSet.h index 07935a7c..18735c74 100644 --- a/dlls/sqlite/sqlitepp/SqliteResultSet.h +++ b/dlls/sqlite/sqlitepp/SqliteResultSet.h @@ -39,6 +39,7 @@ namespace SourceMod int GetInt(unsigned int columnId); bool IsNull(unsigned int columnId); const char *GetRaw(unsigned int columnId, size_t *length); + bool NextResultSet(); private: const char *GetStringSafe(unsigned int columnId); private: diff --git a/dlls/sqlite/threading.cpp b/dlls/sqlite/threading.cpp index 672808d1..fb4d9f0e 100644 --- a/dlls/sqlite/threading.cpp +++ b/dlls/sqlite/threading.cpp @@ -560,6 +560,11 @@ void AtomicResult::Rewind() m_CurRow = 1; } +bool AtomicResult::NextResultSet() +{ + return false; +} + AMX_NATIVE_INFO g_ThreadSqlNatives[] = { {"SQL_ThreadQuery", SQL_ThreadQuery}, diff --git a/dlls/sqlite/threading.h b/dlls/sqlite/threading.h index f05a386a..e5c62ed9 100644 --- a/dlls/sqlite/threading.h +++ b/dlls/sqlite/threading.h @@ -42,6 +42,7 @@ public: virtual int GetInt(unsigned int columnId); virtual bool IsNull(unsigned int columnId); virtual const char *GetRaw(unsigned int columnId, size_t *length); + virtual bool NextResultSet(); public: void CopyFrom(IResultSet *rs); private: diff --git a/plugins/include/sqlx.inc b/plugins/include/sqlx.inc index 60748630..8b86aa15 100644 --- a/plugins/include/sqlx.inc +++ b/plugins/include/sqlx.inc @@ -247,6 +247,20 @@ native SQL_SetAffinity(const driver[]); */ native SQL_GetQueryString(Handle:query, buffer[], maxlength); +/** + * For queries which return multiple result sets, this advances to the next + * result set if one is available. Otherwise, the current result set is + * destroyed and will no longer be accessible. + * + * This function will always return false on SQLite, and when using threaded + * queries in MySQL. Nonetheless, it has the same effect of removing the last + * result set. + * + * @param query Query Handle. + * @return True on success, false on failure. + */ +native bool:SQL_NextResultSet(Handle:query); + /** * This function can be used to find out if a table in a Sqlite database exists. * (updated for newer API)