core/smn_database.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1324 9d1983bf06c7
child 1874 6bd98dba0f2a
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

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