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