1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/extensions/sqlite/driver/SqDriver.cpp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,285 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod SQLite Extension
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include <sm_platform.h>
1.36 +#include "extension.h"
1.37 +#include "SqDriver.h"
1.38 +#include "SqDatabase.h"
1.39 +
1.40 +SqDriver g_SqDriver;
1.41 +
1.42 +unsigned int strncopy(char *dest, const char *src, size_t count)
1.43 +{
1.44 + if (!count)
1.45 + {
1.46 + return 0;
1.47 + }
1.48 +
1.49 + char *start = dest;
1.50 + while ((*src) && (--count))
1.51 + {
1.52 + *dest++ = *src++;
1.53 + }
1.54 + *dest = '\0';
1.55 +
1.56 + return (dest - start);
1.57 +}
1.58 +
1.59 +int busy_handler(void *unused1, int unused2)
1.60 +{
1.61 +#if defined PLATFORM_WINDOWS
1.62 + Sleep(100);
1.63 +#elif defined PLATFORM_LINUX
1.64 + usleep(100000);
1.65 +#endif
1.66 + return 1;
1.67 +}
1.68 +
1.69 +SqDriver::SqDriver()
1.70 +{
1.71 + m_Handle = BAD_HANDLE;
1.72 + m_pOpenLock = NULL;
1.73 + m_bThreadSafe = false;
1.74 +}
1.75 +
1.76 +void SqDriver::Initialize()
1.77 +{
1.78 + m_pOpenLock = threader->MakeMutex();
1.79 +
1.80 + InitializeThreadSafety();
1.81 +}
1.82 +
1.83 +void SqDriver::Shutdown()
1.84 +{
1.85 + if (m_pOpenLock)
1.86 + {
1.87 + m_pOpenLock->DestroyThis();
1.88 + }
1.89 +
1.90 + if (m_bThreadSafe)
1.91 + {
1.92 + sqlite3_enable_shared_cache(0);
1.93 + }
1.94 +}
1.95 +
1.96 +bool SqDriver::IsThreadSafe()
1.97 +{
1.98 + return true;
1.99 +}
1.100 +
1.101 +bool SqDriver::InitializeThreadSafety()
1.102 +{
1.103 + if (m_bThreadSafe)
1.104 + {
1.105 + return true;
1.106 + }
1.107 +
1.108 + if (sqlite3_threadsafe() == 0)
1.109 + {
1.110 + return false;
1.111 + }
1.112 +
1.113 + if (sqlite3_enable_shared_cache(1) != SQLITE_OK)
1.114 + {
1.115 + return false;
1.116 + }
1.117 +
1.118 + m_bThreadSafe = true;
1.119 +
1.120 + return true;
1.121 +}
1.122 +
1.123 +void SqDriver::ShutdownThreadSafety()
1.124 +{
1.125 + return;
1.126 +}
1.127 +
1.128 +IdentityToken_t *SqDriver::GetIdentity()
1.129 +{
1.130 + return myself->GetIdentity();
1.131 +}
1.132 +
1.133 +const char *SqDriver::GetProductName()
1.134 +{
1.135 + return "SQLite";
1.136 +}
1.137 +
1.138 +const char *SqDriver::GetIdentifier()
1.139 +{
1.140 + return "sqlite";
1.141 +}
1.142 +
1.143 +Handle_t SqDriver::GetHandle()
1.144 +{
1.145 + if (m_Handle == BAD_HANDLE)
1.146 + {
1.147 + m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
1.148 + }
1.149 +
1.150 + return m_Handle;
1.151 +}
1.152 +
1.153 +inline bool IsPathSepChar(char c)
1.154 +{
1.155 +#if defined PLATFORM_WINDOWS
1.156 + return (c == '\\' || c == '/');
1.157 +#elif defined PLATFORM_LINUX
1.158 + return (c == '/');
1.159 +#endif
1.160 +}
1.161 +
1.162 +IDatabase *SqDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
1.163 +{
1.164 + /* We wrap most of the open process in a mutex just to be safe */
1.165 + m_pOpenLock->Lock();
1.166 +
1.167 + /* Format our path */
1.168 + char path[PLATFORM_MAX_PATH];
1.169 + size_t len = libsys->PathFormat(path, sizeof(path), "sqlite/%s", info->database);
1.170 +
1.171 + /* Chop any filename off */
1.172 + for (size_t i = len-1;
1.173 + i >= 0 && i <= len-1;
1.174 + i--)
1.175 + {
1.176 + if (IsPathSepChar(path[i]))
1.177 + {
1.178 + path[i] = '\0';
1.179 + break;
1.180 + }
1.181 + }
1.182 +
1.183 + /* Test the full path */
1.184 + char fullpath[PLATFORM_MAX_PATH];
1.185 + g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/%s", path);
1.186 + if (!libsys->IsPathDirectory(fullpath))
1.187 + {
1.188 + /* Make sure the data folder exists */
1.189 + len = g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data");
1.190 + if (!libsys->IsPathDirectory(fullpath))
1.191 + {
1.192 + if (!libsys->CreateFolder(fullpath))
1.193 + {
1.194 + strncopy(error, "Could not create or open \"data\" folder\"", maxlength);
1.195 + m_pOpenLock->Unlock();
1.196 + return NULL;
1.197 + }
1.198 + }
1.199 +
1.200 + /* The data folder exists - create each subdir as needed! */
1.201 + char *cur_ptr = path;
1.202 +
1.203 + do
1.204 + {
1.205 + /* Find the next suitable path */
1.206 + char *next_ptr = cur_ptr;
1.207 + while (*next_ptr != '\0')
1.208 + {
1.209 + if (IsPathSepChar(*next_ptr))
1.210 + {
1.211 + *next_ptr = '\0';
1.212 + next_ptr++;
1.213 + break;
1.214 + }
1.215 + next_ptr++;
1.216 + }
1.217 + if (*next_ptr == '\0')
1.218 + {
1.219 + next_ptr = NULL;
1.220 + }
1.221 + len += libsys->PathFormat(&fullpath[len], sizeof(fullpath)-len, "/%s", cur_ptr);
1.222 + if (!libsys->IsPathDirectory(fullpath) && !libsys->CreateFolder(fullpath))
1.223 + {
1.224 + break;
1.225 + }
1.226 + cur_ptr = next_ptr;
1.227 + } while (cur_ptr);
1.228 + }
1.229 +
1.230 + /* Build the FINAL path. */
1.231 + g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/sqlite/%s.sq3", info->database);
1.232 +
1.233 + /* If we're requesting a persistent connection, see if something is already open */
1.234 + if (persistent)
1.235 + {
1.236 + /* See if anything in the cache matches */
1.237 + List<SqDbInfo>::iterator iter;
1.238 + for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
1.239 + {
1.240 + if ((*iter).path.compare(fullpath) == 0)
1.241 + {
1.242 + (*iter).db->IncReferenceCount();
1.243 + m_pOpenLock->Unlock();
1.244 + return (*iter).db;
1.245 + }
1.246 + }
1.247 + }
1.248 +
1.249 + /* Try to open a new connection */
1.250 + sqlite3 *sql;
1.251 + int err = sqlite3_open(fullpath, &sql);
1.252 + if (err != SQLITE_OK)
1.253 + {
1.254 + strncopy(error, sqlite3_errmsg(sql), maxlength);
1.255 + sqlite3_close(sql);
1.256 + m_pOpenLock->Unlock();
1.257 + return NULL;
1.258 + }
1.259 +
1.260 + sqlite3_busy_handler(sql, busy_handler, NULL);
1.261 +
1.262 + SqDatabase *pdb = new SqDatabase(sql, persistent);
1.263 +
1.264 + if (persistent)
1.265 + {
1.266 + SqDbInfo pinfo;
1.267 + pinfo.path = fullpath;
1.268 + pinfo.db = pdb;
1.269 + m_Cache.push_back(pinfo);
1.270 + }
1.271 +
1.272 + m_pOpenLock->Unlock();
1.273 +
1.274 + return pdb;
1.275 +}
1.276 +
1.277 +void SqDriver::RemovePersistent(IDatabase *pdb)
1.278 +{
1.279 + List<SqDbInfo>::iterator iter;
1.280 + for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
1.281 + {
1.282 + if ((*iter).db == pdb)
1.283 + {
1.284 + iter = m_Cache.erase(iter);
1.285 + return;
1.286 + }
1.287 + }
1.288 +}