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