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