1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/logic/smn_database.cpp Wed Oct 09 08:43:08 2013 -0400
1.3 @@ -0,0 +1,1463 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "common_logic.h"
1.36 +#include "Database.h"
1.37 +#include "ExtensionSys.h"
1.38 +#include "stringutil.h"
1.39 +#include "ISourceMod.h"
1.40 +
1.41 +HandleType_t hStmtType;
1.42 +
1.43 +HandleType_t hCombinedQueryType;
1.44 +typedef struct
1.45 +{
1.46 + IQuery *query;
1.47 + IDatabase *db;
1.48 +} CombinedQuery;
1.49 +
1.50 +class DatabaseHelpers :
1.51 + public SMGlobalClass,
1.52 + public IHandleTypeDispatch
1.53 +{
1.54 +public:
1.55 + virtual void OnSourceModAllInitialized()
1.56 + {
1.57 + HandleAccess acc;
1.58 +
1.59 + /* Disable cloning */
1.60 + handlesys->InitAccessDefaults(NULL, &acc);
1.61 + acc.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
1.62 +
1.63 + TypeAccess tacc;
1.64 +
1.65 + handlesys->InitAccessDefaults(&tacc, NULL);
1.66 + tacc.ident = g_pCoreIdent;
1.67 +
1.68 + hCombinedQueryType = handlesys->CreateType("IQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
1.69 + hStmtType = handlesys->CreateType("IPreparedQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
1.70 + }
1.71 +
1.72 + virtual void OnSourceModShutdown()
1.73 + {
1.74 + handlesys->RemoveType(hStmtType, g_pCoreIdent);
1.75 + handlesys->RemoveType(hCombinedQueryType, g_pCoreIdent);
1.76 + }
1.77 +
1.78 + virtual void OnHandleDestroy(HandleType_t type, void *object)
1.79 + {
1.80 + if (type == hCombinedQueryType)
1.81 + {
1.82 + CombinedQuery *combined = (CombinedQuery *)object;
1.83 + combined->query->Destroy();
1.84 + delete combined;
1.85 + } else if (type == hStmtType) {
1.86 + IPreparedQuery *query = (IPreparedQuery *)object;
1.87 + query->Destroy();
1.88 + }
1.89 + }
1.90 +} s_DatabaseNativeHelpers;
1.91 +
1.92 +//is this safe for stmt handles? i think since it's single inheritance, it always will be.
1.93 +inline HandleError ReadQueryHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query)
1.94 +{
1.95 + HandleSecurity sec;
1.96 + CombinedQuery *c;
1.97 + sec.pOwner = pContext->GetIdentity();
1.98 + sec.pIdentity = g_pCoreIdent;
1.99 +
1.100 + HandleError ret;
1.101 +
1.102 + if ((ret = handlesys->ReadHandle(hndl, hStmtType, &sec, (void **)query)) != HandleError_None)
1.103 + {
1.104 + ret = handlesys->ReadHandle(hndl, hCombinedQueryType, &sec, (void **)&c);
1.105 + if (ret == HandleError_None)
1.106 + {
1.107 + *query = c->query;
1.108 + }
1.109 + }
1.110 + return ret;
1.111 +}
1.112 +
1.113 +inline HandleError ReadQueryAndDbHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query, IDatabase **db)
1.114 +{
1.115 + HandleSecurity sec;
1.116 + CombinedQuery *c;
1.117 + sec.pOwner = pContext->GetIdentity();
1.118 + sec.pIdentity = g_pCoreIdent;
1.119 +
1.120 + HandleError ret = handlesys->ReadHandle(hndl, hCombinedQueryType, &sec, (void **)&c);
1.121 + if (ret == HandleError_None)
1.122 + {
1.123 + *query = c->query;
1.124 + *db = c->db;
1.125 + }
1.126 + return ret;
1.127 +}
1.128 +
1.129 +inline HandleError ReadStmtHndl(Handle_t hndl, IPluginContext *pContext, IPreparedQuery **query)
1.130 +{
1.131 + HandleSecurity sec;
1.132 + sec.pOwner = pContext->GetIdentity();
1.133 + sec.pIdentity = g_pCoreIdent;
1.134 +
1.135 + return handlesys->ReadHandle(hndl, hStmtType, &sec, (void **)query);
1.136 +}
1.137 +
1.138 +inline HandleError ReadDbOrStmtHndl(Handle_t hndl, IPluginContext *pContext, IDatabase **db, IPreparedQuery **query)
1.139 +{
1.140 + HandleError err;
1.141 + if ((err = g_DBMan.ReadHandle(hndl, DBHandle_Database, (void **)db)) == HandleError_Type)
1.142 + {
1.143 + *db = NULL;
1.144 + return ReadStmtHndl(hndl, pContext, query);
1.145 + }
1.146 + return err;
1.147 +}
1.148 +
1.149 +class TQueryOp : public IDBThreadOperation
1.150 +{
1.151 +public:
1.152 + TQueryOp(IDatabase *db, IPluginFunction *pf, const char *query, cell_t data) :
1.153 + m_pDatabase(db), m_pFunction(pf), m_Query(query), m_Data(data),
1.154 + me(scripts->FindPluginByContext(pf->GetParentContext()->GetContext())),
1.155 + m_pQuery(NULL)
1.156 + {
1.157 + /* We always increase the reference count because this is potentially
1.158 + * asynchronous. Otherwise the original handle could be closed while
1.159 + * we're still latched onto it.
1.160 + */
1.161 + m_pDatabase->IncReferenceCount();
1.162 +
1.163 + /* Now create our own Handle such that it can only be closed by us.
1.164 + * We allow cloning just in case someone wants to hold onto it.
1.165 + */
1.166 + HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
1.167 + HandleAccess access;
1.168 + handlesys->InitAccessDefaults(NULL, &access);
1.169 + access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
1.170 + m_MyHandle = handlesys->CreateHandleEx(g_DBMan.GetDatabaseType(),
1.171 + db,
1.172 + &sec,
1.173 + &access,
1.174 + NULL);
1.175 + }
1.176 + ~TQueryOp()
1.177 + {
1.178 + if (m_pQuery)
1.179 + {
1.180 + m_pQuery->Destroy();
1.181 + }
1.182 +
1.183 + /* Close our Handle if it's valid. */
1.184 + if (m_MyHandle != BAD_HANDLE)
1.185 + {
1.186 + HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
1.187 + handlesys->FreeHandle(m_MyHandle, &sec);
1.188 + } else {
1.189 + /* Otherwise, there is an open ref to the db */
1.190 + m_pDatabase->Close();
1.191 + }
1.192 + }
1.193 + IdentityToken_t *GetOwner()
1.194 + {
1.195 + return me->GetIdentity();
1.196 + }
1.197 + IDBDriver *GetDriver()
1.198 + {
1.199 + return m_pDatabase->GetDriver();
1.200 + }
1.201 + void RunThreadPart()
1.202 + {
1.203 + m_pDatabase->LockForFullAtomicOperation();
1.204 + m_pQuery = m_pDatabase->DoQuery(m_Query.c_str());
1.205 + if (!m_pQuery)
1.206 + {
1.207 + g_pSM->Format(error, sizeof(error), "%s", m_pDatabase->GetError());
1.208 + }
1.209 + m_pDatabase->UnlockFromFullAtomicOperation();
1.210 + }
1.211 + void CancelThinkPart()
1.212 + {
1.213 + m_pFunction->PushCell(BAD_HANDLE);
1.214 + m_pFunction->PushCell(BAD_HANDLE);
1.215 + m_pFunction->PushString("Driver is unloading");
1.216 + m_pFunction->PushCell(m_Data);
1.217 + m_pFunction->Execute(NULL);
1.218 + }
1.219 + void RunThinkPart()
1.220 + {
1.221 + /* Create a Handle for our query */
1.222 + HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
1.223 + HandleAccess access;
1.224 + handlesys->InitAccessDefaults(NULL, &access);
1.225 + access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
1.226 +
1.227 + Handle_t qh = BAD_HANDLE;
1.228 +
1.229 + if (m_pQuery)
1.230 + {
1.231 + CombinedQuery *c = new CombinedQuery;
1.232 + c->query = m_pQuery;
1.233 + c->db = m_pDatabase;
1.234 +
1.235 + qh = handlesys->CreateHandle(hCombinedQueryType, c, me->GetIdentity(), g_pCoreIdent, NULL);
1.236 + if (qh != BAD_HANDLE)
1.237 + {
1.238 + m_pQuery = NULL;
1.239 + } else {
1.240 + g_pSM->Format(error, sizeof(error), "Could not alloc handle");
1.241 + delete c;
1.242 + }
1.243 + }
1.244 +
1.245 + m_pFunction->PushCell(m_MyHandle);
1.246 + m_pFunction->PushCell(qh);
1.247 + m_pFunction->PushString(qh == BAD_HANDLE ? error : "");
1.248 + m_pFunction->PushCell(m_Data);
1.249 + m_pFunction->Execute(NULL);
1.250 +
1.251 + if (qh != BAD_HANDLE)
1.252 + {
1.253 + handlesys->FreeHandle(qh, &sec);
1.254 + }
1.255 + }
1.256 + void Destroy()
1.257 + {
1.258 + delete this;
1.259 + }
1.260 +private:
1.261 + IDatabase *m_pDatabase;
1.262 + IPluginFunction *m_pFunction;
1.263 + String m_Query;
1.264 + cell_t m_Data;
1.265 + IPlugin *me;
1.266 + IQuery *m_pQuery;
1.267 + char error[255];
1.268 + Handle_t m_MyHandle;
1.269 +};
1.270 +
1.271 +class TConnectOp : public IDBThreadOperation
1.272 +{
1.273 +public:
1.274 + TConnectOp(IPluginFunction *func, IDBDriver *driver, const char *_dbname, cell_t data)
1.275 + {
1.276 + m_pFunction = func;
1.277 + m_pDriver = driver;
1.278 + m_pDatabase = NULL;
1.279 + m_Data = data;
1.280 + error[0] = '\0';
1.281 + strncopy(dbname, _dbname, sizeof(dbname));
1.282 + me = scripts->FindPluginByContext(m_pFunction->GetParentContext()->GetContext());
1.283 + }
1.284 + IdentityToken_t *GetOwner()
1.285 + {
1.286 + return me->GetIdentity();
1.287 + }
1.288 + IDBDriver *GetDriver()
1.289 + {
1.290 + return m_pDriver;
1.291 + }
1.292 + void RunThreadPart()
1.293 + {
1.294 + g_DBMan.LockConfig();
1.295 + const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(dbname);
1.296 + if (!pInfo)
1.297 + {
1.298 + g_pSM->Format(error, sizeof(error), "Could not find database config \"%s\"", dbname);
1.299 + } else {
1.300 + m_pDatabase = m_pDriver->Connect(pInfo, false, error, sizeof(error));
1.301 + }
1.302 + g_DBMan.UnlockConfig();
1.303 + }
1.304 + void CancelThinkPart()
1.305 + {
1.306 + if (m_pDatabase)
1.307 + {
1.308 + m_pDatabase->Close();
1.309 + }
1.310 + m_pFunction->PushCell(BAD_HANDLE);
1.311 + m_pFunction->PushCell(BAD_HANDLE);
1.312 + m_pFunction->PushString("Driver is unloading");
1.313 + m_pFunction->PushCell(m_Data);
1.314 + m_pFunction->Execute(NULL);
1.315 + }
1.316 + void RunThinkPart()
1.317 + {
1.318 + Handle_t hndl = BAD_HANDLE;
1.319 +
1.320 + if (m_pDatabase)
1.321 + {
1.322 + if ((hndl = g_DBMan.CreateHandle(DBHandle_Database, m_pDatabase, me->GetIdentity()))
1.323 + == BAD_HANDLE)
1.324 + {
1.325 + m_pDatabase->Close();
1.326 + g_pSM->Format(error, sizeof(error), "Unable to allocate Handle");
1.327 + }
1.328 + }
1.329 +
1.330 + m_pFunction->PushCell(m_pDriver->GetHandle());
1.331 + m_pFunction->PushCell(hndl);
1.332 + m_pFunction->PushString(hndl == BAD_HANDLE ? error : "");
1.333 + m_pFunction->PushCell(m_Data);
1.334 + m_pFunction->Execute(NULL);
1.335 + }
1.336 + void Destroy()
1.337 + {
1.338 + delete this;
1.339 + }
1.340 +private:
1.341 + IPlugin *me;
1.342 + IPluginFunction *m_pFunction;
1.343 + IDBDriver *m_pDriver;
1.344 + IDatabase *m_pDatabase;
1.345 + char dbname[64];
1.346 + char error[255];
1.347 + cell_t m_Data;
1.348 +};
1.349 +
1.350 +static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
1.351 +{
1.352 + char *conf, *err;
1.353 +
1.354 + size_t maxlength = (size_t)params[4];
1.355 + bool persistent = params[2] ? true : false;
1.356 + pContext->LocalToString(params[1], &conf);
1.357 + pContext->LocalToString(params[3], &err);
1.358 +
1.359 + IDBDriver *driver;
1.360 + IDatabase *db;
1.361 + if (!g_DBMan.Connect(conf, &driver, &db, persistent, err, maxlength))
1.362 + {
1.363 + return BAD_HANDLE;
1.364 + }
1.365 +
1.366 + Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
1.367 + if (!hndl)
1.368 + {
1.369 + db->Close();
1.370 + return BAD_HANDLE;
1.371 + }
1.372 +
1.373 + /* HACK! Add us to the dependency list */
1.374 + IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
1.375 + if (pExt)
1.376 + {
1.377 + g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
1.378 + }
1.379 +
1.380 + return hndl;
1.381 +}
1.382 +
1.383 +static cell_t SQL_TConnect(IPluginContext *pContext, const cell_t *params)
1.384 +{
1.385 + IPluginFunction *pf = pContext->GetFunctionById(params[1]);
1.386 + if (!pf)
1.387 + {
1.388 + return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
1.389 + }
1.390 +
1.391 + char *conf;
1.392 + pContext->LocalToString(params[2], &conf);
1.393 +
1.394 + IDBDriver *driver = NULL;
1.395 + const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(conf);
1.396 + char error[255];
1.397 + if (pInfo != NULL)
1.398 + {
1.399 + if (pInfo->driver[0] == '\0')
1.400 + {
1.401 + driver = g_DBMan.GetDefaultDriver();
1.402 + } else {
1.403 + driver = g_DBMan.FindOrLoadDriver(pInfo->driver);
1.404 + }
1.405 + if (!driver)
1.406 + {
1.407 + g_pSM->Format(error,
1.408 + sizeof(error),
1.409 + "Could not find driver \"%s\"",
1.410 + pInfo->driver[0] == '\0' ? g_DBMan.GetDefaultDriverName() : pInfo->driver);
1.411 + } else if (!driver->IsThreadSafe()) {
1.412 + g_pSM->Format(error,
1.413 + sizeof(error),
1.414 + "Driver \"%s\" is not thread safe!",
1.415 + driver->GetIdentifier());
1.416 + }
1.417 + } else {
1.418 + g_pSM->Format(error, sizeof(error), "Could not find database conf \"%s\"", conf);
1.419 + }
1.420 +
1.421 + if (!pInfo || !driver)
1.422 + {
1.423 + pf->PushCell(BAD_HANDLE);
1.424 + pf->PushCell(BAD_HANDLE);
1.425 + pf->PushString(error);
1.426 + pf->PushCell(0);
1.427 + pf->Execute(NULL);
1.428 + return 0;
1.429 + }
1.430 +
1.431 + /* HACK! Add us to the dependency list */
1.432 + IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
1.433 + if (pExt)
1.434 + {
1.435 + g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
1.436 + }
1.437 +
1.438 + /* Finally, add to the thread if we can */
1.439 + TConnectOp *op = new TConnectOp(pf, driver, conf, params[3]);
1.440 + IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
1.441 + if (pPlugin->GetProperty("DisallowDBThreads", NULL)
1.442 + || !g_DBMan.AddToThreadQueue(op, PrioQueue_High))
1.443 + {
1.444 + /* Do everything right now */
1.445 + op->RunThreadPart();
1.446 + op->RunThinkPart();
1.447 + op->Destroy();
1.448 + }
1.449 +
1.450 + return 1;
1.451 +}
1.452 +
1.453 +static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
1.454 +{
1.455 + IDBDriver *driver;
1.456 + if (params[1] == BAD_HANDLE)
1.457 + {
1.458 + if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
1.459 + {
1.460 + return pContext->ThrowNativeError("Could not find any default driver");
1.461 + }
1.462 + } else {
1.463 + HandleError err;
1.464 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
1.465 + != HandleError_None)
1.466 + {
1.467 + return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
1.468 + }
1.469 + }
1.470 +
1.471 + char *host, *user, *pass, *database, *error;
1.472 + size_t maxlength = (size_t)params[7];
1.473 + bool persistent = params[8] ? true : false;
1.474 + unsigned int port = params[9];
1.475 + unsigned int maxTimeout = params[10];
1.476 + pContext->LocalToString(params[2], &host);
1.477 + pContext->LocalToString(params[3], &user);
1.478 + pContext->LocalToString(params[4], &pass);
1.479 + pContext->LocalToString(params[5], &database);
1.480 + pContext->LocalToString(params[6], &error);
1.481 +
1.482 + DatabaseInfo info;
1.483 + info.database = database;
1.484 + info.driver = driver->GetIdentifier();
1.485 + info.host = host;
1.486 + info.maxTimeout = maxTimeout;
1.487 + info.pass = pass;
1.488 + info.port = port;
1.489 + info.user = user;
1.490 +
1.491 + IDatabase *db = driver->Connect(&info, persistent, error, maxlength);
1.492 +
1.493 + if (db)
1.494 + {
1.495 + Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
1.496 + if (!hndl)
1.497 + {
1.498 + db->Close();
1.499 + return BAD_HANDLE;
1.500 + }
1.501 +
1.502 + /* HACK! Add us to the dependency list */
1.503 + IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
1.504 + if (pExt)
1.505 + {
1.506 + g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
1.507 + }
1.508 +
1.509 + return hndl;
1.510 + }
1.511 +
1.512 + return BAD_HANDLE;
1.513 +}
1.514 +
1.515 +static cell_t SQL_GetDriverIdent(IPluginContext *pContext, const cell_t *params)
1.516 +{
1.517 + IDBDriver *driver;
1.518 + if (params[1] == BAD_HANDLE)
1.519 + {
1.520 + if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
1.521 + {
1.522 + return pContext->ThrowNativeError("Could not find any default driver");
1.523 + }
1.524 + } else {
1.525 + HandleError err;
1.526 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
1.527 + != HandleError_None)
1.528 + {
1.529 + return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
1.530 + }
1.531 + }
1.532 +
1.533 + pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
1.534 +
1.535 + return 1;
1.536 +}
1.537 +
1.538 +static cell_t SQL_GetDriver(IPluginContext *pContext, const cell_t *params)
1.539 +{
1.540 + char *name;
1.541 + pContext->LocalToString(params[1], &name);
1.542 +
1.543 + IDBDriver *driver = NULL;
1.544 + if (name[0] == '\0')
1.545 + {
1.546 + driver = g_DBMan.GetDefaultDriver();
1.547 + } else {
1.548 + driver = g_DBMan.FindOrLoadDriver(name);
1.549 + }
1.550 +
1.551 + return (driver != NULL) ? driver->GetHandle() : BAD_HANDLE;
1.552 +}
1.553 +
1.554 +static cell_t SQL_GetDriverProduct(IPluginContext *pContext, const cell_t *params)
1.555 +{
1.556 + IDBDriver *driver;
1.557 + if (params[1] == BAD_HANDLE)
1.558 + {
1.559 + if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
1.560 + {
1.561 + return pContext->ThrowNativeError("Could not find any default driver");
1.562 + }
1.563 + } else {
1.564 + HandleError err;
1.565 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
1.566 + != HandleError_None)
1.567 + {
1.568 + return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
1.569 + }
1.570 + }
1.571 +
1.572 + pContext->StringToLocalUTF8(params[2], params[3], driver->GetProductName(), NULL);
1.573 +
1.574 + return 1;
1.575 +}
1.576 +
1.577 +static cell_t SQL_GetAffectedRows(IPluginContext *pContext, const cell_t *params)
1.578 +{
1.579 + IDatabase *db = NULL;
1.580 + IPreparedQuery *stmt = NULL;
1.581 + IQuery *query = NULL;
1.582 + HandleError err;
1.583 +
1.584 + if (((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
1.585 + && ((err = ReadQueryAndDbHndl(params[1], pContext, &query, &db)) != HandleError_None))
1.586 + {
1.587 + return pContext->ThrowNativeError("Invalid statement, db, or query Handle %x (error: %d)", params[1], err);
1.588 + }
1.589 +
1.590 +
1.591 + if (stmt)
1.592 + {
1.593 + return stmt->GetAffectedRows();
1.594 + }
1.595 + else if (query)
1.596 + {
1.597 + return db->GetAffectedRowsForQuery(query);
1.598 + }
1.599 + else if (db)
1.600 + {
1.601 + return db->GetAffectedRows();
1.602 + }
1.603 +
1.604 + return pContext->ThrowNativeError("Unknown error reading db/stmt/query handles");
1.605 +}
1.606 +
1.607 +static cell_t SQL_GetInsertId(IPluginContext *pContext, const cell_t *params)
1.608 +{
1.609 + IDatabase *db = NULL;
1.610 + IQuery *query = NULL;
1.611 + IPreparedQuery *stmt = NULL;
1.612 + HandleError err;
1.613 +
1.614 + if (((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
1.615 + && ((err = ReadQueryAndDbHndl(params[1], pContext, &query, &db)) != HandleError_None))
1.616 + {
1.617 + return pContext->ThrowNativeError("Invalid statement, db, or query Handle %x (error: %d)", params[1], err);
1.618 + }
1.619 +
1.620 + if (query)
1.621 + {
1.622 + return db->GetInsertIDForQuery(query);
1.623 + }
1.624 + else if (db)
1.625 + {
1.626 + return db->GetInsertID();
1.627 + }
1.628 + else if (stmt)
1.629 + {
1.630 + return stmt->GetInsertID();
1.631 + }
1.632 +
1.633 + return pContext->ThrowNativeError("Unknown error reading db/stmt/query handles");
1.634 +}
1.635 +
1.636 +static cell_t SQL_GetError(IPluginContext *pContext, const cell_t *params)
1.637 +{
1.638 + IDatabase *db = NULL;
1.639 + IPreparedQuery *stmt = NULL;
1.640 + HandleError err;
1.641 +
1.642 + if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
1.643 + {
1.644 + return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
1.645 + }
1.646 +
1.647 + const char *error = "";
1.648 + if (db)
1.649 + {
1.650 + error = db->GetError();
1.651 + } else if (stmt) {
1.652 + error = stmt->GetError();
1.653 + }
1.654 +
1.655 + if (error[0] == '\0')
1.656 + {
1.657 + return false;
1.658 + }
1.659 +
1.660 + pContext->StringToLocalUTF8(params[2], params[3], error, NULL);
1.661 +
1.662 + return 1;
1.663 +}
1.664 +
1.665 +static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params)
1.666 +{
1.667 + IDatabase *db = NULL;
1.668 + HandleError err;
1.669 +
1.670 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.671 + != HandleError_None)
1.672 + {
1.673 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.674 + }
1.675 +
1.676 + char *input, *output;
1.677 + size_t maxlength = (size_t)params[4];
1.678 + pContext->LocalToString(params[2], &input);
1.679 + pContext->LocalToString(params[3], &output);
1.680 +
1.681 + size_t written;
1.682 + bool s = db->QuoteString(input, output, maxlength, &written);
1.683 +
1.684 + cell_t *addr;
1.685 + pContext->LocalToPhysAddr(params[5], &addr);
1.686 + *addr = (cell_t)written;
1.687 +
1.688 + return s ? 1 : 0;
1.689 +}
1.690 +
1.691 +static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params)
1.692 +{
1.693 + IDatabase *db = NULL;
1.694 + HandleError err;
1.695 +
1.696 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.697 + != HandleError_None)
1.698 + {
1.699 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.700 + }
1.701 +
1.702 + char *query;
1.703 + pContext->LocalToString(params[2], &query);
1.704 +
1.705 + if (params[0] >= 3 && params[3] != -1)
1.706 + {
1.707 + return db->DoSimpleQueryEx(query, params[3]) ? 1 : 0;
1.708 + }
1.709 +
1.710 + return db->DoSimpleQuery(query) ? 1 : 0;
1.711 +}
1.712 +
1.713 +static cell_t SQL_Query(IPluginContext *pContext, const cell_t *params)
1.714 +{
1.715 + IDatabase *db = NULL;
1.716 + HandleError err;
1.717 +
1.718 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.719 + != HandleError_None)
1.720 + {
1.721 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.722 + }
1.723 +
1.724 + char *query;
1.725 + pContext->LocalToString(params[2], &query);
1.726 +
1.727 + IQuery *qr;
1.728 +
1.729 + if (params[0] >= 3 && params[3] != -1)
1.730 + {
1.731 + qr = db->DoQueryEx(query, params[3]);
1.732 + }
1.733 + else
1.734 + {
1.735 + qr = db->DoQuery(query);
1.736 + }
1.737 +
1.738 + if (!qr)
1.739 + {
1.740 + return BAD_HANDLE;
1.741 + }
1.742 +
1.743 + CombinedQuery *c = new CombinedQuery;
1.744 + c->query = qr;
1.745 + c->db = db;
1.746 + Handle_t hndl = handlesys->CreateHandle(hCombinedQueryType, c, pContext->GetIdentity(), g_pCoreIdent, NULL);
1.747 + if (hndl == BAD_HANDLE)
1.748 + {
1.749 + qr->Destroy();
1.750 + delete c;
1.751 + return BAD_HANDLE;
1.752 + }
1.753 +
1.754 + return hndl;
1.755 +}
1.756 +
1.757 +static cell_t SQL_TQuery(IPluginContext *pContext, const cell_t *params)
1.758 +{
1.759 + IDatabase *db = NULL;
1.760 + HandleError err;
1.761 +
1.762 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.763 + != HandleError_None)
1.764 + {
1.765 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.766 + }
1.767 +
1.768 + if (!db->GetDriver()->IsThreadSafe())
1.769 + {
1.770 + return pContext->ThrowNativeError("Driver \"%s\" is not thread safe!", db->GetDriver()->GetIdentifier());
1.771 + }
1.772 +
1.773 + IPluginFunction *pf = pContext->GetFunctionById(params[2]);
1.774 + if (!pf)
1.775 + {
1.776 + return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
1.777 + }
1.778 +
1.779 + char *query;
1.780 + pContext->LocalToString(params[3], &query);
1.781 +
1.782 + cell_t data = params[4];
1.783 + PrioQueueLevel level = PrioQueue_Normal;
1.784 + if (params[5] == (cell_t)PrioQueue_High)
1.785 + {
1.786 + level = PrioQueue_High;
1.787 + } else if (params[5] == (cell_t)PrioQueue_Low) {
1.788 + level = PrioQueue_Low;
1.789 + }
1.790 +
1.791 + IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
1.792 +
1.793 + TQueryOp *op = new TQueryOp(db, pf, query, data);
1.794 + if (pPlugin->GetProperty("DisallowDBThreads", NULL)
1.795 + || !g_DBMan.AddToThreadQueue(op, level))
1.796 + {
1.797 + /* Do everything right now */
1.798 + op->RunThreadPart();
1.799 + op->RunThinkPart();
1.800 + op->Destroy();
1.801 + }
1.802 +
1.803 + return 1;
1.804 +}
1.805 +
1.806 +static cell_t SQL_LockDatabase(IPluginContext *pContext, const cell_t *params)
1.807 +{
1.808 + IDatabase *db = NULL;
1.809 + HandleError err;
1.810 +
1.811 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.812 + != HandleError_None)
1.813 + {
1.814 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.815 + }
1.816 +
1.817 + db->LockForFullAtomicOperation();
1.818 +
1.819 + return 1;
1.820 +}
1.821 +
1.822 +static cell_t SQL_UnlockDatabase(IPluginContext *pContext, const cell_t *params)
1.823 +{
1.824 + IDatabase *db = NULL;
1.825 + HandleError err;
1.826 +
1.827 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.828 + != HandleError_None)
1.829 + {
1.830 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.831 + }
1.832 +
1.833 + db->UnlockFromFullAtomicOperation();
1.834 +
1.835 + return 1;
1.836 +}
1.837 +
1.838 +static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
1.839 +{
1.840 + IDatabase *db = NULL;
1.841 + HandleError err;
1.842 +
1.843 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.844 + != HandleError_None)
1.845 + {
1.846 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.847 + }
1.848 +
1.849 + char *query, *error;
1.850 + size_t maxlength = (size_t)params[4];
1.851 + pContext->LocalToString(params[2], &query);
1.852 + pContext->LocalToString(params[3], &error);
1.853 +
1.854 + IPreparedQuery *qr = db->PrepareQuery(query, error, maxlength);
1.855 +
1.856 + if (!qr)
1.857 + {
1.858 + return BAD_HANDLE;
1.859 + }
1.860 +
1.861 + Handle_t hndl = handlesys->CreateHandle(hStmtType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
1.862 + if (hndl == BAD_HANDLE)
1.863 + {
1.864 + qr->Destroy();
1.865 + return BAD_HANDLE;
1.866 + }
1.867 +
1.868 + return hndl;
1.869 +}
1.870 +
1.871 +static cell_t SQL_FetchMoreResults(IPluginContext *pContext, const cell_t *params)
1.872 +{
1.873 + IQuery *query;
1.874 + HandleError err;
1.875 +
1.876 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.877 + {
1.878 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.879 + }
1.880 +
1.881 + return query->FetchMoreResults() ? 1 : 0;
1.882 +}
1.883 +
1.884 +static cell_t SQL_HasResultSet(IPluginContext *pContext, const cell_t *params)
1.885 +{
1.886 + IQuery *query;
1.887 + HandleError err;
1.888 +
1.889 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.890 + {
1.891 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.892 + }
1.893 +
1.894 + return query->GetResultSet() != NULL ? true : false;
1.895 +}
1.896 +
1.897 +static cell_t SQL_GetRowCount(IPluginContext *pContext, const cell_t *params)
1.898 +{
1.899 + IQuery *query;
1.900 + HandleError err;
1.901 +
1.902 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.903 + {
1.904 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.905 + }
1.906 +
1.907 + IResultSet *rs = query->GetResultSet();
1.908 + if (!rs)
1.909 + {
1.910 + return 0;
1.911 + }
1.912 +
1.913 + return rs->GetRowCount();
1.914 +}
1.915 +
1.916 +static cell_t SQL_GetFieldCount(IPluginContext *pContext, const cell_t *params)
1.917 +{
1.918 + IQuery *query;
1.919 + HandleError err;
1.920 +
1.921 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.922 + {
1.923 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.924 + }
1.925 +
1.926 + IResultSet *rs = query->GetResultSet();
1.927 + if (!rs)
1.928 + {
1.929 + return 0;
1.930 + }
1.931 +
1.932 + return rs->GetFieldCount();
1.933 +}
1.934 +
1.935 +static cell_t SQL_FieldNumToName(IPluginContext *pContext, const cell_t *params)
1.936 +{
1.937 + IQuery *query;
1.938 + HandleError err;
1.939 +
1.940 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.941 + {
1.942 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.943 + }
1.944 +
1.945 + IResultSet *rs = query->GetResultSet();
1.946 + if (!rs)
1.947 + {
1.948 + return pContext->ThrowNativeError("No current result set");
1.949 + }
1.950 +
1.951 + unsigned int field = params[2];
1.952 +
1.953 + const char *fldname;
1.954 + if ((fldname = rs->FieldNumToName(field)) == NULL)
1.955 + {
1.956 + return pContext->ThrowNativeError("Invalid field index %d", field);
1.957 + }
1.958 +
1.959 + pContext->StringToLocalUTF8(params[3], params[4], fldname, NULL);
1.960 +
1.961 + return 1;
1.962 +}
1.963 +
1.964 +static cell_t SQL_FieldNameToNum(IPluginContext *pContext, const cell_t *params)
1.965 +{
1.966 + IQuery *query;
1.967 + HandleError err;
1.968 +
1.969 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.970 + {
1.971 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.972 + }
1.973 +
1.974 + IResultSet *rs = query->GetResultSet();
1.975 + if (!rs)
1.976 + {
1.977 + return pContext->ThrowNativeError("No current result set");
1.978 + }
1.979 +
1.980 + char *field;
1.981 + pContext->LocalToString(params[2], &field);
1.982 +
1.983 + cell_t *num;
1.984 + pContext->LocalToPhysAddr(params[3], &num);
1.985 +
1.986 + return rs->FieldNameToNum(field, (unsigned int *)num) ? 1 : 0;
1.987 +}
1.988 +
1.989 +static cell_t SQL_FetchRow(IPluginContext *pContext, const cell_t *params)
1.990 +{
1.991 + IQuery *query;
1.992 + HandleError err;
1.993 +
1.994 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.995 + {
1.996 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.997 + }
1.998 +
1.999 + IResultSet *rs = query->GetResultSet();
1.1000 + if (!rs)
1.1001 + {
1.1002 + return pContext->ThrowNativeError("No current result set");
1.1003 + }
1.1004 +
1.1005 + return (rs->FetchRow() != NULL) ? true : false;
1.1006 +}
1.1007 +
1.1008 +static cell_t SQL_MoreRows(IPluginContext *pContext, const cell_t *params)
1.1009 +{
1.1010 + IQuery *query;
1.1011 + HandleError err;
1.1012 +
1.1013 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1014 + {
1.1015 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1016 + }
1.1017 +
1.1018 + IResultSet *rs = query->GetResultSet();
1.1019 + if (!rs)
1.1020 + {
1.1021 + return pContext->ThrowNativeError("No current result set");
1.1022 + }
1.1023 +
1.1024 + return rs->MoreRows() ? 1 : 0;
1.1025 +}
1.1026 +
1.1027 +static cell_t SQL_Rewind(IPluginContext *pContext, const cell_t *params)
1.1028 +{
1.1029 + IQuery *query;
1.1030 + HandleError err;
1.1031 +
1.1032 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1033 + {
1.1034 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1035 + }
1.1036 +
1.1037 + IResultSet *rs = query->GetResultSet();
1.1038 + if (!rs)
1.1039 + {
1.1040 + return pContext->ThrowNativeError("No current result set");
1.1041 + }
1.1042 +
1.1043 + return rs->Rewind() ? 1 : 0;
1.1044 +}
1.1045 +
1.1046 +static cell_t SQL_FetchString(IPluginContext *pContext, const cell_t *params)
1.1047 +{
1.1048 + IQuery *query;
1.1049 + HandleError err;
1.1050 +
1.1051 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1052 + {
1.1053 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1054 + }
1.1055 +
1.1056 + IResultSet *rs = query->GetResultSet();
1.1057 + if (!rs)
1.1058 + {
1.1059 + return pContext->ThrowNativeError("No current result set");
1.1060 + }
1.1061 +
1.1062 + IResultRow *row = rs->CurrentRow();
1.1063 + if (!row)
1.1064 + {
1.1065 + return pContext->ThrowNativeError("Current result set has no fetched rows");
1.1066 + }
1.1067 +
1.1068 + const char *str;
1.1069 + size_t length;
1.1070 + DBResult res = row->GetString(params[2], &str, &length);
1.1071 +
1.1072 + if (res == DBVal_Error)
1.1073 + {
1.1074 + return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
1.1075 + } else if (res == DBVal_TypeMismatch) {
1.1076 + return pContext->ThrowNativeError("Could not fetch data in field %d as a string", params[2]);
1.1077 + }
1.1078 +
1.1079 + pContext->StringToLocalUTF8(params[3], params[4], str, &length);
1.1080 +
1.1081 + cell_t *addr;
1.1082 + pContext->LocalToPhysAddr(params[5], &addr);
1.1083 + *addr = (cell_t)res;
1.1084 +
1.1085 + return (cell_t)length;
1.1086 +}
1.1087 +
1.1088 +static cell_t SQL_FetchFloat(IPluginContext *pContext, const cell_t *params)
1.1089 +{
1.1090 + IQuery *query;
1.1091 + HandleError err;
1.1092 +
1.1093 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1094 + {
1.1095 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1096 + }
1.1097 +
1.1098 + IResultSet *rs = query->GetResultSet();
1.1099 + if (!rs)
1.1100 + {
1.1101 + return pContext->ThrowNativeError("No current result set");
1.1102 + }
1.1103 +
1.1104 + IResultRow *row = rs->CurrentRow();
1.1105 + if (!row)
1.1106 + {
1.1107 + return pContext->ThrowNativeError("Current result set has no fetched rows");
1.1108 + }
1.1109 +
1.1110 + float f;
1.1111 + DBResult res = row->GetFloat(params[2], &f);
1.1112 +
1.1113 + if (res == DBVal_Error)
1.1114 + {
1.1115 + return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
1.1116 + } else if (res == DBVal_TypeMismatch) {
1.1117 + return pContext->ThrowNativeError("Could not fetch data in field %d as a float", params[2]);
1.1118 + }
1.1119 +
1.1120 + cell_t *addr;
1.1121 + pContext->LocalToPhysAddr(params[3], &addr);
1.1122 + *addr = (cell_t)res;
1.1123 +
1.1124 + return sp_ftoc(f);
1.1125 +}
1.1126 +
1.1127 +static cell_t SQL_FetchInt(IPluginContext *pContext, const cell_t *params)
1.1128 +{
1.1129 + IQuery *query;
1.1130 + HandleError err;
1.1131 +
1.1132 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1133 + {
1.1134 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1135 + }
1.1136 +
1.1137 + IResultSet *rs = query->GetResultSet();
1.1138 + if (!rs)
1.1139 + {
1.1140 + return pContext->ThrowNativeError("No current result set");
1.1141 + }
1.1142 +
1.1143 + IResultRow *row = rs->CurrentRow();
1.1144 + if (!row)
1.1145 + {
1.1146 + return pContext->ThrowNativeError("Current result set has no fetched rows");
1.1147 + }
1.1148 +
1.1149 + int iv;
1.1150 + DBResult res = row->GetInt(params[2], &iv);
1.1151 +
1.1152 + if (res == DBVal_Error)
1.1153 + {
1.1154 + return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
1.1155 + } else if (res == DBVal_TypeMismatch) {
1.1156 + return pContext->ThrowNativeError("Could not fetch data in field %d as an integer", params[2]);
1.1157 + }
1.1158 +
1.1159 + cell_t *addr;
1.1160 + pContext->LocalToPhysAddr(params[3], &addr);
1.1161 + *addr = (cell_t)res;
1.1162 +
1.1163 + return iv;
1.1164 +}
1.1165 +
1.1166 +static cell_t SQL_IsFieldNull(IPluginContext *pContext, const cell_t *params)
1.1167 +{
1.1168 + IQuery *query;
1.1169 + HandleError err;
1.1170 +
1.1171 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1172 + {
1.1173 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1174 + }
1.1175 +
1.1176 + IResultSet *rs = query->GetResultSet();
1.1177 + if (!rs)
1.1178 + {
1.1179 + return pContext->ThrowNativeError("No current result set");
1.1180 + }
1.1181 +
1.1182 + IResultRow *row = rs->CurrentRow();
1.1183 + if (!row)
1.1184 + {
1.1185 + return pContext->ThrowNativeError("Current result set has no fetched rows");
1.1186 + }
1.1187 +
1.1188 + if ((unsigned)params[2] >= rs->GetFieldCount())
1.1189 + {
1.1190 + return pContext->ThrowNativeError("Invalid field index %d", params[2]);
1.1191 + }
1.1192 +
1.1193 + return row->IsNull(params[2]) ? 1 : 0;
1.1194 +}
1.1195 +
1.1196 +static cell_t SQL_FetchSize(IPluginContext *pContext, const cell_t *params)
1.1197 +{
1.1198 + IQuery *query;
1.1199 + HandleError err;
1.1200 +
1.1201 + if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
1.1202 + {
1.1203 + return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
1.1204 + }
1.1205 +
1.1206 + IResultSet *rs = query->GetResultSet();
1.1207 + if (!rs)
1.1208 + {
1.1209 + return pContext->ThrowNativeError("No current result set");
1.1210 + }
1.1211 +
1.1212 + IResultRow *row = rs->CurrentRow();
1.1213 + if (!row)
1.1214 + {
1.1215 + return pContext->ThrowNativeError("Current result set has no fetched rows");
1.1216 + }
1.1217 +
1.1218 + if ((unsigned)params[2] >= rs->GetFieldCount())
1.1219 + {
1.1220 + return pContext->ThrowNativeError("Invalid field index %d", params[2]);
1.1221 + }
1.1222 +
1.1223 + return row->GetDataSize(params[2]);
1.1224 +}
1.1225 +
1.1226 +static cell_t SQL_BindParamInt(IPluginContext *pContext, const cell_t *params)
1.1227 +{
1.1228 + IPreparedQuery *stmt;
1.1229 + HandleError err;
1.1230 +
1.1231 + if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
1.1232 + {
1.1233 + return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
1.1234 + }
1.1235 +
1.1236 + if (!stmt->BindParamInt(params[2], params[3], params[4] ? true : false))
1.1237 + {
1.1238 + return pContext->ThrowNativeError("Could not bind parameter %d as an integer", params[2]);
1.1239 + }
1.1240 +
1.1241 + return 1;
1.1242 +}
1.1243 +
1.1244 +static cell_t SQL_BindParamFloat(IPluginContext *pContext, const cell_t *params)
1.1245 +{
1.1246 + IPreparedQuery *stmt;
1.1247 + HandleError err;
1.1248 +
1.1249 + if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
1.1250 + {
1.1251 + return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
1.1252 + }
1.1253 +
1.1254 + if (!stmt->BindParamFloat(params[2], sp_ctof(params[3])))
1.1255 + {
1.1256 + return pContext->ThrowNativeError("Could not bind parameter %d as a float", params[2]);
1.1257 + }
1.1258 +
1.1259 + return 1;
1.1260 +}
1.1261 +
1.1262 +static cell_t SQL_BindParamString(IPluginContext *pContext, const cell_t *params)
1.1263 +{
1.1264 + IPreparedQuery *stmt;
1.1265 + HandleError err;
1.1266 +
1.1267 + if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
1.1268 + {
1.1269 + return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
1.1270 + }
1.1271 +
1.1272 + char *str;
1.1273 + pContext->LocalToString(params[3], &str);
1.1274 +
1.1275 + if (!stmt->BindParamString(params[2], str, params[4] ? true : false))
1.1276 + {
1.1277 + return pContext->ThrowNativeError("Could not bind parameter %d as a string", params[2]);
1.1278 + }
1.1279 +
1.1280 + return 1;
1.1281 +}
1.1282 +
1.1283 +static cell_t SQL_Execute(IPluginContext *pContext, const cell_t *params)
1.1284 +{
1.1285 + IPreparedQuery *stmt;
1.1286 + HandleError err;
1.1287 +
1.1288 + if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
1.1289 + {
1.1290 + return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
1.1291 + }
1.1292 +
1.1293 + return stmt->Execute() ? 1 : 0;
1.1294 +}
1.1295 +
1.1296 +static cell_t SQL_IsSameConnection(IPluginContext *pContext, const cell_t *params)
1.1297 +{
1.1298 + IDatabase *db1=NULL, *db2=NULL;
1.1299 + HandleError err;
1.1300 +
1.1301 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db1))
1.1302 + != HandleError_None)
1.1303 + {
1.1304 + return pContext->ThrowNativeError("Invalid database Handle 1/%x (error: %d)", params[1], err);
1.1305 + }
1.1306 +
1.1307 + if ((err = g_DBMan.ReadHandle(params[2], DBHandle_Database, (void **)&db2))
1.1308 + != HandleError_None)
1.1309 + {
1.1310 + return pContext->ThrowNativeError("Invalid database Handle 2/%x (error: %d)", params[2], err);
1.1311 + }
1.1312 +
1.1313 + return (db1 == db2) ? true : false;
1.1314 +}
1.1315 +
1.1316 +static cell_t SQL_ReadDriver(IPluginContext *pContext, const cell_t *params)
1.1317 +{
1.1318 + IDatabase *db1=NULL;
1.1319 + HandleError err;
1.1320 +
1.1321 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db1))
1.1322 + != HandleError_None)
1.1323 + {
1.1324 + return pContext->ThrowNativeError("Invalid database Handle 1/%x (error: %d)", params[1], err);
1.1325 + }
1.1326 +
1.1327 + IDBDriver *driver = db1->GetDriver();
1.1328 +
1.1329 + pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
1.1330 +
1.1331 + return driver->GetHandle();
1.1332 +}
1.1333 +
1.1334 +static cell_t SQL_CheckConfig(IPluginContext *pContext, const cell_t *params)
1.1335 +{
1.1336 + char *name;
1.1337 + pContext->LocalToString(params[1], &name);
1.1338 +
1.1339 + return (g_DBMan.FindDatabaseConf(name) != NULL) ? 1 : 0;
1.1340 +}
1.1341 +
1.1342 +static cell_t SQL_ConnectCustom(IPluginContext *pContext, const cell_t *params)
1.1343 +{
1.1344 + KeyValues *kv;
1.1345 + HandleError err;
1.1346 +
1.1347 + kv = g_pSM->ReadKeyValuesHandle(params[1], &err, false);
1.1348 + if (kv == NULL)
1.1349 + {
1.1350 + return pContext->ThrowNativeError("Invalid KeyValues handle %x (error: %d)",
1.1351 + params[1],
1.1352 + err);
1.1353 + }
1.1354 +
1.1355 + DatabaseInfo info = smcore.GetDBInfoFromKeyValues(kv);
1.1356 +
1.1357 + IDBDriver *driver;
1.1358 + if (info.driver[0] == '\0' || strcmp(info.driver, "default") == 0)
1.1359 + {
1.1360 + driver = g_DBMan.GetDefaultDriver();
1.1361 + }
1.1362 + else
1.1363 + {
1.1364 + driver = g_DBMan.FindOrLoadDriver(info.driver);
1.1365 + }
1.1366 +
1.1367 + if (driver == NULL)
1.1368 + {
1.1369 + char buffer[255];
1.1370 +
1.1371 + g_pSM->Format(buffer, sizeof(buffer), "Could not find driver \"%s\"", info.driver);
1.1372 + pContext->StringToLocalUTF8(params[2], params[3], buffer, NULL);
1.1373 +
1.1374 + return BAD_HANDLE;
1.1375 + }
1.1376 +
1.1377 + char *buffer;
1.1378 + IDatabase *db;
1.1379 +
1.1380 + pContext->LocalToString(params[2], &buffer);
1.1381 +
1.1382 + db = driver->Connect(&info, params[4] ? true : false, buffer, params[3]);
1.1383 + if (db == NULL)
1.1384 + {
1.1385 + return BAD_HANDLE;
1.1386 + }
1.1387 +
1.1388 + Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
1.1389 + if (!hndl)
1.1390 + {
1.1391 + db->Close();
1.1392 + return pContext->ThrowNativeError("Out of handles!");
1.1393 + }
1.1394 +
1.1395 + /* HACK! Add us to the dependency list */
1.1396 + IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
1.1397 + if (pExt)
1.1398 + {
1.1399 + g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
1.1400 + }
1.1401 +
1.1402 + return hndl;
1.1403 +}
1.1404 +
1.1405 +static cell_t SQL_SetCharset(IPluginContext *pContext, const cell_t *params)
1.1406 +{
1.1407 + IDatabase *db = NULL;
1.1408 + HandleError err;
1.1409 +
1.1410 + if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
1.1411 + != HandleError_None)
1.1412 + {
1.1413 + return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
1.1414 + }
1.1415 +
1.1416 + char *characterset;
1.1417 + pContext->LocalToString(params[2], &characterset);
1.1418 +
1.1419 + return db->SetCharacterSet(characterset);
1.1420 +}
1.1421 +
1.1422 +REGISTER_NATIVES(dbNatives)
1.1423 +{
1.1424 + {"SQL_BindParamInt", SQL_BindParamInt},
1.1425 + {"SQL_BindParamFloat", SQL_BindParamFloat},
1.1426 + {"SQL_BindParamString", SQL_BindParamString},\
1.1427 + {"SQL_CheckConfig", SQL_CheckConfig},
1.1428 + {"SQL_Connect", SQL_Connect},
1.1429 + {"SQL_ConnectEx", SQL_ConnectEx},
1.1430 + {"SQL_EscapeString", SQL_QuoteString},
1.1431 + {"SQL_Execute", SQL_Execute},
1.1432 + {"SQL_FastQuery", SQL_FastQuery},
1.1433 + {"SQL_FetchFloat", SQL_FetchFloat},
1.1434 + {"SQL_FetchInt", SQL_FetchInt},
1.1435 + {"SQL_FetchMoreResults", SQL_FetchMoreResults},
1.1436 + {"SQL_FetchRow", SQL_FetchRow},
1.1437 + {"SQL_FetchSize", SQL_FetchSize},
1.1438 + {"SQL_FetchString", SQL_FetchString},
1.1439 + {"SQL_FieldNameToNum", SQL_FieldNameToNum},
1.1440 + {"SQL_FieldNumToName", SQL_FieldNumToName},
1.1441 + {"SQL_GetAffectedRows", SQL_GetAffectedRows},
1.1442 + {"SQL_GetDriver", SQL_GetDriver},
1.1443 + {"SQL_GetDriverIdent", SQL_GetDriverIdent},
1.1444 + {"SQL_GetDriverProduct", SQL_GetDriverProduct},
1.1445 + {"SQL_GetError", SQL_GetError},
1.1446 + {"SQL_GetFieldCount", SQL_GetFieldCount},
1.1447 + {"SQL_GetInsertId", SQL_GetInsertId},
1.1448 + {"SQL_GetRowCount", SQL_GetRowCount},
1.1449 + {"SQL_HasResultSet", SQL_HasResultSet},
1.1450 + {"SQL_IsFieldNull", SQL_IsFieldNull},
1.1451 + {"SQL_IsSameConnection", SQL_IsSameConnection},
1.1452 + {"SQL_LockDatabase", SQL_LockDatabase},
1.1453 + {"SQL_MoreRows", SQL_MoreRows},
1.1454 + {"SQL_PrepareQuery", SQL_PrepareQuery},
1.1455 + {"SQL_Query", SQL_Query},
1.1456 + {"SQL_QuoteString", SQL_QuoteString},
1.1457 + {"SQL_ReadDriver", SQL_ReadDriver},
1.1458 + {"SQL_Rewind", SQL_Rewind},
1.1459 + {"SQL_TConnect", SQL_TConnect},
1.1460 + {"SQL_TQuery", SQL_TQuery},
1.1461 + {"SQL_UnlockDatabase", SQL_UnlockDatabase},
1.1462 + {"SQL_ConnectCustom", SQL_ConnectCustom},
1.1463 + {"SQL_SetCharset", SQL_SetCharset},
1.1464 + {NULL, NULL},
1.1465 +};
1.1466 +