core/logic/smn_database.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Wed May 21 17:25:30 2014 -0400 (2014-05-21)
changeset 4377 7a44be5b7162
parent 4138 da768ad428cf
permissions -rw-r--r--
Trigger build for hl2sdk-l4d changes.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "common_logic.h"
    33 #include "Database.h"
    34 #include "ExtensionSys.h"
    35 #include "stringutil.h"
    36 #include "ISourceMod.h"
    37 
    38 HandleType_t hStmtType;
    39 
    40 HandleType_t hCombinedQueryType;
    41 typedef struct
    42 {
    43 	IQuery *query;
    44 	IDatabase *db;
    45 } CombinedQuery;
    46 
    47 class DatabaseHelpers : 
    48 	public SMGlobalClass,
    49 	public IHandleTypeDispatch
    50 {
    51 public:
    52 	virtual void OnSourceModAllInitialized()
    53 	{
    54 		HandleAccess acc;
    55 
    56 		/* Disable cloning */
    57 		handlesys->InitAccessDefaults(NULL, &acc);
    58 		acc.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    59 
    60 		TypeAccess tacc;
    61 
    62 		handlesys->InitAccessDefaults(&tacc, NULL);
    63 		tacc.ident = g_pCoreIdent;
    64 
    65 		hCombinedQueryType = handlesys->CreateType("IQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
    66 		hStmtType = handlesys->CreateType("IPreparedQuery", this, 0, &tacc, &acc, g_pCoreIdent, NULL);
    67 	}
    68 
    69 	virtual void OnSourceModShutdown()
    70 	{
    71 		handlesys->RemoveType(hStmtType, g_pCoreIdent);
    72 		handlesys->RemoveType(hCombinedQueryType, g_pCoreIdent);
    73 	}
    74 
    75 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    76 	{
    77 		if (type == hCombinedQueryType)
    78 		{
    79 			CombinedQuery *combined = (CombinedQuery *)object;
    80 			combined->query->Destroy();
    81 			delete combined;
    82 		} else if (type == hStmtType) {
    83 			IPreparedQuery *query = (IPreparedQuery *)object;
    84 			query->Destroy();
    85 		}
    86 	}
    87 } s_DatabaseNativeHelpers;
    88 
    89 //is this safe for stmt handles? i think since it's single inheritance, it always will be.
    90 inline HandleError ReadQueryHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query)
    91 {
    92 	HandleSecurity sec;
    93 	CombinedQuery *c;
    94 	sec.pOwner = pContext->GetIdentity();
    95 	sec.pIdentity = g_pCoreIdent;
    96 
    97 	HandleError ret;
    98 	
    99 	if ((ret = handlesys->ReadHandle(hndl, hStmtType, &sec, (void **)query)) != HandleError_None)
   100 	{
   101 		ret = handlesys->ReadHandle(hndl, hCombinedQueryType, &sec, (void **)&c);
   102 		if (ret == HandleError_None)
   103 		{
   104 			*query = c->query;
   105 		}
   106 	}
   107 	return ret;
   108 }
   109 
   110 inline HandleError ReadQueryAndDbHndl(Handle_t hndl, IPluginContext *pContext, IQuery **query, IDatabase **db)
   111 {
   112 	HandleSecurity sec;
   113 	CombinedQuery *c;
   114 	sec.pOwner = pContext->GetIdentity();
   115 	sec.pIdentity = g_pCoreIdent;
   116 
   117 	HandleError ret = handlesys->ReadHandle(hndl, hCombinedQueryType, &sec, (void **)&c);
   118 	if (ret == HandleError_None)
   119 	{
   120 		*query = c->query;
   121 		*db = c->db;
   122 	}
   123 	return ret;
   124 }
   125 
   126 inline HandleError ReadStmtHndl(Handle_t hndl, IPluginContext *pContext, IPreparedQuery **query)
   127 {
   128 	HandleSecurity sec;
   129 	sec.pOwner = pContext->GetIdentity();
   130 	sec.pIdentity = g_pCoreIdent;
   131 
   132 	return handlesys->ReadHandle(hndl, hStmtType, &sec, (void **)query);
   133 }
   134 
   135 inline HandleError ReadDbOrStmtHndl(Handle_t hndl, IPluginContext *pContext, IDatabase **db, IPreparedQuery **query)
   136 {
   137 	HandleError err;
   138 	if ((err = g_DBMan.ReadHandle(hndl, DBHandle_Database, (void **)db)) == HandleError_Type)
   139 	{
   140 		*db = NULL;
   141 		return ReadStmtHndl(hndl, pContext, query);
   142 	}
   143 	return err;
   144 }
   145 
   146 class TQueryOp : public IDBThreadOperation
   147 {
   148 public:
   149 	TQueryOp(IDatabase *db, IPluginFunction *pf, const char *query, cell_t data) : 
   150 	  m_pDatabase(db), m_pFunction(pf), m_Query(query), m_Data(data),
   151 	  me(scripts->FindPluginByContext(pf->GetParentContext()->GetContext())),
   152 	  m_pQuery(NULL)
   153 	{
   154 		/* We always increase the reference count because this is potentially
   155 		 * asynchronous.  Otherwise the original handle could be closed while 
   156 		 * we're still latched onto it.
   157 		 */
   158 		m_pDatabase->IncReferenceCount();
   159 
   160 		/* Now create our own Handle such that it can only be closed by us.
   161 		 * We allow cloning just in case someone wants to hold onto it.
   162 		 */
   163 		HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
   164 		HandleAccess access;
   165 		handlesys->InitAccessDefaults(NULL, &access);
   166 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   167 		m_MyHandle = handlesys->CreateHandleEx(g_DBMan.GetDatabaseType(),
   168 			db,
   169 			&sec,
   170 			&access,
   171 			NULL);
   172 	}
   173 	~TQueryOp()
   174 	{
   175 		if (m_pQuery)
   176 		{
   177 			m_pQuery->Destroy();
   178 		}
   179 
   180 		/* Close our Handle if it's valid. */
   181 		if (m_MyHandle != BAD_HANDLE)
   182 		{
   183 			HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
   184 			handlesys->FreeHandle(m_MyHandle, &sec);
   185 		} else {
   186 			/* Otherwise, there is an open ref to the db */
   187 			m_pDatabase->Close();
   188 		}
   189 	}
   190 	IdentityToken_t *GetOwner()
   191 	{
   192 		return me->GetIdentity();
   193 	}
   194 	IDBDriver *GetDriver()
   195 	{
   196 		return m_pDatabase->GetDriver();
   197 	}
   198 	void RunThreadPart()
   199 	{
   200 		m_pDatabase->LockForFullAtomicOperation();
   201 		m_pQuery = m_pDatabase->DoQuery(m_Query.c_str());
   202 		if (!m_pQuery)
   203 		{
   204 			g_pSM->Format(error, sizeof(error), "%s", m_pDatabase->GetError());
   205 		}
   206 		m_pDatabase->UnlockFromFullAtomicOperation();
   207 	}
   208 	void CancelThinkPart()
   209 	{
   210 		m_pFunction->PushCell(BAD_HANDLE);
   211 		m_pFunction->PushCell(BAD_HANDLE);
   212 		m_pFunction->PushString("Driver is unloading");
   213 		m_pFunction->PushCell(m_Data);
   214 		m_pFunction->Execute(NULL);
   215 	}
   216 	void RunThinkPart()
   217 	{
   218 		/* Create a Handle for our query */
   219 		HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
   220 		HandleAccess access;
   221 		handlesys->InitAccessDefaults(NULL, &access);
   222 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   223 
   224 		Handle_t qh = BAD_HANDLE;
   225 		
   226 		if (m_pQuery)
   227 		{
   228 			CombinedQuery *c = new CombinedQuery;
   229 			c->query = m_pQuery;
   230 			c->db = m_pDatabase;
   231 			
   232 			qh = handlesys->CreateHandle(hCombinedQueryType, c, me->GetIdentity(), g_pCoreIdent, NULL);
   233 			if (qh != BAD_HANDLE)
   234 			{
   235 				m_pQuery = NULL;
   236 			} else {
   237 				g_pSM->Format(error, sizeof(error), "Could not alloc handle");
   238 				delete c;
   239 			}
   240 		}
   241 
   242 		m_pFunction->PushCell(m_MyHandle);
   243 		m_pFunction->PushCell(qh);
   244 		m_pFunction->PushString(qh == BAD_HANDLE ? error : "");
   245 		m_pFunction->PushCell(m_Data);
   246 		m_pFunction->Execute(NULL);
   247 
   248 		if (qh != BAD_HANDLE)
   249 		{
   250 			handlesys->FreeHandle(qh, &sec);
   251 		}
   252 	}
   253 	void Destroy()
   254 	{
   255 		delete this;
   256 	}
   257 private:
   258 	IDatabase *m_pDatabase;
   259 	IPluginFunction *m_pFunction;
   260 	String m_Query;
   261 	cell_t m_Data;
   262 	IPlugin *me;
   263 	IQuery *m_pQuery;
   264 	char error[255];
   265 	Handle_t m_MyHandle;
   266 };
   267 
   268 class TConnectOp : public IDBThreadOperation
   269 {
   270 public:
   271 	TConnectOp(IPluginFunction *func, IDBDriver *driver, const char *_dbname, cell_t data)
   272 	{
   273 		m_pFunction = func;
   274 		m_pDriver = driver;
   275 		m_pDatabase = NULL;
   276 		m_Data = data;
   277 		error[0] = '\0';
   278 		strncopy(dbname, _dbname, sizeof(dbname));
   279 		me = scripts->FindPluginByContext(m_pFunction->GetParentContext()->GetContext());
   280 	}
   281 	IdentityToken_t *GetOwner()
   282 	{
   283 		return me->GetIdentity();
   284 	}
   285 	IDBDriver *GetDriver()
   286 	{
   287 		return m_pDriver;
   288 	}
   289 	void RunThreadPart()
   290 	{
   291 		g_DBMan.LockConfig();
   292 		const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(dbname);
   293 		if (!pInfo)
   294 		{
   295 			g_pSM->Format(error, sizeof(error), "Could not find database config \"%s\"", dbname);
   296 		} else {
   297 			m_pDatabase = m_pDriver->Connect(pInfo, false, error, sizeof(error));
   298 		}
   299 		g_DBMan.UnlockConfig();
   300 	}
   301 	void CancelThinkPart()
   302 	{
   303 		if (m_pDatabase)
   304 		{
   305 			m_pDatabase->Close();
   306 		}
   307 		m_pFunction->PushCell(BAD_HANDLE);
   308 		m_pFunction->PushCell(BAD_HANDLE);
   309 		m_pFunction->PushString("Driver is unloading");
   310 		m_pFunction->PushCell(m_Data);
   311 		m_pFunction->Execute(NULL);
   312 	}
   313 	void RunThinkPart()
   314 	{
   315 		Handle_t hndl = BAD_HANDLE;
   316 		
   317 		if (m_pDatabase)
   318 		{
   319 			if ((hndl = g_DBMan.CreateHandle(DBHandle_Database, m_pDatabase, me->GetIdentity()))
   320 				== BAD_HANDLE)
   321 			{
   322 				m_pDatabase->Close();
   323 				g_pSM->Format(error, sizeof(error), "Unable to allocate Handle");
   324 			}
   325 		}
   326 
   327 		m_pFunction->PushCell(m_pDriver->GetHandle());
   328 		m_pFunction->PushCell(hndl);
   329 		m_pFunction->PushString(hndl == BAD_HANDLE ? error : "");
   330 		m_pFunction->PushCell(m_Data);
   331 		m_pFunction->Execute(NULL);
   332 	}
   333 	void Destroy()
   334 	{
   335 		delete this;
   336 	}
   337 private:
   338 	IPlugin *me;
   339 	IPluginFunction *m_pFunction;
   340 	IDBDriver *m_pDriver;
   341 	IDatabase *m_pDatabase;
   342 	char dbname[64];
   343 	char error[255];
   344 	cell_t m_Data;
   345 };
   346 
   347 static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
   348 {
   349 	char *conf, *err;
   350 
   351 	size_t maxlength = (size_t)params[4];
   352 	bool persistent = params[2] ? true : false;
   353 	pContext->LocalToString(params[1], &conf);
   354 	pContext->LocalToString(params[3], &err);
   355 	
   356 	IDBDriver *driver;
   357 	IDatabase *db;
   358 	if (!g_DBMan.Connect(conf, &driver, &db, persistent, err, maxlength))
   359 	{
   360 		return BAD_HANDLE;
   361 	}
   362 
   363 	Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
   364 	if (!hndl)
   365 	{
   366 		db->Close();
   367 		return BAD_HANDLE;
   368 	}
   369 
   370 	/* HACK! Add us to the dependency list */
   371 	IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   372 	if (pExt)
   373 	{
   374 		g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
   375 	}
   376 
   377 	return hndl;
   378 }
   379 
   380 static cell_t SQL_TConnect(IPluginContext *pContext, const cell_t *params)
   381 {
   382 	IPluginFunction *pf = pContext->GetFunctionById(params[1]);
   383 	if (!pf)
   384 	{
   385 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   386 	}
   387 
   388 	char *conf;
   389 	pContext->LocalToString(params[2], &conf);
   390 
   391 	IDBDriver *driver = NULL;
   392 	const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(conf);
   393 	char error[255];
   394 	if (pInfo != NULL)
   395 	{
   396 		if (pInfo->driver[0] == '\0')
   397 		{
   398 			driver = g_DBMan.GetDefaultDriver();
   399 		} else {
   400 			driver = g_DBMan.FindOrLoadDriver(pInfo->driver);
   401 		}
   402 		if (!driver)
   403 		{
   404 			g_pSM->Format(error, 
   405 				sizeof(error), 
   406 				"Could not find driver \"%s\"", 
   407 				pInfo->driver[0] == '\0' ? g_DBMan.GetDefaultDriverName() : pInfo->driver);
   408 		} else if (!driver->IsThreadSafe()) {
   409 			g_pSM->Format(error,
   410 				sizeof(error),
   411 				"Driver \"%s\" is not thread safe!",
   412 				driver->GetIdentifier());
   413 		}
   414 	} else {
   415 		g_pSM->Format(error, sizeof(error), "Could not find database conf \"%s\"", conf);
   416 	}
   417 
   418 	if (!pInfo || !driver)
   419 	{
   420 		pf->PushCell(BAD_HANDLE);
   421 		pf->PushCell(BAD_HANDLE);
   422 		pf->PushString(error);
   423 		pf->PushCell(0);
   424 		pf->Execute(NULL);
   425 		return 0;
   426 	}
   427 
   428 	/* HACK! Add us to the dependency list */
   429 	IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   430 	if (pExt)
   431 	{
   432 		g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
   433 	}
   434 
   435 	/* Finally, add to the thread if we can */
   436 	TConnectOp *op = new TConnectOp(pf, driver, conf, params[3]);
   437 	IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
   438 	if (pPlugin->GetProperty("DisallowDBThreads", NULL)
   439 		|| !g_DBMan.AddToThreadQueue(op, PrioQueue_High))
   440 	{
   441 		/* Do everything right now */
   442 		op->RunThreadPart();
   443 		op->RunThinkPart();
   444 		op->Destroy();
   445 	}
   446 
   447 	return 1;
   448 }
   449 
   450 static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
   451 {
   452 	IDBDriver *driver;
   453 	if (params[1] == BAD_HANDLE)
   454 	{
   455 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   456 		{
   457 			return pContext->ThrowNativeError("Could not find any default driver");
   458 		}
   459 	} else {
   460 		HandleError err;
   461 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   462 			!= HandleError_None)
   463 		{
   464 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   465 		}
   466 	}
   467  
   468 	char *host, *user, *pass, *database, *error;
   469 	size_t maxlength = (size_t)params[7];
   470 	bool persistent = params[8] ? true : false;
   471 	unsigned int port = params[9];
   472 	unsigned int maxTimeout = params[10];
   473 	pContext->LocalToString(params[2], &host);
   474 	pContext->LocalToString(params[3], &user);
   475 	pContext->LocalToString(params[4], &pass);
   476 	pContext->LocalToString(params[5], &database);
   477 	pContext->LocalToString(params[6], &error);
   478 
   479 	DatabaseInfo info;
   480 	info.database = database;
   481 	info.driver = driver->GetIdentifier();
   482 	info.host = host;
   483 	info.maxTimeout = maxTimeout;
   484 	info.pass = pass;
   485 	info.port = port;
   486 	info.user = user;
   487 	
   488 	IDatabase *db = driver->Connect(&info, persistent, error, maxlength);
   489 
   490 	if (db)
   491 	{
   492 		Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
   493 		if (!hndl)
   494 		{
   495 			db->Close();
   496 			return BAD_HANDLE;
   497 		}
   498 
   499 		/* HACK! Add us to the dependency list */
   500 		IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   501 		if (pExt)
   502 		{
   503 			g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
   504 		}
   505 
   506 		return hndl;
   507 	}
   508 	
   509 	return BAD_HANDLE;
   510 }
   511 
   512 static cell_t SQL_GetDriverIdent(IPluginContext *pContext, const cell_t *params)
   513 {
   514 	IDBDriver *driver;
   515 	if (params[1] == BAD_HANDLE)
   516 	{
   517 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   518 		{
   519 			return pContext->ThrowNativeError("Could not find any default driver");
   520 		}
   521 	} else {
   522 		HandleError err;
   523 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   524 			!= HandleError_None)
   525 		{
   526 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   527 		}
   528 	}
   529 
   530 	pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
   531 
   532 	return 1;
   533 }
   534 
   535 static cell_t SQL_GetDriver(IPluginContext *pContext, const cell_t *params)
   536 {
   537 	char *name;
   538 	pContext->LocalToString(params[1], &name);
   539 
   540 	IDBDriver *driver = NULL;
   541 	if (name[0] == '\0')
   542 	{
   543 		driver = g_DBMan.GetDefaultDriver();
   544 	} else {
   545 		driver = g_DBMan.FindOrLoadDriver(name);
   546 	}
   547 
   548 	return (driver != NULL) ? driver->GetHandle() : BAD_HANDLE;
   549 }
   550 
   551 static cell_t SQL_GetDriverProduct(IPluginContext *pContext, const cell_t *params)
   552 {
   553 	IDBDriver *driver;
   554 	if (params[1] == BAD_HANDLE)
   555 	{
   556 		if ((driver = g_DBMan.GetDefaultDriver()) == NULL)
   557 		{
   558 			return pContext->ThrowNativeError("Could not find any default driver");
   559 		}
   560 	} else {
   561 		HandleError err;
   562 		if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Driver, (void **)&driver))
   563 			!= HandleError_None)
   564 		{
   565 			return pContext->ThrowNativeError("Invalid driver Handle %x (error: %d)", params[1], err);
   566 		}
   567 	}
   568 
   569 	pContext->StringToLocalUTF8(params[2], params[3], driver->GetProductName(), NULL);
   570 
   571 	return 1;
   572 }
   573 
   574 static cell_t SQL_GetAffectedRows(IPluginContext *pContext, const cell_t *params)
   575 {
   576 	IDatabase *db = NULL;
   577 	IPreparedQuery *stmt = NULL;
   578 	IQuery *query = NULL;
   579 	HandleError err;
   580 
   581 	if (((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   582 		&& ((err = ReadQueryAndDbHndl(params[1], pContext, &query, &db)) != HandleError_None))
   583 	{
   584 		return pContext->ThrowNativeError("Invalid statement, db, or query Handle %x (error: %d)", params[1], err);
   585 	}
   586 
   587 
   588 	if (stmt)
   589 	{
   590 		return stmt->GetAffectedRows();
   591 	}
   592 	else if (query)
   593 	{
   594 		return db->GetAffectedRowsForQuery(query);
   595 	}
   596 	else if (db)
   597 	{
   598 		return db->GetAffectedRows();
   599 	}
   600 
   601 	return pContext->ThrowNativeError("Unknown error reading db/stmt/query handles");
   602 }
   603 
   604 static cell_t SQL_GetInsertId(IPluginContext *pContext, const cell_t *params)
   605 {
   606 	IDatabase *db = NULL;
   607 	IQuery *query = NULL;
   608 	IPreparedQuery *stmt = NULL;
   609 	HandleError err;
   610 
   611 	if (((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   612 		&& ((err = ReadQueryAndDbHndl(params[1], pContext, &query, &db)) != HandleError_None))
   613 	{
   614 		return pContext->ThrowNativeError("Invalid statement, db, or query Handle %x (error: %d)", params[1], err);
   615 	}
   616 
   617 	if (query)
   618 	{
   619 		return db->GetInsertIDForQuery(query);
   620 	}
   621 	else if (db)
   622 	{
   623 		return db->GetInsertID();
   624 	}
   625 	else if (stmt)
   626 	{
   627 		return stmt->GetInsertID();
   628 	}
   629 
   630 	return pContext->ThrowNativeError("Unknown error reading db/stmt/query handles");
   631 }
   632 
   633 static cell_t SQL_GetError(IPluginContext *pContext, const cell_t *params)
   634 {
   635 	IDatabase *db = NULL;
   636 	IPreparedQuery *stmt = NULL;
   637 	HandleError err;
   638 
   639 	if ((err = ReadDbOrStmtHndl(params[1], pContext, &db, &stmt)) != HandleError_None)
   640 	{
   641 		return pContext->ThrowNativeError("Invalid statement or db Handle %x (error: %d)", params[1], err);
   642 	}
   643 
   644 	const char *error = "";
   645 	if (db)
   646 	{
   647 		error = db->GetError();
   648 	} else if (stmt) {
   649 		error = stmt->GetError();
   650 	}
   651 
   652 	if (error[0] == '\0')
   653 	{
   654 		return false;
   655 	}
   656 
   657 	pContext->StringToLocalUTF8(params[2], params[3], error, NULL);
   658 
   659 	return 1;
   660 }
   661 
   662 static cell_t SQL_QuoteString(IPluginContext *pContext, const cell_t *params)
   663 {
   664 	IDatabase *db = NULL;
   665 	HandleError err;
   666 
   667 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   668 		!= HandleError_None)
   669 	{
   670 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   671 	}
   672 
   673 	char *input, *output;
   674 	size_t maxlength = (size_t)params[4];
   675 	pContext->LocalToString(params[2], &input);
   676 	pContext->LocalToString(params[3], &output);
   677 
   678 	size_t written;
   679 	bool s = db->QuoteString(input, output, maxlength, &written);
   680 
   681 	cell_t *addr;
   682 	pContext->LocalToPhysAddr(params[5], &addr);
   683 	*addr = (cell_t)written;
   684 
   685 	return s ? 1 : 0;
   686 }
   687 
   688 static cell_t SQL_FastQuery(IPluginContext *pContext, const cell_t *params)
   689 {
   690 	IDatabase *db = NULL;
   691 	HandleError err;
   692 
   693 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   694 		!= HandleError_None)
   695 	{
   696 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   697 	}
   698 
   699 	char *query;
   700 	pContext->LocalToString(params[2], &query);
   701 
   702 	if (params[0] >= 3 && params[3] != -1)
   703 	{
   704 		return db->DoSimpleQueryEx(query, params[3]) ? 1 : 0;
   705 	}
   706 
   707 	return db->DoSimpleQuery(query) ? 1 : 0;
   708 }
   709 
   710 static cell_t SQL_Query(IPluginContext *pContext, const cell_t *params)
   711 {
   712 	IDatabase *db = NULL;
   713 	HandleError err;
   714 
   715 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   716 		!= HandleError_None)
   717 	{
   718 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   719 	}
   720 
   721 	char *query;
   722 	pContext->LocalToString(params[2], &query);
   723 
   724 	IQuery *qr;
   725 	
   726 	if (params[0] >= 3 && params[3] != -1)
   727 	{
   728 		qr = db->DoQueryEx(query, params[3]);
   729 	}
   730 	else
   731 	{
   732 		qr = db->DoQuery(query);
   733 	}
   734 
   735 	if (!qr)
   736 	{
   737 		return BAD_HANDLE;
   738 	}
   739 
   740 	CombinedQuery *c = new CombinedQuery;
   741 	c->query = qr;
   742 	c->db = db;
   743 	Handle_t hndl = handlesys->CreateHandle(hCombinedQueryType, c, pContext->GetIdentity(), g_pCoreIdent, NULL);
   744 	if (hndl == BAD_HANDLE)
   745 	{
   746 		qr->Destroy();
   747 		delete c;
   748 		return BAD_HANDLE;
   749 	}
   750 
   751 	return hndl;
   752 }
   753 
   754 static cell_t SQL_TQuery(IPluginContext *pContext, const cell_t *params)
   755 {
   756 	IDatabase *db = NULL;
   757 	HandleError err;
   758 
   759 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   760 		!= HandleError_None)
   761 	{
   762 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   763 	}
   764 
   765 	if (!db->GetDriver()->IsThreadSafe())
   766 	{
   767 		return pContext->ThrowNativeError("Driver \"%s\" is not thread safe!", db->GetDriver()->GetIdentifier());
   768 	}
   769 
   770 	IPluginFunction *pf = pContext->GetFunctionById(params[2]);
   771 	if (!pf)
   772 	{
   773 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
   774 	}
   775 
   776 	char *query;
   777 	pContext->LocalToString(params[3], &query);
   778 
   779 	cell_t data = params[4];
   780 	PrioQueueLevel level = PrioQueue_Normal;
   781 	if (params[5] == (cell_t)PrioQueue_High)
   782 	{
   783 		level = PrioQueue_High;
   784 	} else if (params[5] == (cell_t)PrioQueue_Low) {
   785 		level = PrioQueue_Low;
   786 	}
   787 
   788 	IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
   789 
   790 	TQueryOp *op = new TQueryOp(db, pf, query, data);
   791 	if (pPlugin->GetProperty("DisallowDBThreads", NULL)
   792 		|| !g_DBMan.AddToThreadQueue(op, level))
   793 	{
   794 		/* Do everything right now */
   795 		op->RunThreadPart();
   796 		op->RunThinkPart();
   797 		op->Destroy();
   798 	}
   799 
   800 	return 1;
   801 }
   802 
   803 static cell_t SQL_LockDatabase(IPluginContext *pContext, const cell_t *params)
   804 {
   805 	IDatabase *db = NULL;
   806 	HandleError err;
   807 
   808 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   809 		!= HandleError_None)
   810 	{
   811 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   812 	}
   813 
   814 	db->LockForFullAtomicOperation();
   815 
   816 	return 1;
   817 }
   818 
   819 static cell_t SQL_UnlockDatabase(IPluginContext *pContext, const cell_t *params)
   820 {
   821 	IDatabase *db = NULL;
   822 	HandleError err;
   823 
   824 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   825 		!= HandleError_None)
   826 	{
   827 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   828 	}
   829 
   830 	db->UnlockFromFullAtomicOperation();
   831 
   832 	return 1;
   833 }
   834 
   835 static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
   836 {
   837 	IDatabase *db = NULL;
   838 	HandleError err;
   839 
   840 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   841 		!= HandleError_None)
   842 	{
   843 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   844 	}
   845 
   846 	char *query, *error;
   847 	size_t maxlength = (size_t)params[4];
   848 	pContext->LocalToString(params[2], &query);
   849 	pContext->LocalToString(params[3], &error);
   850 
   851 	IPreparedQuery *qr = db->PrepareQuery(query, error, maxlength);
   852 
   853 	if (!qr)
   854 	{
   855 		return BAD_HANDLE;
   856 	}
   857 
   858 	Handle_t hndl = handlesys->CreateHandle(hStmtType, qr, pContext->GetIdentity(), g_pCoreIdent, NULL);
   859 	if (hndl == BAD_HANDLE)
   860 	{
   861 		qr->Destroy();
   862 		return BAD_HANDLE;
   863 	}
   864 
   865 	return hndl;
   866 }
   867 
   868 static cell_t SQL_FetchMoreResults(IPluginContext *pContext, const cell_t *params)
   869 {
   870 	IQuery *query;
   871 	HandleError err;
   872 
   873 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   874 	{
   875 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   876 	}
   877 
   878 	return query->FetchMoreResults() ? 1 : 0;
   879 }
   880 
   881 static cell_t SQL_HasResultSet(IPluginContext *pContext, const cell_t *params)
   882 {
   883 	IQuery *query;
   884 	HandleError err;
   885 
   886 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   887 	{
   888 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   889 	}
   890 
   891 	return query->GetResultSet() != NULL ? true : false;
   892 }
   893 
   894 static cell_t SQL_GetRowCount(IPluginContext *pContext, const cell_t *params)
   895 {
   896 	IQuery *query;
   897 	HandleError err;
   898 
   899 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   900 	{
   901 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   902 	}
   903 
   904 	IResultSet *rs = query->GetResultSet();
   905 	if (!rs)
   906 	{
   907 		return 0;
   908 	}
   909 
   910 	return rs->GetRowCount();
   911 }
   912 
   913 static cell_t SQL_GetFieldCount(IPluginContext *pContext, const cell_t *params)
   914 {
   915 	IQuery *query;
   916 	HandleError err;
   917 
   918 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   919 	{
   920 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   921 	}
   922 
   923 	IResultSet *rs = query->GetResultSet();
   924 	if (!rs)
   925 	{
   926 		return 0;
   927 	}
   928 
   929 	return rs->GetFieldCount();
   930 }
   931 
   932 static cell_t SQL_FieldNumToName(IPluginContext *pContext, const cell_t *params)
   933 {
   934 	IQuery *query;
   935 	HandleError err;
   936 
   937 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   938 	{
   939 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   940 	}
   941 
   942 	IResultSet *rs = query->GetResultSet();
   943 	if (!rs)
   944 	{
   945 		return pContext->ThrowNativeError("No current result set");
   946 	}
   947 
   948 	unsigned int field = params[2];
   949 
   950 	const char *fldname;
   951 	if ((fldname = rs->FieldNumToName(field)) == NULL)
   952 	{
   953 		return pContext->ThrowNativeError("Invalid field index %d", field);
   954 	}
   955 
   956 	pContext->StringToLocalUTF8(params[3], params[4], fldname, NULL);
   957 
   958 	return 1;
   959 }
   960 
   961 static cell_t SQL_FieldNameToNum(IPluginContext *pContext, const cell_t *params)
   962 {
   963 	IQuery *query;
   964 	HandleError err;
   965 
   966 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   967 	{
   968 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   969 	}
   970 
   971 	IResultSet *rs = query->GetResultSet();
   972 	if (!rs)
   973 	{
   974 		return pContext->ThrowNativeError("No current result set");
   975 	}
   976 
   977 	char *field;
   978 	pContext->LocalToString(params[2], &field);
   979 
   980 	cell_t *num;
   981 	pContext->LocalToPhysAddr(params[3], &num);
   982 
   983 	return rs->FieldNameToNum(field, (unsigned int *)num) ? 1 : 0;
   984 }
   985 
   986 static cell_t SQL_FetchRow(IPluginContext *pContext, const cell_t *params)
   987 {
   988 	IQuery *query;
   989 	HandleError err;
   990 
   991 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
   992 	{
   993 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
   994 	}
   995 
   996 	IResultSet *rs = query->GetResultSet();
   997 	if (!rs)
   998 	{
   999 		return pContext->ThrowNativeError("No current result set");
  1000 	}
  1001 
  1002 	return (rs->FetchRow() != NULL) ? true : false;
  1003 }
  1004 
  1005 static cell_t SQL_MoreRows(IPluginContext *pContext, const cell_t *params)
  1006 {
  1007 	IQuery *query;
  1008 	HandleError err;
  1009 
  1010 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1011 	{
  1012 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1013 	}
  1014 
  1015 	IResultSet *rs = query->GetResultSet();
  1016 	if (!rs)
  1017 	{
  1018 		return pContext->ThrowNativeError("No current result set");
  1019 	}
  1020 
  1021 	return rs->MoreRows() ? 1 : 0;
  1022 }
  1023 
  1024 static cell_t SQL_Rewind(IPluginContext *pContext, const cell_t *params)
  1025 {
  1026 	IQuery *query;
  1027 	HandleError err;
  1028 
  1029 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1030 	{
  1031 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1032 	}
  1033 
  1034 	IResultSet *rs = query->GetResultSet();
  1035 	if (!rs)
  1036 	{
  1037 		return pContext->ThrowNativeError("No current result set");
  1038 	}
  1039 
  1040 	return rs->Rewind() ? 1 : 0;
  1041 }
  1042 
  1043 static cell_t SQL_FetchString(IPluginContext *pContext, const cell_t *params)
  1044 {
  1045 	IQuery *query;
  1046 	HandleError err;
  1047 
  1048 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1049 	{
  1050 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1051 	}
  1052 
  1053 	IResultSet *rs = query->GetResultSet();
  1054 	if (!rs)
  1055 	{
  1056 		return pContext->ThrowNativeError("No current result set");
  1057 	}
  1058 
  1059 	IResultRow *row = rs->CurrentRow();
  1060 	if (!row)
  1061 	{
  1062 		return pContext->ThrowNativeError("Current result set has no fetched rows");
  1063 	}
  1064 
  1065 	const char *str;
  1066 	size_t length;
  1067 	DBResult res = row->GetString(params[2], &str, &length);
  1068 
  1069 	if (res == DBVal_Error)
  1070 	{
  1071 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
  1072 	} else if (res == DBVal_TypeMismatch) {
  1073 		return pContext->ThrowNativeError("Could not fetch data in field %d as a string", params[2]);
  1074 	}
  1075 
  1076 	pContext->StringToLocalUTF8(params[3], params[4], str, &length);
  1077 
  1078 	cell_t *addr;
  1079 	pContext->LocalToPhysAddr(params[5], &addr);
  1080 	*addr = (cell_t)res;
  1081 
  1082 	return (cell_t)length;
  1083 }
  1084 
  1085 static cell_t SQL_FetchFloat(IPluginContext *pContext, const cell_t *params)
  1086 {
  1087 	IQuery *query;
  1088 	HandleError err;
  1089 
  1090 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1091 	{
  1092 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1093 	}
  1094 
  1095 	IResultSet *rs = query->GetResultSet();
  1096 	if (!rs)
  1097 	{
  1098 		return pContext->ThrowNativeError("No current result set");
  1099 	}
  1100 
  1101 	IResultRow *row = rs->CurrentRow();
  1102 	if (!row)
  1103 	{
  1104 		return pContext->ThrowNativeError("Current result set has no fetched rows");
  1105 	}
  1106 
  1107 	float f;
  1108 	DBResult res = row->GetFloat(params[2], &f);
  1109 
  1110 	if (res == DBVal_Error)
  1111 	{
  1112 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
  1113 	} else if (res == DBVal_TypeMismatch) {
  1114 		return pContext->ThrowNativeError("Could not fetch data in field %d as a float", params[2]);
  1115 	}
  1116 
  1117 	cell_t *addr;
  1118 	pContext->LocalToPhysAddr(params[3], &addr);
  1119 	*addr = (cell_t)res;
  1120 
  1121 	return sp_ftoc(f);
  1122 }
  1123 
  1124 static cell_t SQL_FetchInt(IPluginContext *pContext, const cell_t *params)
  1125 {
  1126 	IQuery *query;
  1127 	HandleError err;
  1128 
  1129 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1130 	{
  1131 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1132 	}
  1133 
  1134 	IResultSet *rs = query->GetResultSet();
  1135 	if (!rs)
  1136 	{
  1137 		return pContext->ThrowNativeError("No current result set");
  1138 	}
  1139 
  1140 	IResultRow *row = rs->CurrentRow();
  1141 	if (!row)
  1142 	{
  1143 		return pContext->ThrowNativeError("Current result set has no fetched rows");
  1144 	}
  1145 
  1146 	int iv;
  1147 	DBResult res = row->GetInt(params[2], &iv);
  1148 
  1149 	if (res == DBVal_Error)
  1150 	{
  1151 		return pContext->ThrowNativeError("Error fetching data from field %d", params[2]);
  1152 	} else if (res == DBVal_TypeMismatch) {
  1153 		return pContext->ThrowNativeError("Could not fetch data in field %d as an integer", params[2]);
  1154 	}
  1155 
  1156 	cell_t *addr;
  1157 	pContext->LocalToPhysAddr(params[3], &addr);
  1158 	*addr = (cell_t)res;
  1159 
  1160 	return iv;
  1161 }
  1162 
  1163 static cell_t SQL_IsFieldNull(IPluginContext *pContext, const cell_t *params)
  1164 {
  1165 	IQuery *query;
  1166 	HandleError err;
  1167 
  1168 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1169 	{
  1170 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1171 	}
  1172 
  1173 	IResultSet *rs = query->GetResultSet();
  1174 	if (!rs)
  1175 	{
  1176 		return pContext->ThrowNativeError("No current result set");
  1177 	}
  1178 
  1179 	IResultRow *row = rs->CurrentRow();
  1180 	if (!row)
  1181 	{
  1182 		return pContext->ThrowNativeError("Current result set has no fetched rows");
  1183 	}
  1184 
  1185 	if ((unsigned)params[2] >= rs->GetFieldCount())
  1186 	{
  1187 		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
  1188 	}
  1189 
  1190 	return row->IsNull(params[2]) ? 1 : 0;
  1191 }
  1192 
  1193 static cell_t SQL_FetchSize(IPluginContext *pContext, const cell_t *params)
  1194 {
  1195 	IQuery *query;
  1196 	HandleError err;
  1197 
  1198 	if ((err = ReadQueryHndl(params[1], pContext, &query)) != HandleError_None)
  1199 	{
  1200 		return pContext->ThrowNativeError("Invalid query Handle %x (error: %d)", params[1], err);
  1201 	}
  1202 
  1203 	IResultSet *rs = query->GetResultSet();
  1204 	if (!rs)
  1205 	{
  1206 		return pContext->ThrowNativeError("No current result set");
  1207 	}
  1208 
  1209 	IResultRow *row = rs->CurrentRow();
  1210 	if (!row)
  1211 	{
  1212 		return pContext->ThrowNativeError("Current result set has no fetched rows");
  1213 	}
  1214 
  1215 	if ((unsigned)params[2] >= rs->GetFieldCount())
  1216 	{
  1217 		return pContext->ThrowNativeError("Invalid field index %d", params[2]);
  1218 	}
  1219 
  1220 	return row->GetDataSize(params[2]);
  1221 }
  1222 
  1223 static cell_t SQL_BindParamInt(IPluginContext *pContext, const cell_t *params)
  1224 {
  1225 	IPreparedQuery *stmt;
  1226 	HandleError err;
  1227 
  1228 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
  1229 	{
  1230 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
  1231 	}
  1232 
  1233 	if (!stmt->BindParamInt(params[2], params[3], params[4] ? true : false))
  1234 	{
  1235 		return pContext->ThrowNativeError("Could not bind parameter %d as an integer", params[2]);
  1236 	}
  1237 
  1238 	return 1;
  1239 }
  1240 
  1241 static cell_t SQL_BindParamFloat(IPluginContext *pContext, const cell_t *params)
  1242 {
  1243 	IPreparedQuery *stmt;
  1244 	HandleError err;
  1245 
  1246 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
  1247 	{
  1248 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
  1249 	}
  1250 
  1251 	if (!stmt->BindParamFloat(params[2], sp_ctof(params[3])))
  1252 	{
  1253 		return pContext->ThrowNativeError("Could not bind parameter %d as a float", params[2]);
  1254 	}
  1255 
  1256 	return 1;
  1257 }
  1258 
  1259 static cell_t SQL_BindParamString(IPluginContext *pContext, const cell_t *params)
  1260 {
  1261 	IPreparedQuery *stmt;
  1262 	HandleError err;
  1263 
  1264 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
  1265 	{
  1266 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
  1267 	}
  1268 
  1269 	char *str;
  1270 	pContext->LocalToString(params[3], &str);
  1271 
  1272 	if (!stmt->BindParamString(params[2], str, params[4] ? true : false))
  1273 	{
  1274 		return pContext->ThrowNativeError("Could not bind parameter %d as a string", params[2]);
  1275 	}
  1276 
  1277 	return 1;
  1278 }
  1279 
  1280 static cell_t SQL_Execute(IPluginContext *pContext, const cell_t *params)
  1281 {
  1282 	IPreparedQuery *stmt;
  1283 	HandleError err;
  1284 
  1285 	if ((err = ReadStmtHndl(params[1], pContext, &stmt)) != HandleError_None)
  1286 	{
  1287 		return pContext->ThrowNativeError("Invalid statement Handle %x (error: %d)", params[1], err);
  1288 	}
  1289 
  1290 	return stmt->Execute() ? 1 : 0;
  1291 }
  1292 
  1293 static cell_t SQL_IsSameConnection(IPluginContext *pContext, const cell_t *params)
  1294 {
  1295 	IDatabase *db1=NULL, *db2=NULL;
  1296 	HandleError err;
  1297 
  1298 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db1))
  1299 		!= HandleError_None)
  1300 	{
  1301 		return pContext->ThrowNativeError("Invalid database Handle 1/%x (error: %d)", params[1], err);
  1302 	}
  1303 
  1304 	if ((err = g_DBMan.ReadHandle(params[2], DBHandle_Database, (void **)&db2))
  1305 		!= HandleError_None)
  1306 	{
  1307 		return pContext->ThrowNativeError("Invalid database Handle 2/%x (error: %d)", params[2], err);
  1308 	}
  1309 
  1310 	return (db1 == db2) ? true : false;
  1311 }
  1312 
  1313 static cell_t SQL_ReadDriver(IPluginContext *pContext, const cell_t *params)
  1314 {
  1315 	IDatabase *db1=NULL;
  1316 	HandleError err;
  1317 
  1318 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db1))
  1319 		!= HandleError_None)
  1320 	{
  1321 		return pContext->ThrowNativeError("Invalid database Handle 1/%x (error: %d)", params[1], err);
  1322 	}
  1323 
  1324 	IDBDriver *driver = db1->GetDriver();
  1325 
  1326 	pContext->StringToLocalUTF8(params[2], params[3], driver->GetIdentifier(), NULL);
  1327 
  1328 	return driver->GetHandle();
  1329 }
  1330 
  1331 static cell_t SQL_CheckConfig(IPluginContext *pContext, const cell_t *params)
  1332 {
  1333 	char *name;
  1334 	pContext->LocalToString(params[1], &name);
  1335 
  1336 	return (g_DBMan.FindDatabaseConf(name) != NULL) ? 1 : 0;
  1337 }
  1338 
  1339 static cell_t SQL_ConnectCustom(IPluginContext *pContext, const cell_t *params)
  1340 {
  1341 	KeyValues *kv;
  1342 	HandleError err;
  1343 
  1344 	kv = g_pSM->ReadKeyValuesHandle(params[1], &err, false);
  1345 	if (kv == NULL)
  1346 	{
  1347 		return pContext->ThrowNativeError("Invalid KeyValues handle %x (error: %d)",
  1348 										  params[1],
  1349 										  err);
  1350 	}
  1351 
  1352 	DatabaseInfo info = smcore.GetDBInfoFromKeyValues(kv);
  1353 
  1354 	IDBDriver *driver;
  1355 	if (info.driver[0] == '\0' || strcmp(info.driver, "default") == 0)
  1356 	{
  1357 		driver = g_DBMan.GetDefaultDriver();
  1358 	}
  1359 	else
  1360 	{
  1361 		driver = g_DBMan.FindOrLoadDriver(info.driver);
  1362 	}
  1363 
  1364 	if (driver == NULL)
  1365 	{
  1366 		char buffer[255];
  1367 
  1368 		g_pSM->Format(buffer, sizeof(buffer), "Could not find driver \"%s\"", info.driver);
  1369 		pContext->StringToLocalUTF8(params[2], params[3], buffer, NULL);
  1370 
  1371 		return BAD_HANDLE;
  1372 	}
  1373 
  1374 	char *buffer;
  1375 	IDatabase *db;
  1376 
  1377 	pContext->LocalToString(params[2], &buffer);
  1378 	
  1379 	db = driver->Connect(&info, params[4] ? true : false, buffer, params[3]);
  1380 	if (db == NULL)
  1381 	{
  1382 		return BAD_HANDLE;
  1383 	}
  1384 
  1385 	Handle_t hndl = g_DBMan.CreateHandle(DBHandle_Database, db, pContext->GetIdentity());
  1386 	if (!hndl)
  1387 	{
  1388 		db->Close();
  1389 		return pContext->ThrowNativeError("Out of handles!");
  1390 	}
  1391 
  1392 	/* HACK! Add us to the dependency list */
  1393 	IExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
  1394 	if (pExt)
  1395 	{
  1396 		g_Extensions.BindChildPlugin(pExt, scripts->FindPluginByContext(pContext->GetContext()));
  1397 	}
  1398 
  1399 	return hndl;
  1400 }
  1401 
  1402 static cell_t SQL_SetCharset(IPluginContext *pContext, const cell_t *params)
  1403 {
  1404 	IDatabase *db = NULL;
  1405 	HandleError err;
  1406 
  1407 	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
  1408 		!= HandleError_None)
  1409 	{
  1410 		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
  1411 	}
  1412 
  1413 	char *characterset;
  1414 	pContext->LocalToString(params[2], &characterset);
  1415 
  1416 	return db->SetCharacterSet(characterset);
  1417 }
  1418 
  1419 REGISTER_NATIVES(dbNatives)
  1420 {
  1421 	{"SQL_BindParamInt",		SQL_BindParamInt},
  1422 	{"SQL_BindParamFloat",		SQL_BindParamFloat},
  1423 	{"SQL_BindParamString",		SQL_BindParamString},\
  1424 	{"SQL_CheckConfig",			SQL_CheckConfig},
  1425 	{"SQL_Connect",				SQL_Connect},
  1426 	{"SQL_ConnectEx",			SQL_ConnectEx},
  1427 	{"SQL_EscapeString",		SQL_QuoteString},
  1428 	{"SQL_Execute",				SQL_Execute},
  1429 	{"SQL_FastQuery",			SQL_FastQuery},
  1430 	{"SQL_FetchFloat",			SQL_FetchFloat},
  1431 	{"SQL_FetchInt",			SQL_FetchInt},
  1432 	{"SQL_FetchMoreResults",	SQL_FetchMoreResults},
  1433 	{"SQL_FetchRow",			SQL_FetchRow},
  1434 	{"SQL_FetchSize",			SQL_FetchSize},
  1435 	{"SQL_FetchString",			SQL_FetchString},
  1436 	{"SQL_FieldNameToNum",		SQL_FieldNameToNum},
  1437 	{"SQL_FieldNumToName",		SQL_FieldNumToName},
  1438 	{"SQL_GetAffectedRows",		SQL_GetAffectedRows},
  1439 	{"SQL_GetDriver",			SQL_GetDriver},
  1440 	{"SQL_GetDriverIdent",		SQL_GetDriverIdent},
  1441 	{"SQL_GetDriverProduct",	SQL_GetDriverProduct},
  1442 	{"SQL_GetError",			SQL_GetError},
  1443 	{"SQL_GetFieldCount",		SQL_GetFieldCount},
  1444 	{"SQL_GetInsertId",			SQL_GetInsertId},
  1445 	{"SQL_GetRowCount",			SQL_GetRowCount},
  1446 	{"SQL_HasResultSet",		SQL_HasResultSet},
  1447 	{"SQL_IsFieldNull",			SQL_IsFieldNull},
  1448 	{"SQL_IsSameConnection",	SQL_IsSameConnection},
  1449 	{"SQL_LockDatabase",		SQL_LockDatabase},
  1450 	{"SQL_MoreRows",			SQL_MoreRows},
  1451 	{"SQL_PrepareQuery",		SQL_PrepareQuery},
  1452 	{"SQL_Query",				SQL_Query},
  1453 	{"SQL_QuoteString",			SQL_QuoteString},
  1454 	{"SQL_ReadDriver",			SQL_ReadDriver},
  1455 	{"SQL_Rewind",				SQL_Rewind},
  1456 	{"SQL_TConnect",			SQL_TConnect},
  1457 	{"SQL_TQuery",				SQL_TQuery},
  1458 	{"SQL_UnlockDatabase",		SQL_UnlockDatabase},
  1459 	{"SQL_ConnectCustom",		SQL_ConnectCustom},
  1460 	{"SQL_SetCharset",		SQL_SetCharset},
  1461 	{NULL,						NULL},
  1462 };
  1463