core/smn_database.cpp
changeset 1096 8e11b2ebdc22
parent 1025 2baaa750e2b7
child 1100 f08cf99df0cc
     1.1 --- a/core/smn_database.cpp	Fri Jul 13 19:19:30 2007 +0000
     1.2 +++ b/core/smn_database.cpp	Fri Jul 13 19:20:12 2007 +0000
     1.3 @@ -3,6 +3,7 @@
     1.4  #include "Database.h"
     1.5  #include "ExtensionSys.h"
     1.6  #include "PluginSys.h"
     1.7 +#include "sm_stringutil.h"
     1.8  
     1.9  HandleType_t hQueryType;
    1.10  HandleType_t hStmtType;
    1.11 @@ -78,6 +79,197 @@
    1.12  	return err;
    1.13  }
    1.14  
    1.15 +class TQueryOp : public IDBThreadOperation
    1.16 +{
    1.17 +public:
    1.18 +	TQueryOp(IDatabase *db, IPluginFunction *pf, const char *query, cell_t data) : 
    1.19 +	  m_pDatabase(db), m_pFunction(pf), m_Query(query), m_Data(data),
    1.20 +	  me(g_PluginSys.GetPluginByCtx(pf->GetParentContext()->GetContext())),
    1.21 +	  m_pQuery(NULL)
    1.22 +	{
    1.23 +		/* We always increase the reference count because this is potentially
    1.24 +		 * asynchronous.  Otherwise the original handle could be closed while 
    1.25 +		 * we're still latched onto it.
    1.26 +		 */
    1.27 +		m_pDatabase->IncReferenceCount();
    1.28 +
    1.29 +		/* Now create our own Handle such that it can only be closed by us.
    1.30 +		 * We allow cloning just in case someone wants to hold onto it.
    1.31 +		 */
    1.32 +		HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
    1.33 +		HandleAccess access;
    1.34 +		g_HandleSys.InitAccessDefaults(NULL, &access);
    1.35 +		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
    1.36 +		m_MyHandle = g_HandleSys.CreateHandleEx(g_DBMan.GetDatabaseType(),
    1.37 +			db,
    1.38 +			&sec,
    1.39 +			&access,
    1.40 +			NULL);
    1.41 +	}
    1.42 +	~TQueryOp()
    1.43 +	{
    1.44 +		if (m_pQuery)
    1.45 +		{
    1.46 +			m_pQuery->Destroy();
    1.47 +		}
    1.48 +
    1.49 +		/* Close our Handle if it's valid. */
    1.50 +		if (m_MyHandle != BAD_HANDLE)
    1.51 +		{
    1.52 +			HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
    1.53 +			g_HandleSys.FreeHandle(m_MyHandle, &sec);
    1.54 +		} else {
    1.55 +			/* Otherwise, there is an open ref to the db */
    1.56 +			m_pDatabase->Close();
    1.57 +		}
    1.58 +	}
    1.59 +	CPlugin *GetPlugin()
    1.60 +	{
    1.61 +		return me;
    1.62 +	}
    1.63 +	IDBDriver *GetDriver()
    1.64 +	{
    1.65 +		return m_pDatabase->GetDriver();
    1.66 +	}
    1.67 +	void RunThreadPart()
    1.68 +	{
    1.69 +		m_pDatabase->LockForFullAtomicOperation();
    1.70 +		m_pQuery = m_pDatabase->DoQuery(m_Query.c_str());
    1.71 +		if (!m_pQuery)
    1.72 +		{
    1.73 +			UTIL_Format(error, sizeof(error), "%s", m_pDatabase->GetError());
    1.74 +		}
    1.75 +		m_pDatabase->UnlockFromFullAtomicOperation();
    1.76 +	}
    1.77 +	void CancelThinkPart()
    1.78 +	{
    1.79 +		m_pFunction->PushCell(BAD_HANDLE);
    1.80 +		m_pFunction->PushCell(BAD_HANDLE);
    1.81 +		m_pFunction->PushString("Driver is unloading");
    1.82 +		m_pFunction->PushCell(m_Data);
    1.83 +		m_pFunction->Execute(NULL);
    1.84 +		delete this;
    1.85 +	}
    1.86 +	void RunThinkPart()
    1.87 +	{
    1.88 +		/* Create a Handle for our query */
    1.89 +		HandleSecurity sec(me->GetIdentity(), g_pCoreIdent);
    1.90 +		HandleAccess access;
    1.91 +		g_HandleSys.InitAccessDefaults(NULL, &access);
    1.92 +		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
    1.93 +
    1.94 +		Handle_t qh = BAD_HANDLE;
    1.95 +		
    1.96 +		if (m_pQuery)
    1.97 +		{
    1.98 +			qh = g_HandleSys.CreateHandle(hQueryType, m_pQuery, me->GetIdentity(), g_pCoreIdent, NULL);
    1.99 +			if (qh != BAD_HANDLE)
   1.100 +			{
   1.101 +				m_pQuery = NULL;
   1.102 +			} else {
   1.103 +				UTIL_Format(error, sizeof(error), "Could not alloc handle");
   1.104 +			}
   1.105 +		}
   1.106 +
   1.107 +		m_pFunction->PushCell(m_MyHandle);
   1.108 +		m_pFunction->PushCell(qh);
   1.109 +		m_pFunction->PushString(qh == BAD_HANDLE ? error : "");
   1.110 +		m_pFunction->PushCell(m_Data);
   1.111 +		m_pFunction->Execute(NULL);
   1.112 +
   1.113 +		if (qh != BAD_HANDLE)
   1.114 +		{
   1.115 +			g_HandleSys.FreeHandle(qh, &sec);
   1.116 +		}
   1.117 +
   1.118 +		delete this;
   1.119 +	}
   1.120 +private:
   1.121 +	IDatabase *m_pDatabase;
   1.122 +	IPluginFunction *m_pFunction;
   1.123 +	String m_Query;
   1.124 +	cell_t m_Data;
   1.125 +	CPlugin *me;
   1.126 +	IQuery *m_pQuery;
   1.127 +	char error[255];
   1.128 +	Handle_t m_MyHandle;
   1.129 +};
   1.130 +
   1.131 +class TConnectOp : public IDBThreadOperation
   1.132 +{
   1.133 +public:
   1.134 +	TConnectOp(IPluginFunction *func, IDBDriver *driver, const char *_dbname)
   1.135 +	{
   1.136 +		m_pFunction = func;
   1.137 +		m_pDriver = driver;
   1.138 +		m_pDatabase = NULL;
   1.139 +		error[0] = '\0';
   1.140 +		strncopy(dbname, _dbname, sizeof(dbname));
   1.141 +		me = g_PluginSys.GetPluginByCtx(m_pFunction->GetParentContext()->GetContext());
   1.142 +	}
   1.143 +	CPlugin *GetPlugin()
   1.144 +	{
   1.145 +		return me;
   1.146 +	}
   1.147 +	IDBDriver *GetDriver()
   1.148 +	{
   1.149 +		return m_pDriver;
   1.150 +	}
   1.151 +	void RunThreadPart()
   1.152 +	{
   1.153 +		g_DBMan.LockConfig();
   1.154 +		const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(dbname);
   1.155 +		if (!pInfo)
   1.156 +		{
   1.157 +			UTIL_Format(error, sizeof(error), "Could not find database config \"%s\"", dbname);
   1.158 +		} else {
   1.159 +			m_pDatabase = m_pDriver->Connect(pInfo, false, error, sizeof(error));
   1.160 +		}
   1.161 +		g_DBMan.UnlockConfig();
   1.162 +	}
   1.163 +	void CancelThinkPart()
   1.164 +	{
   1.165 +		if (m_pDatabase)
   1.166 +		{
   1.167 +			m_pDatabase->Close();
   1.168 +		}
   1.169 +		m_pFunction->PushCell(BAD_HANDLE);
   1.170 +		m_pFunction->PushCell(BAD_HANDLE);
   1.171 +		m_pFunction->PushString("Driver is unloading");
   1.172 +		m_pFunction->PushCell(0);
   1.173 +		m_pFunction->Execute(NULL);
   1.174 +		delete this;
   1.175 +	}
   1.176 +	void RunThinkPart()
   1.177 +	{
   1.178 +		Handle_t hndl = BAD_HANDLE;
   1.179 +		
   1.180 +		if (m_pDatabase)
   1.181 +		{
   1.182 +			if ((hndl = g_DBMan.CreateHandle(DBHandle_Database, m_pDatabase, me->GetIdentity()))
   1.183 +				== BAD_HANDLE)
   1.184 +			{
   1.185 +				m_pDatabase->Close();
   1.186 +				UTIL_Format(error, sizeof(error), "Unable to allocate Handle");
   1.187 +			}
   1.188 +		}
   1.189 +
   1.190 +		m_pFunction->PushCell(m_pDriver->GetHandle());
   1.191 +		m_pFunction->PushCell(hndl);
   1.192 +		m_pFunction->PushString(hndl == BAD_HANDLE ? "" : error);
   1.193 +		m_pFunction->PushCell(0);
   1.194 +		m_pFunction->Execute(NULL);
   1.195 +		delete this;
   1.196 +	}
   1.197 +private:
   1.198 +	CPlugin *me;
   1.199 +	IPluginFunction *m_pFunction;
   1.200 +	IDBDriver *m_pDriver;
   1.201 +	IDatabase *m_pDatabase;
   1.202 +	char dbname[64];
   1.203 +	char error[255];
   1.204 +};
   1.205 +
   1.206  static cell_t SQL_Connect(IPluginContext *pContext, const cell_t *params)
   1.207  {
   1.208  	char *conf, *err;
   1.209 @@ -111,6 +303,75 @@
   1.210  	return hndl;
   1.211  }
   1.212  
   1.213 +static cell_t SQL_TConnect(IPluginContext *pContext, const cell_t *params)
   1.214 +{
   1.215 +	IPluginFunction *pf = pContext->GetFunctionById(params[1]);
   1.216 +	if (!pf)
   1.217 +	{
   1.218 +		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   1.219 +	}
   1.220 +
   1.221 +	char *conf;
   1.222 +	pContext->LocalToString(params[2], &conf);
   1.223 +
   1.224 +	IDBDriver *driver = NULL;
   1.225 +	const DatabaseInfo *pInfo = g_DBMan.FindDatabaseConf(conf);
   1.226 +	char error[255];
   1.227 +	if (pInfo != NULL)
   1.228 +	{
   1.229 +		if (pInfo->driver[0] == '\0')
   1.230 +		{
   1.231 +			driver = g_DBMan.GetDefaultDriver();
   1.232 +		} else {
   1.233 +			driver = g_DBMan.FindOrLoadDriver(pInfo->driver);
   1.234 +		}
   1.235 +		if (!driver)
   1.236 +		{
   1.237 +			UTIL_Format(error, 
   1.238 +				sizeof(error), 
   1.239 +				"Could not find driver \"%s\"", 
   1.240 +				pInfo->driver[0] == '\0' ? g_DBMan.GetDefaultDriverName() : pInfo->driver);
   1.241 +		} else if (!driver->IsThreadSafe()) {
   1.242 +			UTIL_Format(error,
   1.243 +				sizeof(error),
   1.244 +				"Driver \"%s\" is not thread safe!",
   1.245 +				driver->GetIdentifier());
   1.246 +		}
   1.247 +	} else {
   1.248 +		UTIL_Format(error, sizeof(error), "Could not find database conf \"%s\"", conf);
   1.249 +	}
   1.250 +
   1.251 +	if (!pInfo || !driver)
   1.252 +	{
   1.253 +		pf->PushCell(BAD_HANDLE);
   1.254 +		pf->PushCell(BAD_HANDLE);
   1.255 +		pf->PushString(error);
   1.256 +		pf->PushCell(0);
   1.257 +		pf->Execute(NULL);
   1.258 +		return 0;
   1.259 +	}
   1.260 +
   1.261 +	/* HACK! Add us to the dependency list */
   1.262 +	CExtension *pExt = g_Extensions.GetExtensionFromIdent(driver->GetIdentity());
   1.263 +	if (pExt)
   1.264 +	{
   1.265 +		g_Extensions.BindChildPlugin(pExt, g_PluginSys.FindPluginByContext(pContext->GetContext()));
   1.266 +	}
   1.267 +
   1.268 +	/* Finally, add to the thread if we can */
   1.269 +	TConnectOp *op = new TConnectOp(pf, driver, conf);
   1.270 +	CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   1.271 +	if (pPlugin->GetProperty("DisallowDBThreads", NULL)
   1.272 +		|| !g_DBMan.AddToThreadQueue(op, PrioQueue_High))
   1.273 +	{
   1.274 +		/* Do everything right now */
   1.275 +		op->RunThreadPart();
   1.276 +		op->RunThinkPart();
   1.277 +	}
   1.278 +
   1.279 +	return 1;
   1.280 +}
   1.281 +
   1.282  static cell_t SQL_ConnectEx(IPluginContext *pContext, const cell_t *params)
   1.283  {
   1.284  	IDBDriver *driver;
   1.285 @@ -380,6 +641,81 @@
   1.286  	return hndl;
   1.287  }
   1.288  
   1.289 +static cell_t SQL_TQuery(IPluginContext *pContext, const cell_t *params)
   1.290 +{
   1.291 +	IDatabase *db = NULL;
   1.292 +	HandleError err;
   1.293 +
   1.294 +	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   1.295 +		!= HandleError_None)
   1.296 +	{
   1.297 +		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   1.298 +	}
   1.299 +
   1.300 +	IPluginFunction *pf = pContext->GetFunctionById(params[2]);
   1.301 +	if (!pf)
   1.302 +	{
   1.303 +		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
   1.304 +	}
   1.305 +
   1.306 +	char *query;
   1.307 +	pContext->LocalToString(params[3], &query);
   1.308 +
   1.309 +	cell_t data = params[4];
   1.310 +	PrioQueueLevel level = PrioQueue_Normal;
   1.311 +	if (params[5] == (cell_t)PrioQueue_High)
   1.312 +	{
   1.313 +		level = PrioQueue_High;
   1.314 +	} else if (params[5] == (cell_t)PrioQueue_Low) {
   1.315 +		level = PrioQueue_Low;
   1.316 +	}
   1.317 +
   1.318 +	CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   1.319 +
   1.320 +	TQueryOp *op = new TQueryOp(db, pf, query, data);
   1.321 +	if (pPlugin->GetProperty("DisallowDBThreads", NULL)
   1.322 +		|| !g_DBMan.AddToThreadQueue(op, level))
   1.323 +	{
   1.324 +		/* Do everything right now */
   1.325 +		op->RunThreadPart();
   1.326 +		op->RunThinkPart();
   1.327 +	}
   1.328 +
   1.329 +	return 1;
   1.330 +}
   1.331 +
   1.332 +static cell_t SQL_LockDatabase(IPluginContext *pContext, const cell_t *params)
   1.333 +{
   1.334 +	IDatabase *db = NULL;
   1.335 +	HandleError err;
   1.336 +
   1.337 +	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   1.338 +		!= HandleError_None)
   1.339 +	{
   1.340 +		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   1.341 +	}
   1.342 +
   1.343 +	db->LockForFullAtomicOperation();
   1.344 +
   1.345 +	return 1;
   1.346 +}
   1.347 +
   1.348 +static cell_t SQL_UnlockDatabase(IPluginContext *pContext, const cell_t *params)
   1.349 +{
   1.350 +	IDatabase *db = NULL;
   1.351 +	HandleError err;
   1.352 +
   1.353 +	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db))
   1.354 +		!= HandleError_None)
   1.355 +	{
   1.356 +		return pContext->ThrowNativeError("Invalid database Handle %x (error: %d)", params[1], err);
   1.357 +	}
   1.358 +
   1.359 +	db->UnlockFromFullAtomicOperation();
   1.360 +
   1.361 +	return 1;
   1.362 +}
   1.363 +
   1.364  static cell_t SQL_PrepareQuery(IPluginContext *pContext, const cell_t *params)
   1.365  {
   1.366  	IDatabase *db = NULL;
   1.367 @@ -838,6 +1174,26 @@
   1.368  	return stmt->Execute() ? 1 : 0;
   1.369  }
   1.370  
   1.371 +static cell_t SQL_IsSameConnection(IPluginContext *pContext, const cell_t *params)
   1.372 +{
   1.373 +	IDatabase *db1=NULL, *db2=NULL;
   1.374 +	HandleError err;
   1.375 +
   1.376 +	if ((err = g_DBMan.ReadHandle(params[1], DBHandle_Database, (void **)&db1))
   1.377 +		!= HandleError_None)
   1.378 +	{
   1.379 +		return pContext->ThrowNativeError("Invalid database Handle 1/%x (error: %d)", params[1], err);
   1.380 +	}
   1.381 +
   1.382 +	if ((err = g_DBMan.ReadHandle(params[2], DBHandle_Database, (void **)&db2))
   1.383 +		!= HandleError_None)
   1.384 +	{
   1.385 +		return pContext->ThrowNativeError("Invalid database Handle 2/%x (error: %d)", params[2], err);
   1.386 +	}
   1.387 +
   1.388 +	return (db1 == db2) ? true : false;
   1.389 +}
   1.390 +
   1.391  REGISTER_NATIVES(dbNatives)
   1.392  {
   1.393  	{"SQL_BindParamInt",		SQL_BindParamInt},
   1.394 @@ -865,10 +1221,15 @@
   1.395  	{"SQL_GetRowCount",			SQL_GetRowCount},
   1.396  	{"SQL_HasResultSet",		SQL_HasResultSet},
   1.397  	{"SQL_IsFieldNull",			SQL_IsFieldNull},
   1.398 +	{"SQL_IsSameConnection",	SQL_IsSameConnection},
   1.399 +	{"SQL_LockDatabase",		SQL_LockDatabase},
   1.400  	{"SQL_MoreRows",			SQL_MoreRows},
   1.401 +	{"SQL_PrepareQuery",		SQL_PrepareQuery},
   1.402  	{"SQL_Query",				SQL_Query},
   1.403  	{"SQL_QuoteString",			SQL_QuoteString},
   1.404 -	{"SQL_PrepareQuery",		SQL_PrepareQuery},
   1.405  	{"SQL_Rewind",				SQL_Rewind},
   1.406 +	{"SQL_TConnect",			SQL_TConnect},
   1.407 +	{"SQL_TQuery",				SQL_TQuery},
   1.408 +	{"SQL_UnlockDatabase",		SQL_UnlockDatabase},
   1.409  	{NULL,						NULL},
   1.410  };