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