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 };