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