1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/extensions/sqlite/driver/SqDriver.cpp Sun Jul 29 01:15:31 2007 +0000
1.3 @@ -0,0 +1,244 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * ===============================================================
1.7 + * SourceMod SQLite Driver Extension
1.8 + * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
1.9 + * ===============================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or
1.12 + * modify it under the terms of the GNU General Public License
1.13 + * as published by the Free Software Foundation; either version 2
1.14 + * of the License, or (at your option) any later version.
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program; if not, write to the Free Software
1.22 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1.23 + *
1.24 + * Version: $Id$
1.25 + */
1.26 +
1.27 +#include <sm_platform.h>
1.28 +#include "extension.h"
1.29 +#include "SqDriver.h"
1.30 +#include "SqDatabase.h"
1.31 +
1.32 +SqDriver g_SqDriver;
1.33 +
1.34 +unsigned int strncopy(char *dest, const char *src, size_t count)
1.35 +{
1.36 + if (!count)
1.37 + {
1.38 + return 0;
1.39 + }
1.40 +
1.41 + char *start = dest;
1.42 + while ((*src) && (--count))
1.43 + {
1.44 + *dest++ = *src++;
1.45 + }
1.46 + *dest = '\0';
1.47 +
1.48 + return (dest - start);
1.49 +}
1.50 +
1.51 +SqDriver::SqDriver()
1.52 +{
1.53 + m_Handle = BAD_HANDLE;
1.54 + m_pOpenLock = NULL;
1.55 +}
1.56 +
1.57 +void SqDriver::Initialize()
1.58 +{
1.59 + m_pOpenLock = threader->MakeMutex();
1.60 +}
1.61 +
1.62 +void SqDriver::Shutdown()
1.63 +{
1.64 + if (m_pOpenLock)
1.65 + {
1.66 + m_pOpenLock->DestroyThis();
1.67 + }
1.68 +}
1.69 +
1.70 +bool SqDriver::IsThreadSafe()
1.71 +{
1.72 + return true;
1.73 +}
1.74 +
1.75 +bool SqDriver::InitializeThreadSafety()
1.76 +{
1.77 + /* sqlite should be thread safe if the locks are done right.
1.78 + * we don't enable the "shared cache" because it can corrupt
1.79 + * open databases!
1.80 + */
1.81 + return true;
1.82 +}
1.83 +
1.84 +void SqDriver::ShutdownThreadSafety()
1.85 +{
1.86 + return;
1.87 +}
1.88 +
1.89 +IdentityToken_t *SqDriver::GetIdentity()
1.90 +{
1.91 + return myself->GetIdentity();
1.92 +}
1.93 +
1.94 +const char *SqDriver::GetProductName()
1.95 +{
1.96 + return "SQLite";
1.97 +}
1.98 +
1.99 +const char *SqDriver::GetIdentifier()
1.100 +{
1.101 + return "sqlite";
1.102 +}
1.103 +
1.104 +Handle_t SqDriver::GetHandle()
1.105 +{
1.106 + if (m_Handle == BAD_HANDLE)
1.107 + {
1.108 + m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
1.109 + }
1.110 +
1.111 + return m_Handle;
1.112 +}
1.113 +
1.114 +inline bool IsPathSepChar(char c)
1.115 +{
1.116 +#if defined PLATFORM_WINDOWS
1.117 + return (c == '\\' || c == '/');
1.118 +#elif defined PLATFORM_LINUX
1.119 + return (c == '/');
1.120 +#endif
1.121 +}
1.122 +
1.123 +IDatabase *SqDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
1.124 +{
1.125 + /* We wrap most of the open process in a mutex just to be safe */
1.126 + m_pOpenLock->Lock();
1.127 +
1.128 + /* Format our path */
1.129 + char path[PLATFORM_MAX_PATH];
1.130 + size_t len = libsys->PathFormat(path, sizeof(path), "sqlite/%s", info->database);
1.131 +
1.132 + /* Chop any filename off */
1.133 + for (size_t i = len-1;
1.134 + i >= 0 && i <= len-1;
1.135 + i--)
1.136 + {
1.137 + if (IsPathSepChar(path[i]))
1.138 + {
1.139 + path[i] = '\0';
1.140 + break;
1.141 + }
1.142 + }
1.143 +
1.144 + /* Test the full path */
1.145 + char fullpath[PLATFORM_MAX_PATH];
1.146 + g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/%s", path);
1.147 + if (!libsys->IsPathDirectory(fullpath))
1.148 + {
1.149 + /* Make sure the data folder exists */
1.150 + len = g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data");
1.151 + if (!libsys->IsPathDirectory(fullpath))
1.152 + {
1.153 + if (!libsys->CreateFolder(fullpath))
1.154 + {
1.155 + strncopy(error, "Could not create or open \"data\" folder\"", maxlength);
1.156 + m_pOpenLock->Unlock();
1.157 + return NULL;
1.158 + }
1.159 + }
1.160 +
1.161 + /* The data folder exists - create each subdir as needed! */
1.162 + char *cur_ptr = path;
1.163 +
1.164 + do
1.165 + {
1.166 + /* Find the next suitable path */
1.167 + char *next_ptr = cur_ptr;
1.168 + while (*next_ptr != '\0')
1.169 + {
1.170 + if (IsPathSepChar(*next_ptr))
1.171 + {
1.172 + *next_ptr = '\0';
1.173 + next_ptr++;
1.174 + break;
1.175 + }
1.176 + next_ptr++;
1.177 + }
1.178 + if (*next_ptr == '\0')
1.179 + {
1.180 + next_ptr = NULL;
1.181 + }
1.182 + len += libsys->PathFormat(&fullpath[len], sizeof(fullpath)-len, "/%s", cur_ptr);
1.183 + if (!libsys->IsPathDirectory(fullpath) && !libsys->CreateFolder(fullpath))
1.184 + {
1.185 + break;
1.186 + }
1.187 + cur_ptr = next_ptr;
1.188 + } while (cur_ptr);
1.189 + }
1.190 +
1.191 + /* Build the FINAL path. */
1.192 + g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/sqlite/%s.sq3", info->database);
1.193 +
1.194 + /* If we're requesting a persistent connection, see if something is already open */
1.195 + if (persistent)
1.196 + {
1.197 + /* See if anything in the cache matches */
1.198 + List<SqDbInfo>::iterator iter;
1.199 + for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
1.200 + {
1.201 + if ((*iter).path.compare(fullpath) == 0)
1.202 + {
1.203 + (*iter).db->IncReferenceCount();
1.204 + m_pOpenLock->Unlock();
1.205 + return (*iter).db;
1.206 + }
1.207 + }
1.208 + }
1.209 +
1.210 + /* Try to open a new connection */
1.211 + sqlite3 *sql;
1.212 + int err = sqlite3_open(fullpath, &sql);
1.213 + if (err != SQLITE_OK)
1.214 + {
1.215 + strncopy(error, sqlite3_errmsg(sql), maxlength);
1.216 + sqlite3_close(sql);
1.217 + m_pOpenLock->Unlock();
1.218 + return NULL;
1.219 + }
1.220 +
1.221 + SqDatabase *pdb = new SqDatabase(sql, persistent);
1.222 +
1.223 + if (persistent)
1.224 + {
1.225 + SqDbInfo pinfo;
1.226 + pinfo.path = fullpath;
1.227 + pinfo.db = pdb;
1.228 + m_Cache.push_back(pinfo);
1.229 + }
1.230 +
1.231 + m_pOpenLock->Unlock();
1.232 +
1.233 + return pdb;
1.234 +}
1.235 +
1.236 +void SqDriver::RemovePersistent(IDatabase *pdb)
1.237 +{
1.238 + List<SqDbInfo>::iterator iter;
1.239 + for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
1.240 + {
1.241 + if ((*iter).db == pdb)
1.242 + {
1.243 + iter = m_Cache.erase(iter);
1.244 + return;
1.245 + }
1.246 + }
1.247 +}