-initial import of database natives + config

-initial import of completed database layer
-CreateIdentity now requires a non-optional second pointer.  This breaks backwards compatibility for CreateIdentity(), however, this is not a function extension authors are supposed to be calling

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40874
This commit is contained in:
David Anderson 2007-06-01 02:30:29 +00:00
parent d461239e74
commit e334e5f7e7
14 changed files with 1684 additions and 25 deletions

15
configs/databases.cfg Normal file
View File

@ -0,0 +1,15 @@
"Databases"
{
"driver_default" "mysql"
"default"
{
"driver" "default"
"host" "localhost"
"database" "sourcemod"
"user" "root"
"pass" ""
//"timeout" "0"
//"port" "0"
}
}

View File

@ -16,9 +16,23 @@
#include "HandleSys.h"
#include "ShareSys.h"
#include "sourcemod.h"
#include "sm_stringutil.h"
#include "TextParsers.h"
#include "Logger.h"
#include "ExtensionSys.h"
#include <stdlib.h>
#define DBPARSE_LEVEL_NONE 0
#define DBPARSE_LEVEL_MAIN 1
#define DBPARSE_LEVEL_DATABASE 2
DBManager g_DBMan;
DBManager::DBManager()
: m_StrTab(512), m_ParseLevel(0), m_ParseState(0), m_pDefault(NULL)
{
}
void DBManager::OnSourceModAllInitialized()
{
HandleAccess sec;
@ -31,6 +45,23 @@ void DBManager::OnSourceModAllInitialized()
m_DatabaseType = g_HandleSys.CreateType("IDatabase", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_ShareSys.AddInterface(NULL, this);
g_SourceMod.BuildPath(Path_SM, m_Filename, sizeof(m_Filename), "configs/databases.cfg");
}
void DBManager::OnSourceModLevelChange(const char *mapName)
{
SMCParseError err;
unsigned int line = 0;
if ((err = g_TextParser.ParseFile_SMC(m_Filename, this, &line, NULL)) != SMCParse_Okay)
{
g_Logger.LogError("[SM] Detected parse error(s) in file \"%s\"", m_Filename);
if (err != SMCParse_Custom)
{
const char *txt = g_TextParser.GetSMCErrorString(err);
g_Logger.LogError("[SM] Line %d: %s", line, txt);
}
}
}
void DBManager::OnSourceModShutdown()
@ -64,23 +95,173 @@ void DBManager::OnHandleDestroy(HandleType_t type, void *object)
}
}
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
void DBManager::ReadSMC_ParseStart()
{
const DatabaseInfo *pInfo = FindDatabaseConf(name);
m_confs.clear();
m_ParseLevel = 0;
m_ParseState = DBPARSE_LEVEL_NONE;
m_StrTab.Reset();
m_DefDriver.clear();
}
for (size_t i=0; i<m_drivers.size(); i++)
ConfDbInfo s_CurInfo;
SMCParseResult DBManager::ReadSMC_NewSection(const char *name, bool opt_quotes)
{
if (m_ParseLevel)
{
if (strcasecmp(pInfo->driver, m_drivers[i]->GetIdentifier()) == 0)
m_ParseLevel++;
return SMCParse_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_NONE)
{
if (strcmp(name, "Databases") == 0)
{
*pdr = m_drivers[i];
*pdb = m_drivers[i]->Connect(pInfo, persistent, error, maxlength);
return (*pdb == NULL);
m_ParseState = DBPARSE_LEVEL_MAIN;
} else {
m_ParseLevel++;
}
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
s_CurInfo = ConfDbInfo();
s_CurInfo.database = m_StrTab.AddString(name);
m_ParseState = DBPARSE_LEVEL_DATABASE;
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
m_ParseLevel++;
}
return SMCParse_Continue;
}
SMCParseResult DBManager::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes)
{
if (m_ParseLevel)
{
return SMCParse_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_MAIN)
{
if (strcmp(key, "driver_default") == 0)
{
m_DefDriver.assign(value);
}
} else if (m_ParseState == DBPARSE_LEVEL_DATABASE) {
if (strcmp(key, "driver") == 0)
{
if (strcmp(value, "default") != 0)
{
s_CurInfo.driver = m_StrTab.AddString(value);
}
} else if (strcmp(key, "database") == 0) {
s_CurInfo.database = m_StrTab.AddString(value);
} else if (strcmp(key, "host") == 0) {
s_CurInfo.host = m_StrTab.AddString(value);
} else if (strcmp(key, "user") == 0) {
s_CurInfo.user = m_StrTab.AddString(value);
} else if (strcmp(key, "pass") == 0) {
s_CurInfo.pass = m_StrTab.AddString(value);
} else if (strcmp(key, "timeout") == 0) {
s_CurInfo.info.maxTimeout = atoi(value);
} else if (strcmp(key, "port") == 0) {
s_CurInfo.info.port = atoi(value);
}
}
*pdr = NULL;
return SMCParse_Continue;
}
#define ASSIGN_VAR(var) \
if (s_CurInfo.var == -1) { \
s_CurInfo.info.var = ""; \
} else { \
s_CurInfo.info.var = m_StrTab.GetString(s_CurInfo.var); \
}
SMCParseResult DBManager::ReadSMC_LeavingSection()
{
if (m_ParseLevel)
{
m_ParseLevel--;
return SMCParse_Continue;
}
if (m_ParseState == DBPARSE_LEVEL_DATABASE)
{
/* Set all of the info members to either a blank string
* or the string pointer from the string table.
*/
ASSIGN_VAR(driver);
ASSIGN_VAR(database);
ASSIGN_VAR(host);
ASSIGN_VAR(user);
ASSIGN_VAR(pass);
/* Save it.. */
m_confs.push_back(s_CurInfo);
/* Go up one level */
m_ParseState = DBPARSE_LEVEL_MAIN;
} else if (m_ParseState == DBPARSE_LEVEL_MAIN) {
m_ParseState = DBPARSE_LEVEL_NONE;
return SMCParse_Halt;
}
return SMCParse_Continue;
}
#undef ASSIGN_VAR
void DBManager::ReadSMC_ParseEnd(bool halted, bool failed)
{
}
bool DBManager::Connect(const char *name, IDBDriver **pdr, IDatabase **pdb, bool persistent, char *error, size_t maxlength)
{
ConfDbInfo *pInfo = GetDatabaseConf(name);
if (!pInfo)
{
if (pdr)
{
*pdr = NULL;
}
*pdb = NULL;
UTIL_Format(error, maxlength, "Configuration \"%s\" not found", name);
return false;
}
if (!pInfo->realDriver)
{
/* Try to assign a real driver pointer */
if (pInfo->info.driver[0] == '\0')
{
if (!m_pDefault && m_DefDriver.size() > 0)
{
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
}
pInfo->realDriver = m_pDefault;
} else {
pInfo->realDriver = FindOrLoadDriver(pInfo->info.driver);
}
}
if (pInfo->realDriver)
{
if (pdr)
{
*pdr = pInfo->realDriver;
}
*pdb = pInfo->realDriver->Connect(&pInfo->info, persistent, error, maxlength);
return (*pdb != NULL);
}
if (pdr)
{
*pdr = NULL;
}
*pdb = NULL;
UTIL_Format(error, maxlength, "Driver \"%s\" not found", pInfo->driver);
return false;
}
@ -99,6 +280,27 @@ void DBManager::RemoveDriver(IDBDriver *pDriver)
return;
}
}
/* Make sure NOTHING references this! */
List<ConfDbInfo>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{
ConfDbInfo &db = (*iter);
if (db.realDriver == pDriver)
{
db.realDriver = NULL;
}
}
}
IDBDriver *DBManager::GetDefaultDriver()
{
if (!m_pDefault && m_DefDriver.size() > 0)
{
m_pDefault = FindOrLoadDriver(m_DefDriver.c_str());
}
return m_pDefault;
}
Handle_t DBManager::CreateHandle(DBHandleType dtype, void *ptr, IdentityToken_t *pToken)
@ -158,6 +360,58 @@ IDBDriver *DBManager::GetDriver(unsigned int index)
const DatabaseInfo *DBManager::FindDatabaseConf(const char *name)
{
/* :TODO: */
ConfDbInfo *info = GetDatabaseConf(name);
if (!info)
{
return NULL;
}
return &info->info;
}
ConfDbInfo *DBManager::GetDatabaseConf(const char *name)
{
List<ConfDbInfo>::iterator iter;
for (iter=m_confs.begin(); iter!=m_confs.end(); iter++)
{
ConfDbInfo &conf = (*iter);
if (strcmp(m_StrTab.GetString(conf.name), name) == 0)
{
return &conf;
}
}
return NULL;
}
IDBDriver *DBManager::FindOrLoadDriver(const char *name)
{
size_t last_size = m_drivers.size();
for (size_t i=0; i<last_size; i++)
{
if (strcmp(m_drivers[i]->GetIdentifier(), name) == 0)
{
return m_drivers[i];
}
}
char filename[PLATFORM_MAX_PATH];
UTIL_Format(filename, sizeof(filename), "dbi.%s", name);
IExtension *pExt = g_Extensions.LoadAutoExtension(filename);
if (!pExt || !pExt->IsLoaded() || m_drivers.size() <= last_size)
{
return NULL;
}
/* last_size is now gauranteed to be a valid index.
* The identifier must match the name.
*/
if (strcmp(m_drivers[last_size]->GetIdentifier(), name) == 0)
{
return m_drivers[last_size];
}
return NULL;
}

View File

@ -18,19 +18,43 @@
#include <IDBDriver.h>
#include "sm_globals.h"
#include <sh_vector.h>
#include <sh_string.h>
#include <sh_list.h>
#include <ITextParsers.h>
#include "sm_memtable.h"
using namespace SourceHook;
struct ConfDbInfo
{
ConfDbInfo() : name(-1), driver(-1), host(-1), user(-1), pass(-1),
database(-1), realDriver(NULL)
{
}
int name;
int driver;
int host;
int user;
int pass;
int database;
IDBDriver *realDriver;
DatabaseInfo info;
};
class DBManager :
public IDBManager,
public SMGlobalClass,
public IHandleTypeDispatch
public IHandleTypeDispatch,
public ITextListener_SMC
{
public:
DBManager();
public:
const char *GetInterfaceName();
unsigned int GetInterfaceVersion();
public: //SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModLevelChange(const char *mapName);
void OnSourceModShutdown();
public: //IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object);
@ -44,10 +68,27 @@ public: //IDBManager
Handle_t CreateHandle(DBHandleType type, void *ptr, IdentityToken_t *pToken);
HandleError ReadHandle(Handle_t hndl, DBHandleType type, void **ptr);
HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token);
public: //ITextListener_SMC
void ReadSMC_ParseStart();
SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes);
SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes);
SMCParseResult ReadSMC_LeavingSection();
void ReadSMC_ParseEnd(bool halted, bool failed);
public:
ConfDbInfo *GetDatabaseConf(const char *name);
IDBDriver *FindOrLoadDriver(const char *name);
IDBDriver *GetDefaultDriver();
private:
CVector<IDBDriver *> m_drivers;
List<ConfDbInfo> m_confs;
HandleType_t m_DriverType;
HandleType_t m_DatabaseType;
String m_DefDriver;
BaseStringTable m_StrTab;
char m_Filename[PLATFORM_MAX_PATH];
unsigned int m_ParseLevel;
unsigned int m_ParseState;
IDBDriver *m_pDefault;
};
extern DBManager g_DBMan;

View File

@ -796,6 +796,10 @@
RelativePath="..\smn_core.cpp"
>
</File>
<File
RelativePath="..\smn_database.cpp"
>
</File>
<File
RelativePath="..\smn_datapacks.cpp"
>

860
core/smn_database.cpp Normal file
View File

@ -0,0 +1,860 @@
#include "sm_globals.h"
#include "HandleSys.h"
#include "Database.h"
#include "ExtensionSys.h"
#include "PluginSys.h"
HandleType_t hQueryType;
HandleType_t hStmtType;
class DatabaseHelpers :
public SMGlobalClass,
public IHandleTypeDispatch
{
public:
virtual void OnSourceModAllInitialized()
{
HandleAccess acc;
/* Disable cloning */
g_HandleSys.InitAccessDefaults(NULL, &acc);
acc.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
TypeAccess tacc;
g_HandleSys.InitAccessDefaults(&tacc, NULL);
tacc.ident = g_pCoreIdent;
hQueryType = g_HandleSys.CreateType("IQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
hStmtType = g_HandleSys.CreateType("IPreparedQuery", this, hQueryType, &tacc, &acc, NULL, NULL);
}
virtual void OnSourceModShutdown()
{
g_HandleSys.RemoveType(hStmtType, g_pCoreIdent);
g_HandleSys.RemoveType(hQueryType, g_pCoreIdent);
}
virtual void OnHandleDestroy(HandleType_t type, void *object)
{
if (type == hQueryType)
{
IQuery *query = (IQuery *)object;
query->Destroy();
} else if (type == hStmtType) {
IPreparedQuery *query = (IPreparedQuery *)object;
query->Destroy();
}
}
} s_DatabaseNativeHelpers;
//is this safe for stmt handles? i think since it's single inheritance, it always will be.
inline HandleError ReadQueryHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query)
{
HandleSecurity sec;
sec.pOwner = pContext->GetIdentity();
sec.pIdentity = g_pCoreIdent;
return g_HandleSys.ReadHandle(hndl, hQueryType, &sec, (void **)query);
}
inline HandleError ReadStmtHndl(Handle_t hndl, IPluginContext *pContext, IPreparedQuery **query)
{
HandleSecurity sec;
sec.pOwner = pContext->GetIdentity();
sec.pIdentity = g_pCoreIdent;
return g_HandleSys.ReadHandle(hndl, hStmtType, &sec, (void **)query);
}
inline HandleError ReadDbOrStmtHndl(Handle_t hndl, IPluginContext *pContext, IDatabase **db, IPreparedQuery **query)
{
HandleError err;
if ((err = g_DBMan.ReadHandle(hndl, DBHandle_Database, (void **)db)) == HandleError_Type)
{
*db = NULL;
return ReadStmtHndl(hndl, pContext, query);
}
return err;
}
static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
{
char *conf, *err;
size_t maxlength = (size_t)params[4];
bool persistent = params[2] ? true : false;
pContext->LocalToString(params[1], &conf);
pContext->LocalToString(params[3], &err);
IDBDriver *driver;
IDatabase *db;
if (!g_DBMan.Connect(conf, &driver, &db, persistent, err, maxlength))
{
return BAD_HANDLE;
}
/* HACK! Add us to the dependency list */
CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
if (pExt)
{
g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
}
return db->GetHandle();
}
static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
{
IDBDriver *driver;
if (params[1] == BAD_HANDLE)
{
if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
{
return pContext->ThrowNativeError("Could not find any default driver");
}
} else {
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
}
}
char *host, *user, *pass, *database, *error;
size_t maxlength = (size_t)params[7];
bool persistent = params[8] ? true : false;
unsigned int port = params[9];
unsigned int maxTimeout = params[10];
pContext->LocalToString(params[2], &host);
pContext->LocalToString(params[3], &user);
pContext->LocalToString(params[4], &pass);
pContext->LocalToString(params[5], &database);
pContext->LocalToString(params[6], &error);
DatabaseInfo info;
info.database = database;
info.driver = driver->GetIdentifier();
info.host = host;
info.maxTimeout = maxTimeout;
info.pass = pass;
info.port = port;
info.user = user;
IDatabase *db = driver->Connect(&info, persistent, error, maxlength);
if (db)
{
/* HACK! Add us to the dependency list */
CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
if (pExt)
{
g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
}
return db->GetHandle();
}
return BAD_HANDLE;
}
static cell_t SQL_GetDriverIdent(IPluginContext *pContext, const cell_t *params)
{
IDBDriver *driver;
if (params[1] == BAD_HANDLE)
{
if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
{
return pContext->ThrowNativeError("Could not find any default driver");
}
} else {
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
}
}
pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
return 1;
}
static cell_t SQL_GetDriver(IPluginContext *pContext, const cell_t *params)
{
char *name;
pContext->LocalToString(params[1], &name);
IDBDriver *driver = NULL;
if (name[0] == '\0')
{
driver = g_DBMan.GetDefaultDriver();
} else {
driver = g_DBMan.FindOrLoadDriver(name);
}
return (driver != NULL) ? driver->GetHandle() : BAD_HANDLE;
}
static cell_t SQL_GetDriverProduct(IPluginContext *pContext, const cell_t *params)
{
IDBDriver *driver;
if (params[1] == BAD_HANDLE)
{
if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
{
return pContext->ThrowNativeError("Could not find any default driver");
}
} else {
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
}
}
pContext->StringToLocalUTF8(params[2], params[3], driver->GetProductName(), NULL);
return 1;
}
static cell_t SQL_GetAffectedRows(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
IPreparedQuery *stmt = NULL;
HandleError err;
if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
}
if (db)
{
return db->GetAffectedRows();
} else if (stmt) {
return stmt->GetAffectedRows();
}
return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
}
static cell_t SQL_GetInsertId(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
IPreparedQuery *stmt = NULL;
HandleError err;
if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
}
if (db)
{
return db->GetInsertID();
} else if (stmt) {
return stmt->GetInsertID();
}
return pContext->ThrowNativeError("Unknown error reading db/stmt handles");
}
static cell_t SQL_GetError(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
IPreparedQuery *stmt = NULL;
HandleError err;
if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
}
const char *error = "";
if (db)
{
error = db->GetError();
} else if (stmt) {
error = stmt->GetError();
}
if (error[0] == '\0')
{
return false;
}
pContext->StringToLocalUTF8(params[2], params[3], error, NULL);
return 1;
}
static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
}
char *input, *output;
size_t maxlength = (size_t)params[4];
pContext->LocalToString(params[2], &input);
pContext->LocalToString(params[3], &output);
size_t written;
bool s = db->QuoteString(input, output, maxlength, &written);
cell_t *addr;
pContext->LocalToPhysAddr(params[5], &addr);
*addr = (cell_t)written;
return s ? 1 : 0;
}
static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
}
char *query;
pContext->LocalToString(params[2], &query);
return db->DoSimpleQuery(query) ? 1 : 0;
}
static cell_t SQL_Query(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
}
char *query;
pContext->LocalToString(params[2], &query);
IQuery *qr = db->DoQuery(query);
if (!qr)
{
return BAD_HANDLE;
}
Handle_t hndl = g_HandleSys.CreateHandle(hQueryType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE)
{
qr->Destroy();
return BAD_HANDLE;
}
return hndl;
}
static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
{
IDatabase *db = NULL;
HandleError err;
if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
!= HandleError_None)
{
return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
}
char *query, *error;
size_t maxlength = (size_t)params[4];
pContext->LocalToString(params[2], &query);
pContext->LocalToString(params[3], &error);
IPreparedQuery *qr = db->PrepareQuery(query, error, maxlength);
if (!qr)
{
return BAD_HANDLE;
}
Handle_t hndl = g_HandleSys.CreateHandle(hStmtType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
if (hndl == BAD_HANDLE)
{
qr->Destroy();
return BAD_HANDLE;
}
return hndl;
}
static cell_t SQL_FetchMoreResults(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
return query->FetchMoreResults() ? 1 : 0;
}
static cell_t SQL_HasResultSet(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
return query->GetResultSet() != NULL ? true : false;
}
static cell_t SQL_GetRowCount(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return 0;
}
return rs->GetRowCount();
}
static cell_t SQL_GetFieldCount(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return 0;
}
return rs->GetFieldCount();
}
static cell_t SQL_FieldNumToName(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
unsigned int field = params[2];
const char *fldname;
if ((fldname = rs->FieldNumToName(field)) == NULL)
{
return pContext->ThrowNativeError("Invalid field index %d", field);
}
pContext->StringToLocalUTF8(params[3], params[4], fldname, NULL);
return 1;
}
static cell_t SQL_FieldNameToNum(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
char *field;
pContext->LocalToString(params[2], &field);
cell_t *num;
pContext->LocalToPhysAddr(params[3], &num);
return rs->FieldNameToNum(field, (unsigned int *)num) ? 1 : 0;
}
static cell_t SQL_FetchRow(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
return (rs->FetchRow() != NULL) ? true : false;
}
static cell_t SQL_MoreRows(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
return rs->MoreRows() ? 1 : 0;
}
static cell_t SQL_Rewind(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
return rs->Rewind() ? 1 : 0;
}
static cell_t SQL_FetchString(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
IResultRow *row = rs->CurrentRow();
if (!row)
{
return pContext->ThrowNativeError("Current result set has no fetched rows");
}
const char *str;
size_t length;
DBResult res = row->GetString(params[2], &str, &length);
if (res == DBVal_Error)
{
return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
} else if (res == DBVal_TypeMismatch) {
return pContext->ThrowNativeError("Could not fetch data in field %d as a string", params[2]);
}
pContext->StringToLocalUTF8(params[3], params[4], str, &length);
cell_t *addr;
pContext->LocalToPhysAddr(params[5], &addr);
*addr = (cell_t)res;
return (cell_t)length;
}
static cell_t SQL_FetchFloat(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
IResultRow *row = rs->CurrentRow();
if (!row)
{
return pContext->ThrowNativeError("Current result set has no fetched rows");
}
float f;
DBResult res = row->GetFloat(params[2], &f);
if (res == DBVal_Error)
{
return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
} else if (res == DBVal_TypeMismatch) {
return pContext->ThrowNativeError("Could not fetch data in field %d as a float", params[2]);
}
cell_t *addr;
pContext->LocalToPhysAddr(params[5], &addr);
*addr = (cell_t)res;
return sp_ftoc(f);
}
static cell_t SQL_FetchInt(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
IResultRow *row = rs->CurrentRow();
if (!row)
{
return pContext->ThrowNativeError("Current result set has no fetched rows");
}
int iv;
DBResult res = row->GetInt(params[2], &iv);
if (res == DBVal_Error)
{
return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
} else if (res == DBVal_TypeMismatch) {
return pContext->ThrowNativeError("Could not fetch data in field %d as an integer", params[2]);
}
cell_t *addr;
pContext->LocalToPhysAddr(params[5], &addr);
*addr = (cell_t)res;
return iv;
}
static cell_t SQL_IsFieldNull(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
IResultRow *row = rs->CurrentRow();
if (!row)
{
return pContext->ThrowNativeError("Current result set has no fetched rows");
}
if ((unsigned)params[2] >= rs->GetFieldCount())
{
return pContext->ThrowNativeError("Invalid field index %d", params[2]);
}
return row->IsNull(params[2]) ? 1 : 0;
}
static cell_t SQL_FetchSize(IPluginContext *pContext, const cell_t *params)
{
IQuery *query;
HandleError err;
if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
}
IResultSet *rs = query->GetResultSet();
if (!rs)
{
return pContext->ThrowNativeError("No current result set");
}
IResultRow *row = rs->CurrentRow();
if (!row)
{
return pContext->ThrowNativeError("Current result set has no fetched rows");
}
if ((unsigned)params[2] >= rs->GetFieldCount())
{
return pContext->ThrowNativeError("Invalid field index %d", params[2]);
}
return row->GetDataSize(params[2]);
}
static cell_t SQL_BindParamInt(IPluginContext *pContext, const cell_t *params)
{
IPreparedQuery *stmt;
HandleError err;
if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
}
if (!stmt->BindParamInt(params[2], params[3], params[4] ? true : false))
{
return pContext->ThrowNativeError("Could not bind parameter %d as an integer", params[2]);
}
return 1;
}
static cell_t SQL_BindParamFloat(IPluginContext *pContext, const cell_t *params)
{
IPreparedQuery *stmt;
HandleError err;
if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
}
if (!stmt->BindParamFloat(params[2], sp_ctof(params[3])))
{
return pContext->ThrowNativeError("Could not bind parameter %d as a float", params[2]);
}
return 1;
}
static cell_t SQL_BindParamString(IPluginContext *pContext, const cell_t *params)
{
IPreparedQuery *stmt;
HandleError err;
if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
}
char *str;
pContext->LocalToString(params[3], &str);
if (!stmt->BindParamString(params[2], str, params[4] ? true : false))
{
return pContext->ThrowNativeError("Could not bind parameter %d as a string", params[2]);
}
return 1;
}
static cell_t SQL_Execute(IPluginContext *pContext, const cell_t *params)
{
IPreparedQuery *stmt;
HandleError err;
if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
{
return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
}
return stmt->Execute() ? 1 : 0;
}
REGISTER_NATIVES(dbNatives)
{
{"SQL_BindParamInt", SQL_BindParamInt},
{"SQL_BindParamFloat", SQL_BindParamFloat},
{"SQL_BindParamString", SQL_BindParamString},
{"SQL_Connect", SQL_Connect},
{"SQL_ConnectEx", SQL_ConnectEx},
{"SQL_Execute", SQL_Execute},
{"SQL_FastQuery", SQL_FastQuery},
{"SQL_FetchFloat", SQL_FetchFloat},
{"SQL_FetchInt", SQL_FetchInt},
{"SQL_FetchMoreResults", SQL_FetchMoreResults},
{"SQL_FetchRow", SQL_FetchRow},
{"SQL_FetchSize", SQL_FetchSize},
{"SQL_FetchString", SQL_FetchString},
{"SQL_FieldNameToNum", SQL_FieldNameToNum},
{"SQL_FieldNumToName", SQL_FieldNumToName},
{"SQL_GetAffectedRows", SQL_GetAffectedRows},
{"SQL_GetDriver", SQL_GetDriver},
{"SQL_GetDriverIdent", SQL_GetDriverIdent},
{"SQL_GetDriverProduct", SQL_GetDriverProduct},
{"SQL_GetError", SQL_GetError},
{"SQL_GetFieldCount", SQL_GetFieldCount},
{"SQL_GetInsertId", SQL_GetInsertId},
{"SQL_GetRowCount", SQL_GetRowCount},
{"SQL_HasResultSet", SQL_HasResultSet},
{"SQL_IsFieldNull", SQL_IsFieldNull},
{"SQL_MoreRows", SQL_MoreRows},
{"SQL_Query", SQL_Query},
{"SQL_QuoteString", SQL_QuoteString},
{"SQL_PrepareQuery", SQL_PrepareQuery},
{"SQL_Rewind", SQL_Rewind},
{NULL, NULL},
};

View File

@ -70,7 +70,7 @@ CExtension::CExtension(const char *filename, char *error, size_t maxlength)
m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, maxlength);
}
m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this);
if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading()))
{
@ -132,7 +132,11 @@ void CExtension::MarkAllLoaded()
void CExtension::AddPlugin(IPlugin *pPlugin)
{
m_Plugins.push_back(pPlugin);
/* Unfortunately we have to do this :( */
if (m_Plugins.find(pPlugin) != m_Plugins.end())
{
m_Plugins.push_back(pPlugin);
}
}
void CExtension::RemovePlugin(IPlugin *pPlugin)
@ -866,3 +870,13 @@ void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argco
g_RootMenu.DrawGenericOption("list", "List extensions");
g_RootMenu.DrawGenericOption("unload", "Unload an extension");
}
CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr)
{
if (ptr->type == g_ExtType)
{
return (CExtension *)(ptr->ptr);
}
return NULL;
}

View File

@ -96,6 +96,8 @@ public:
void MarkAllLoaded();
void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload);
void TryAutoload();
public:
CExtension *GetExtensionFromIdent(IdentityToken_t *ptr);
private:
CExtension *FindByOrder(unsigned int num);
private:

View File

@ -91,7 +91,7 @@ void CPlugin::InitIdentity()
{
if (!m_handle)
{
m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
m_ctx.base->SetIdentity(m_ident);
}

View File

@ -34,7 +34,7 @@ IdentityToken_t *ShareSystem::CreateCoreIdentity()
m_CoreType = CreateIdentType("CORE");
}
return CreateIdentity(m_CoreType);
return CreateIdentity(m_CoreType, this);
}
void ShareSystem::OnSourceModStartup(bool late)
@ -96,7 +96,7 @@ void ShareSystem::OnHandleDestroy(HandleType_t type, void *object)
/* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */
}
IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type)
IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr)
{
if (!m_TypeRoot)
{
@ -110,6 +110,8 @@ IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type)
sec.pOwner = sec.pIdentity = GetIdentRoot();
pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true);
pToken->ptr = ptr;
pToken->type = type;
return pToken;
}

View File

@ -28,6 +28,8 @@ namespace SourceMod
struct IdentityToken_t
{
Handle_t ident;
void *ptr;
IdentityType_t type;
};
};
@ -53,7 +55,7 @@ public: //IShareSys
void AddNatives(IExtension *myself, const sp_nativeinfo_t *natives);
IdentityType_t CreateIdentType(const char *name);
IdentityType_t FindIdentType(const char *name);
IdentityToken_t *CreateIdentity(IdentityType_t type);
IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr);
void DestroyIdentType(IdentityType_t type);
void DestroyIdentity(IdentityToken_t *identity);
void AddDependency(IExtension *myself, const char *filename, bool require, bool autoload);

445
plugins/include/dbi.inc Normal file
View File

@ -0,0 +1,445 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is part of the SourceMod/SourcePawn SDK. This file may only be used
* or modified under the Terms and Conditions of its License Agreement, which is found
* in LICENSE.txt. The Terms and Conditions for making SourceMod extensions/plugins
* may change at any time. To view the latest information, see:
* http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#if defined _dbi_included
#endinput
#endif
#define _dbi_included
/**
* @handle Driver
*
* Contains information about an SQL driver.
*/
/**
* @handle Database
*
* Contains information about a database connection.
*/
/**
* @handle Query
*
* Contains information about an active query and its
* result sets.
*/
/**
* @handle Statement : Query
*
* Extends a Query Handle and can be used as a Query Handle.
* Statement Handles are for prepared queries and contain
* their own function for binding parameters. Statement
* Handles can be used instead of database Handles in a few
* select functions.
*/
/**
* Describes a database field fetch status.
*/
enum DBResult
{
DBVal_Error = 0, /**< Column number/field is invalid. */
DBVal_TypeMismatch = 1, /**< You cannot retrieve this data with this type. */
DBVal_Null = 2, /**< Field has no data (NULL) */
DBVal_Data = 3, /**< Field has data */
}
/**
* Creates an SQL connection from a named configuration.
*
* @param confname Named configuration.
* @param persistent True to re-use a previous persistent connection if
* possible, false otherwise.
* @param error Error buffer.
* @param maxlength Maximum length of the error buffer.
* @return A database connection Handle, or INVALID_HANDLE on failure.
*/
native Handle:SQL_Connect(const String:confname[], bool:persistent, String:error[], maxlength);
/**
* Creates a default SQL connection.
*
* @param error Error buffer.
* @param maxlength Maximum length of the error buffer.
* @param persistent True to re-use a previous persistent connection
* if possible, false otherwise.
* @return A database connection Handle, or INVALID_HANDLE on failure.
*/
stock Handle:SQL_DefConnect(String:error[], maxlength, bool:persistent=true)
{
return SQL_Connect("default", persistent, error, maxlength);
}
/**
* Creates an SQL connection from specific parameters.
*
* @param driver Driver Handle, or INVALID_HANDLE for default.
* @param host Host name.
* @param user User name.
* @param pass User password.
* @param database Database name.
* @param error Error buffer.
* @param maxlength Maximum length of the error buffer.
* @param persistent True to re-use a previous persistent connection
* if possible, false otherwise.
* @param port Optional port to specify.
* @param maxTimeout Maximum timeout in seconds if applicable.
* @return A database connection Handle, or INVALID_HANDLE on failure.
* @error Invalid driver Handle other than INVALID_HANDLE.
*/
native Handle:SQL_ConnectEx(Handle:driver,
const String:host[],
const String:user[],
const String:pass[],
const String:database[],
String:error[],
maxlength,
bool:persistent=true,
port=0,
maxTimeout=0);
/**
* Returns a driver Handle from a name string.
*
* If the driver is not found, SourceMod will attempt
* to load an extension named dbi.<name>.ext.[dll|so].
*
* @param name Driver identification string, or an empty
* string to return the default driver.
* @return Driver Handle, or INVALID_HANDLE on failure.
*/
native Handle:SQL_GetDriver(const String:name[]="");
/**
* Retrieves a driver's identification string.
*
* @param driver Driver Handle, or INVALID_HANDLE for the default driver.
* @param ident Identification string buffer.
* @param maxlength Maximum length of the buffer.
* @noreturn
* @error Invalid Handle other than INVALID_HANDLE.
*/
native SQL_GetDriverIdent(Handle:driver, String:ident[], maxlength);
/**
* Retrieves a driver's product string.
*
* @param driver Driver Handle, or INVALID_HANDLE for the default driver.
* @param product Product string buffer.
* @param maxlength Maximum length of the buffer.
* @noreturn
* @error Invalid Handle other than INVALID_HANDLE.
*/
native SQL_GetDriverProduct(Handle:driver, String:product[], maxlength);
/**
* Returns the number of affected rows from the last query.
*
* @param hndl A database OR statement Handle.
* @return Number of rows affected by the last query.
* @error Invalid database or statement Handle.
*/
native SQL_GetAffectedRows(Handle:hndl);
/**
* Returns the last query's insertion id.
*
* @param hndl A database OR statement Handle.
* @return Last query's insertion id.
* @error Invalid database or statement Handle.
*/
native SQL_GetInsertId(Handle:hndl);
/**
* Returns the error reported by the last query.
*
* @param hndl A database OR statement Handle.
* @param error Error buffer.
* @param maxlength Maximum length of the buffer.
* @return True if there was an error, false otherwise.
* @error Invalid database or statement Handle.
*/
native bool:SQL_GetError(Handle:hndl, String:error[], maxlength);
/**
* Quotes a database string for literal insertion. This is not needed
* for binding strings in prepared statements.
*
* @param hndl A database Handle.
* @param string String to quote.
* @param buffer Buffer to store quoted string in.
* @param maxlength Maximum length of the buffer.
* @param written Optionally returns the number of bytes written.
* @return True on success, false if buffer is not big enough.
* The buffer must be at least 2*strlen(string)+1.
* @error Invalid database or statement Handle.
*/
native bool:SQL_QuoteString(Handle:database, const String:string[], String:buffer[], maxlength, &written=0);
/**
* Executes a query and ignores the result set.
*
* @param database A database Handle.
* @param query Query string.
* @return True if query succeeded, false otherwise. Use
* SQL_GetError to find the last error.
* @error Invalid database Handle.
*/
native bool:SQL_FastQuery(Handle:database, const String:query[]);
/**
* Executes a simple query and returns a new query Handle for
* receiving the results.
*
* @param database A database Handle.
* @param query Query string.
* @return A new Query Handle on success, INVALID_HANDLE
* otherwise. The Handle must be freed with CloseHandle().
* @error Invalid database Handle.
*/
native Handle:SQL_Query(Handle:database, const String:query[]);
/**
* Creates a new prepared statement query. Prepared statements can
* be executed any number of times. They can also have placeholder
* parameters, similar to variables, which can be bound safely and
* securely (for example, you do not need to quote bound strings).
*
* Statement handles will work in any function that accepts a Query handle.
*
* @param database A database Handle.
* @param query Query string.
* @param error Error buffer.
* @param maxlength Maximum size of the error buffer.
* @return A new statement Handle on success, INVALID_HANDLE
* otherwise. The Handle must be freed with CloseHandle().
* @error Invalid database Handle.
*/
native Handle:SQL_PrepareQuery(Handle:database, const String:query[], String:error[], maxlength);
/**
* Advances to the next set of results.
*
* In some SQL implementations, multiple result sets can exist on one query.
* This is possible in MySQL with simple queries when executing a CALL
* query. If this is the case, all result sets must be processed before
* another query is made.
*
* @param query A query Handle.
* @return True if there was another result set, false otherwise.
* @error Invalid query Handle.
*/
native bool:SQL_FetchMoreResults(Handle:query);
/**
* Returns whether or not a result set exists. This will
* return true even if 0 results were returned, but false
* on queries like UPDATE, INSERT, or DELETE.
*
* @param query A query (or statement) Handle.
* @return True if there is a result set, false otherwise.
* @error Invalid query Handle.
*/
native bool:SQL_HasResultSet(Handle:query);
/**
* Retrieves the number of rows in the last result set.
*
* @param query A query (or statement) Handle.
* @error Invalid query Handle.
*/
native SQL_GetRowCount(Handle:query);
/**
* Retrieves the name of a field by index.
*
* @param query A query (or statement) Handle.
* @param field Field number (starting from 0).
* @param name Name buffer.
* @param maxlength Maximum length of the name buffer.
* @noreturn
* @error Invalid query Handle, invalid field index, or
* no current result set.
*/
native SQL_FieldNumToName(Handle:query, field, String:name[], maxlength);
/**
* Retrieves a field index by name.
*
* @param query A query (or statement) Handle.
* @param name Name of the field (case sensitive).
* @param field Variable to store field index in.
* @return True if found, false if not found.
* @error Invalid query Handle or no current result set.
*/
native bool:SQL_FieldNameToNum(Handle:query, const String:name[], &field);
/**
* Fetches a row from the current result set. This must be
* successfully called before any results are fetched.
*
* If this function fails, SQL_MoreResults() can be used to
* tell if there was an error or the result set is finished.
*
* @param query A query (or statement) Handle.
* @return True if a row was fetched, false otherwise.
* @error Invalid query Handle.
*/
native bool:SQL_FetchRow(Handle:query);
/**
* Returns if there are more rows.
*
* @param query A query (or statement) Handle.
* @return True if there are more rows, false otherwise.
* @error Invalid query Handle.
*/
native bool:SQL_MoreRows(Handle:query);
/**
* Rewinds a result set back to the first result.
*
* @param query A query (or statement) Handle.
* @return True on success, false otherwise.
* @error Invalid query Handle or no current result set.
*/
native bool:SQL_Rewind(Handle:query);
/**
* Fetches a string from a field in the current row of a result set.
* If the result is NULL, an empty string will be returned. A NULL
* check can be done with the result parameter, or SQL_IsFieldNull().
*
* @param query A query (or statement) Handle.
* @param field The field index (starting from 0).
* @param buffer String buffer.
* @param maxlength Maximum size of the string buffer.
* @param result Optional variable to store the status of the return value.
* @return Number of bytes written.
* @error Invalid query Handle or field index, invalid
* type conversion requested from the database,
* or no current result set.
*/
native SQL_FetchString(Handle:query, field, String:buffer[], maxlength, &DBResult:result=DBVal_Error);
/**
* Fetches a float from a field in the current row of a result set.
* If the result is NULL, a value of 0.0 will be returned. A NULL
* check can be done with the result parameter, or SQL_IsFieldNull().
*
* @param query A query (or statement) Handle.
* @param field The field index (starting from 0).
* @param result Optional variable to store the status of the return value.
* @return A float value.
* @error Invalid query Handle or field index, invalid
* type conversion requested from the database,
* or no current result set.
*/
native Float:SQL_FetchFloat(Handle:query, field, &DBResult:result=DBVal_Error);
/**
* Fetches an integer from a field in the current row of a result set.
* If the result is NULL, a value of 0 will be returned. A NULL
* check can be done with the result parameter, or SQL_IsFieldNull().
*
* @param query A query (or statement) Handle.
* @param field The field index (starting from 0).
* @param result Optional variable to store the status of the return value.
* @return An integer value.
* @error Invalid query Handle or field index, invalid
* type conversion requested from the database,
* or no current result set.
*/
native SQL_FetchInt(Handle:query, field, &DBResult:result=DBVal_Error);
/**
* Returns whether a field's data in the current row of a result set is
* NULL or not. NULL is an SQL type which means "no data."
*
* @param query A query (or statement) Handle.
* @param field The field index (starting from 0).
* @return True if data is NULL, false otherwise.
* @error Invalid query Handle or field index, or no
* current result set.
*/
native bool:SQL_IsFieldNull(Handle:query, field);
/**
* Returns the length of a field's data in the current row of a result
* set. This only needs to be called for strings to determine how many
* bytes to use. Note that the return value does not include the null
* terminator.
*
* @param query A query (or statement) Handle.
* @param field The field index (starting from 0).
* @return Number of bytes for the field's data size.
* @error Invalid query Handle or field index or no
* current result set.
*/
native SQL_FetchSize(Handle:query, field);
/**
* Binds a parameter in a prepared statement to a given integer value.
*
* @param statement A statement (prepared query) Handle.
* @param param The parameter index (starting from 0).
* @param number The number to bind.
* @param signed True to bind the number as signed, false to
* bind it as unsigned.
* @noreturn
* @error Invalid statement Handle or parameter index, or
* SQL error.
*/
native SQL_BindParamInt(Handle:statement, param, number, bool:signed=true);
/**
* Binds a parameter in a prepared statement to a given float value.
*
* @param statement A statement (prepared query) Handle.
* @param param The parameter index (starting from 0).
* @param float The float number to bind.
* @noreturn
* @error Invalid statement Handle or parameter index, or
* SQL error.
*/
native SQL_BindParamFloat(Handle:statement, param, Float:value);
/**
* Binds a parameter in a prepared statement to a given string value.
*
* @param statement A statement (prepared query) Handle.
* @param param The parameter index (starting from 0).
* @param value The string to bind.
* @param copy Whether or not SourceMod should copy the value
* locally if necessary. If the string contents
* won't change before calling SQL_Execute(), this
* can be set to false for optimization.
* @noreturn
* @error Invalid statement Handle or parameter index, or
* SQL error.
*/
native SQL_BindParamString(Handle:statement, param, const String:value[], bool:copy);
/**
* Executes a prepared statement. All parameters must be bound beforehand.
*
* @param statement A statement (prepared query) Handle.
* @return True on success, false on failure.
* @error Invalid statement Handle.
*/
native bool:SQL_Execute(Handle:statement);

View File

@ -44,6 +44,7 @@ struct Plugin
#include <events>
#include <functions>
#include <timers>
#include <dbi>
enum DialogType
{

View File

@ -24,7 +24,7 @@
#include <string.h>
#define SMINTERFACE_DBI_NAME "IDBI"
#define SMINTERFACE_DBI_VERSION 1
#define SMINTERFACE_DBI_VERSION 2
namespace SourceMod
{
@ -561,7 +561,8 @@ namespace SourceMod
virtual Handle_t GetHandle() =0;
/**
* @brief Returns the driver's controlling identity.
* @brief Returns the driver's controlling identity (must be the same
* as from IExtension::GetIdentity).
*
* @return An IdentityToken_t identity.
*/
@ -601,7 +602,8 @@ namespace SourceMod
virtual void RemoveDriver(IDBDriver *pDriver) =0;
/**
* @brief Searches for database info by name.
* @brief Searches for database info by name. Both the return pointer
* and all pointers contained therein should be considered volatile.
*
* @param name Named database info.
* @return DatabaseInfo pointer.
@ -648,7 +650,8 @@ namespace SourceMod
* @brief Creates a Handle_t of the IDBDriver type.
*
* @param type A DBHandleType value.
* @param ptr A pointer corrresponding to a DBHandleType object.
* @param ptr A pointer corrresponding to a DBHandleType
* object.
* @param token Identity pointer of the owning identity.
* @return A new Handle_t handle, or 0 on failure.
*/
@ -673,6 +676,22 @@ namespace SourceMod
* @return HandleError value.
*/
virtual HandleError ReleaseHandle(Handle_t hndl, DBHandleType type, IdentityToken_t *token) =0;
/**
* @brief Given a driver name, attempts to find it. If it is not found, SourceMod
* will attempt to load it.
*
* @param name Driver identifier name.
* @return IDBDriver pointer on success, NULL otherwise.
*/
virtual IDBDriver *FindOrLoadDriver(const char *driver) =0;
/**
* @brief Returns the default driver, or NULL if none is set.
*
* @return IDBDriver pointer on success, NULL otherwise.
*/
virtual IDBDriver *GetDefaultDriver() =0;
};
}

View File

@ -137,13 +137,14 @@ namespace SourceMod
virtual IdentityType_t FindIdentType(const char *name) =0;
/**
* @brief Creates a new identity token. This token is guaranteed to be unique
* amongst all other open identities.
* @brief Creates a new identity token. This token is guaranteed to be
* unique amongst all other open identities.
*
* @param type Identity type.
* @return A new IdentityToken_t identifier.
* @param ptr Private data pointer (cannot be NULL).
* @return A new IdentityToken_t pointer, or NULL on failure.
*/
virtual IdentityToken_t *CreateIdentity(IdentityType_t type) =0;
virtual IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr) =0;
/**
* @brief Destroys an identity type. Note that this will delete any identities
@ -161,7 +162,6 @@ namespace SourceMod
*/
virtual void DestroyIdentity(IdentityToken_t *identity) =0;
/**
* @brief Requires an extension. This tells SourceMod that without this extension,
* your extension should not be loaded. The name should not include the ".dll" or