3 * ===============================================================
4 * SourceMod SQLite Driver Extension
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ===============================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include <sm_platform.h>
25 #include "extension.h"
27 #include "SqDatabase.h"
31 unsigned int strncopy(char *dest, const char *src, size_t count)
39 while ((*src) && (--count))
45 return (dest - start);
50 m_Handle = BAD_HANDLE;
54 void SqDriver::Initialize()
56 m_pOpenLock = threader->MakeMutex();
59 void SqDriver::Shutdown()
63 m_pOpenLock->DestroyThis();
67 bool SqDriver::IsThreadSafe()
72 bool SqDriver::InitializeThreadSafety()
74 /* sqlite should be thread safe if the locks are done right.
75 * we don't enable the "shared cache" because it can corrupt
81 void SqDriver::ShutdownThreadSafety()
86 IdentityToken_t *SqDriver::GetIdentity()
88 return myself->GetIdentity();
91 const char *SqDriver::GetProductName()
96 const char *SqDriver::GetIdentifier()
101 Handle_t SqDriver::GetHandle()
103 if (m_Handle == BAD_HANDLE)
105 m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
111 inline bool IsPathSepChar(char c)
113 #if defined PLATFORM_WINDOWS
114 return (c == '\\' || c == '/');
115 #elif defined PLATFORM_LINUX
120 IDatabase *SqDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
122 /* We wrap most of the open process in a mutex just to be safe */
125 /* Format our path */
126 char path[PLATFORM_MAX_PATH];
127 size_t len = libsys->PathFormat(path, sizeof(path), "sqlite/%s", info->database);
129 /* Chop any filename off */
130 for (size_t i = len-1;
131 i >= 0 && i <= len-1;
134 if (IsPathSepChar(path[i]))
141 /* Test the full path */
142 char fullpath[PLATFORM_MAX_PATH];
143 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/%s", path);
144 if (!libsys->IsPathDirectory(fullpath))
146 /* Make sure the data folder exists */
147 len = g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data");
148 if (!libsys->IsPathDirectory(fullpath))
150 if (!libsys->CreateFolder(fullpath))
152 strncopy(error, "Could not create or open \"data\" folder\"", maxlength);
153 m_pOpenLock->Unlock();
158 /* The data folder exists - create each subdir as needed! */
159 char *cur_ptr = path;
163 /* Find the next suitable path */
164 char *next_ptr = cur_ptr;
165 while (*next_ptr != '\0')
167 if (IsPathSepChar(*next_ptr))
175 if (*next_ptr == '\0')
179 len += libsys->PathFormat(&fullpath[len], sizeof(fullpath)-len, "/%s", cur_ptr);
180 if (!libsys->IsPathDirectory(fullpath) && !libsys->CreateFolder(fullpath))
188 /* Build the FINAL path. */
189 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/sqlite/%s.sq3", info->database);
191 /* If we're requesting a persistent connection, see if something is already open */
194 /* See if anything in the cache matches */
195 List<SqDbInfo>::iterator iter;
196 for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
198 if ((*iter).path.compare(fullpath) == 0)
200 (*iter).db->IncReferenceCount();
201 m_pOpenLock->Unlock();
207 /* Try to open a new connection */
209 int err = sqlite3_open(fullpath, &sql);
210 if (err != SQLITE_OK)
212 strncopy(error, sqlite3_errmsg(sql), maxlength);
214 m_pOpenLock->Unlock();
218 SqDatabase *pdb = new SqDatabase(sql, persistent);
223 pinfo.path = fullpath;
225 m_Cache.push_back(pinfo);
228 m_pOpenLock->Unlock();
233 void SqDriver::RemovePersistent(IDatabase *pdb)
235 List<SqDbInfo>::iterator iter;
236 for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
238 if ((*iter).db == pdb)
240 iter = m_Cache.erase(iter);