diff -r 000000000000 -r b7aa2b626807 extensions/sqlite/driver/SqDriver.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/extensions/sqlite/driver/SqDriver.cpp Sun Mar 30 07:00:22 2008 +0000
@@ -0,0 +1,285 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod SQLite Extension
+ * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include
+#include "extension.h"
+#include "SqDriver.h"
+#include "SqDatabase.h"
+
+SqDriver g_SqDriver;
+
+unsigned int strncopy(char *dest, const char *src, size_t count)
+{
+ if (!count)
+ {
+ return 0;
+ }
+
+ char *start = dest;
+ while ((*src) && (--count))
+ {
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+
+ return (dest - start);
+}
+
+int busy_handler(void *unused1, int unused2)
+{
+#if defined PLATFORM_WINDOWS
+ Sleep(100);
+#elif defined PLATFORM_LINUX
+ usleep(100000);
+#endif
+ return 1;
+}
+
+SqDriver::SqDriver()
+{
+ m_Handle = BAD_HANDLE;
+ m_pOpenLock = NULL;
+ m_bThreadSafe = false;
+}
+
+void SqDriver::Initialize()
+{
+ m_pOpenLock = threader->MakeMutex();
+
+ InitializeThreadSafety();
+}
+
+void SqDriver::Shutdown()
+{
+ if (m_pOpenLock)
+ {
+ m_pOpenLock->DestroyThis();
+ }
+
+ if (m_bThreadSafe)
+ {
+ sqlite3_enable_shared_cache(0);
+ }
+}
+
+bool SqDriver::IsThreadSafe()
+{
+ return true;
+}
+
+bool SqDriver::InitializeThreadSafety()
+{
+ if (m_bThreadSafe)
+ {
+ return true;
+ }
+
+ if (sqlite3_threadsafe() == 0)
+ {
+ return false;
+ }
+
+ if (sqlite3_enable_shared_cache(1) != SQLITE_OK)
+ {
+ return false;
+ }
+
+ m_bThreadSafe = true;
+
+ return true;
+}
+
+void SqDriver::ShutdownThreadSafety()
+{
+ return;
+}
+
+IdentityToken_t *SqDriver::GetIdentity()
+{
+ return myself->GetIdentity();
+}
+
+const char *SqDriver::GetProductName()
+{
+ return "SQLite";
+}
+
+const char *SqDriver::GetIdentifier()
+{
+ return "sqlite";
+}
+
+Handle_t SqDriver::GetHandle()
+{
+ if (m_Handle == BAD_HANDLE)
+ {
+ m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
+ }
+
+ return m_Handle;
+}
+
+inline bool IsPathSepChar(char c)
+{
+#if defined PLATFORM_WINDOWS
+ return (c == '\\' || c == '/');
+#elif defined PLATFORM_LINUX
+ return (c == '/');
+#endif
+}
+
+IDatabase *SqDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
+{
+ /* We wrap most of the open process in a mutex just to be safe */
+ m_pOpenLock->Lock();
+
+ /* Format our path */
+ char path[PLATFORM_MAX_PATH];
+ size_t len = libsys->PathFormat(path, sizeof(path), "sqlite/%s", info->database);
+
+ /* Chop any filename off */
+ for (size_t i = len-1;
+ i >= 0 && i <= len-1;
+ i--)
+ {
+ if (IsPathSepChar(path[i]))
+ {
+ path[i] = '\0';
+ break;
+ }
+ }
+
+ /* Test the full path */
+ char fullpath[PLATFORM_MAX_PATH];
+ g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/%s", path);
+ if (!libsys->IsPathDirectory(fullpath))
+ {
+ /* Make sure the data folder exists */
+ len = g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data");
+ if (!libsys->IsPathDirectory(fullpath))
+ {
+ if (!libsys->CreateFolder(fullpath))
+ {
+ strncopy(error, "Could not create or open \"data\" folder\"", maxlength);
+ m_pOpenLock->Unlock();
+ return NULL;
+ }
+ }
+
+ /* The data folder exists - create each subdir as needed! */
+ char *cur_ptr = path;
+
+ do
+ {
+ /* Find the next suitable path */
+ char *next_ptr = cur_ptr;
+ while (*next_ptr != '\0')
+ {
+ if (IsPathSepChar(*next_ptr))
+ {
+ *next_ptr = '\0';
+ next_ptr++;
+ break;
+ }
+ next_ptr++;
+ }
+ if (*next_ptr == '\0')
+ {
+ next_ptr = NULL;
+ }
+ len += libsys->PathFormat(&fullpath[len], sizeof(fullpath)-len, "/%s", cur_ptr);
+ if (!libsys->IsPathDirectory(fullpath) && !libsys->CreateFolder(fullpath))
+ {
+ break;
+ }
+ cur_ptr = next_ptr;
+ } while (cur_ptr);
+ }
+
+ /* Build the FINAL path. */
+ g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/sqlite/%s.sq3", info->database);
+
+ /* If we're requesting a persistent connection, see if something is already open */
+ if (persistent)
+ {
+ /* See if anything in the cache matches */
+ List::iterator iter;
+ for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
+ {
+ if ((*iter).path.compare(fullpath) == 0)
+ {
+ (*iter).db->IncReferenceCount();
+ m_pOpenLock->Unlock();
+ return (*iter).db;
+ }
+ }
+ }
+
+ /* Try to open a new connection */
+ sqlite3 *sql;
+ int err = sqlite3_open(fullpath, &sql);
+ if (err != SQLITE_OK)
+ {
+ strncopy(error, sqlite3_errmsg(sql), maxlength);
+ sqlite3_close(sql);
+ m_pOpenLock->Unlock();
+ return NULL;
+ }
+
+ sqlite3_busy_handler(sql, busy_handler, NULL);
+
+ SqDatabase *pdb = new SqDatabase(sql, persistent);
+
+ if (persistent)
+ {
+ SqDbInfo pinfo;
+ pinfo.path = fullpath;
+ pinfo.db = pdb;
+ m_Cache.push_back(pinfo);
+ }
+
+ m_pOpenLock->Unlock();
+
+ return pdb;
+}
+
+void SqDriver::RemovePersistent(IDatabase *pdb)
+{
+ List::iterator iter;
+ for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
+ {
+ if ((*iter).db == pdb)
+ {
+ iter = m_Cache.erase(iter);
+ return;
+ }
+ }
+}