extensions/sqlite/driver/SqDriver.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 2289 44045ead61cd
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod SQLite Extension
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <sm_platform.h>
    33 #include "extension.h"
    34 #include "SqDriver.h"
    35 #include "SqDatabase.h"
    36 
    37 SqDriver g_SqDriver;
    38 
    39 unsigned int strncopy(char *dest, const char *src, size_t count)
    40 {
    41 	if (!count)
    42 	{
    43 		return 0;
    44 	}
    45 
    46 	char *start = dest;
    47 	while ((*src) && (--count))
    48 	{
    49 		*dest++ = *src++;
    50 	}
    51 	*dest = '\0';
    52 
    53 	return (dest - start);
    54 }
    55 
    56 int busy_handler(void *unused1, int unused2)
    57 {
    58 #if defined PLATFORM_WINDOWS
    59 	Sleep(100);
    60 #elif defined PLATFORM_LINUX
    61 	usleep(100000);
    62 #endif
    63 	return 1;
    64 }
    65 
    66 SqDriver::SqDriver()
    67 {
    68 	m_Handle = BAD_HANDLE;
    69 	m_pOpenLock = NULL;
    70 	m_bThreadSafe = false;
    71 }
    72 
    73 void SqDriver::Initialize()
    74 {
    75 	m_pOpenLock = threader->MakeMutex();
    76 
    77 	InitializeThreadSafety();
    78 }
    79 
    80 void SqDriver::Shutdown()
    81 {
    82 	if (m_pOpenLock)
    83 	{
    84 		m_pOpenLock->DestroyThis();
    85 	}
    86 
    87 	if (m_bThreadSafe)
    88 	{
    89 		sqlite3_enable_shared_cache(0);
    90 	}
    91 }
    92 
    93 bool SqDriver::IsThreadSafe()
    94 {
    95 	return true;
    96 }
    97 
    98 bool SqDriver::InitializeThreadSafety()
    99 {
   100 	if (m_bThreadSafe)
   101 	{
   102 		return true;
   103 	}
   104 
   105 	if (sqlite3_threadsafe() == 0)
   106 	{
   107 		return false;
   108 	}
   109 
   110 	if (sqlite3_enable_shared_cache(1) != SQLITE_OK)
   111 	{
   112 		return false;
   113 	}
   114 
   115 	m_bThreadSafe = true;
   116 
   117 	return true;
   118 }
   119 
   120 void SqDriver::ShutdownThreadSafety()
   121 {
   122 	return;
   123 }
   124 
   125 IdentityToken_t *SqDriver::GetIdentity()
   126 {
   127 	return myself->GetIdentity();
   128 }
   129 
   130 const char *SqDriver::GetProductName()
   131 {
   132 	return "SQLite";
   133 }
   134 
   135 const char *SqDriver::GetIdentifier()
   136 {
   137 	return "sqlite";
   138 }
   139 
   140 Handle_t SqDriver::GetHandle()
   141 {
   142 	if (m_Handle == BAD_HANDLE)
   143 	{
   144 		m_Handle = dbi->CreateHandle(DBHandle_Driver, this, myself->GetIdentity());
   145 	}
   146 
   147 	return m_Handle;
   148 }
   149 
   150 inline bool IsPathSepChar(char c)
   151 {
   152 #if defined PLATFORM_WINDOWS
   153 	return (c == '\\' || c == '/');
   154 #elif defined PLATFORM_LINUX
   155 	return (c == '/');
   156 #endif
   157 }
   158 
   159 IDatabase *SqDriver::Connect(const DatabaseInfo *info, bool persistent, char *error, size_t maxlength)
   160 {
   161 	/* We wrap most of the open process in a mutex just to be safe */
   162 	m_pOpenLock->Lock();
   163 
   164 	/* Format our path */
   165 	char path[PLATFORM_MAX_PATH];
   166 	size_t len = libsys->PathFormat(path, sizeof(path), "sqlite/%s", info->database);
   167 
   168 	/* Chop any filename off */
   169 	for (size_t i = len-1;
   170 		 i >= 0 && i <= len-1;
   171 		 i--)
   172 	{
   173 		if (IsPathSepChar(path[i]))
   174 		{
   175 			path[i] = '\0';
   176 			break;
   177 		}
   178 	}
   179 
   180 	/* Test the full path */
   181 	char fullpath[PLATFORM_MAX_PATH];
   182 	g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/%s", path);
   183 	if (!libsys->IsPathDirectory(fullpath))
   184 	{
   185 		/* Make sure the data folder exists */
   186 		len = g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data");
   187 		if (!libsys->IsPathDirectory(fullpath))
   188 		{
   189 			if (!libsys->CreateFolder(fullpath))
   190 			{
   191 				strncopy(error, "Could not create or open \"data\" folder\"", maxlength);
   192 				m_pOpenLock->Unlock();
   193 				return NULL;
   194 			}
   195 		}
   196 
   197 		/* The data folder exists - create each subdir as needed! */
   198 		char *cur_ptr = path;
   199 
   200 		do
   201 		{
   202 			/* Find the next suitable path */
   203 			char *next_ptr = cur_ptr;
   204 			while (*next_ptr != '\0')
   205 			{
   206 				if (IsPathSepChar(*next_ptr))
   207 				{
   208 					*next_ptr = '\0';
   209 					next_ptr++;
   210 					break;
   211 				}
   212 				next_ptr++;
   213 			}
   214 			if (*next_ptr == '\0')
   215 			{
   216 				next_ptr = NULL;
   217 			}
   218 			len += libsys->PathFormat(&fullpath[len], sizeof(fullpath)-len, "/%s", cur_ptr);
   219 			if (!libsys->IsPathDirectory(fullpath) && !libsys->CreateFolder(fullpath))
   220 			{
   221 				break;
   222 			}
   223 			cur_ptr = next_ptr;
   224 		} while (cur_ptr);
   225 	}
   226 
   227 	/* Build the FINAL path. */
   228 	g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "data/sqlite/%s.sq3", info->database);
   229 
   230 	/* If we're requesting a persistent connection, see if something is already open */
   231 	if (persistent)
   232 	{
   233 		/* See if anything in the cache matches */
   234 		List<SqDbInfo>::iterator iter;
   235 		for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
   236 		{
   237 			if ((*iter).path.compare(fullpath) == 0)
   238 			{
   239 				(*iter).db->IncReferenceCount();
   240 				m_pOpenLock->Unlock();
   241 				return (*iter).db;
   242 			}
   243 		}
   244 	}
   245 
   246 	/* Try to open a new connection */
   247 	sqlite3 *sql;
   248 	int err = sqlite3_open(fullpath, &sql);
   249 	if (err != SQLITE_OK)
   250 	{
   251 		strncopy(error, sqlite3_errmsg(sql), maxlength);
   252 		sqlite3_close(sql);
   253 		m_pOpenLock->Unlock();
   254 		return NULL;
   255 	}
   256 
   257 	sqlite3_busy_handler(sql, busy_handler, NULL);
   258 
   259 	SqDatabase *pdb = new SqDatabase(sql, persistent);
   260 
   261 	if (persistent)
   262 	{
   263 		SqDbInfo pinfo;
   264 		pinfo.path = fullpath;
   265 		pinfo.db = pdb;
   266 		m_Cache.push_back(pinfo);
   267 	}
   268 
   269 	m_pOpenLock->Unlock();
   270 
   271 	return pdb;
   272 }
   273 
   274 void SqDriver::RemovePersistent(IDatabase *pdb)
   275 {
   276 	List<SqDbInfo>::iterator iter;
   277 	for (iter = m_Cache.begin(); iter != m_Cache.end(); iter++)
   278 	{
   279 		if ((*iter).db == pdb)
   280 		{
   281 			iter = m_Cache.erase(iter);
   282 			return;
   283 		}
   284 	}
   285 }