1/* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "modules/webdatabase/sqlite/SQLiteDatabase.h" 29 30#include <sqlite3.h> 31#include "platform/Logging.h" 32#include "modules/webdatabase/sqlite/SQLiteFileSystem.h" 33#include "modules/webdatabase/sqlite/SQLiteStatement.h" 34#include "modules/webdatabase/DatabaseAuthorizer.h" 35 36namespace WebCore { 37 38const int SQLResultDone = SQLITE_DONE; 39const int SQLResultOk = SQLITE_OK; 40const int SQLResultRow = SQLITE_ROW; 41const int SQLResultFull = SQLITE_FULL; 42const int SQLResultInterrupt = SQLITE_INTERRUPT; 43const int SQLResultConstraint = SQLITE_CONSTRAINT; 44 45static const char notOpenErrorMessage[] = "database is not open"; 46 47SQLiteDatabase::SQLiteDatabase() 48 : m_db(0) 49 , m_pageSize(-1) 50 , m_transactionInProgress(false) 51 , m_sharable(false) 52 , m_openingThread(0) 53 , m_interrupted(false) 54 , m_openError(SQLITE_ERROR) 55 , m_openErrorMessage() 56 , m_lastChangesCount(0) 57{ 58} 59 60SQLiteDatabase::~SQLiteDatabase() 61{ 62 close(); 63} 64 65bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase) 66{ 67 close(); 68 69 m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase); 70 if (m_openError != SQLITE_OK) { 71 m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null"; 72 WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(), 73 m_openErrorMessage.data()); 74 sqlite3_close(m_db); 75 m_db = 0; 76 return false; 77 } 78 79 m_openError = sqlite3_extended_result_codes(m_db, 1); 80 if (m_openError != SQLITE_OK) { 81 m_openErrorMessage = sqlite3_errmsg(m_db); 82 WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data()); 83 sqlite3_close(m_db); 84 m_db = 0; 85 return false; 86 } 87 88 if (isOpen()) 89 m_openingThread = currentThread(); 90 else 91 m_openErrorMessage = "sqlite_open returned null"; 92 93 if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand()) 94 WTF_LOG_ERROR("SQLite database could not set temp_store to memory"); 95 96 return isOpen(); 97} 98 99void SQLiteDatabase::close() 100{ 101 if (m_db) { 102 // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818> 103 // ASSERT(currentThread() == m_openingThread); 104 sqlite3* db = m_db; 105 { 106 MutexLocker locker(m_databaseClosingMutex); 107 m_db = 0; 108 } 109 sqlite3_close(db); 110 } 111 112 m_openingThread = 0; 113 m_openError = SQLITE_ERROR; 114 m_openErrorMessage = CString(); 115} 116 117void SQLiteDatabase::interrupt() 118{ 119 m_interrupted = true; 120 while (!m_lockingMutex.tryLock()) { 121 MutexLocker locker(m_databaseClosingMutex); 122 if (!m_db) 123 return; 124 sqlite3_interrupt(m_db); 125 yield(); 126 } 127 128 m_lockingMutex.unlock(); 129} 130 131bool SQLiteDatabase::isInterrupted() 132{ 133 ASSERT(!m_lockingMutex.tryLock()); 134 return m_interrupted; 135} 136 137void SQLiteDatabase::setMaximumSize(int64_t size) 138{ 139 if (size < 0) 140 size = 0; 141 142 int currentPageSize = pageSize(); 143 144 ASSERT(currentPageSize || !m_db); 145 int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0; 146 147 MutexLocker locker(m_authorizerLock); 148 enableAuthorizer(false); 149 150 SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount)); 151 statement.prepare(); 152 if (statement.step() != SQLResultRow) 153#if OS(WIN) 154 WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size)); 155#else 156 WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size)); 157#endif 158 159 enableAuthorizer(true); 160 161} 162 163int SQLiteDatabase::pageSize() 164{ 165 // Since the page size of a database is locked in at creation and therefore cannot be dynamic, 166 // we can cache the value for future use 167 if (m_pageSize == -1) { 168 MutexLocker locker(m_authorizerLock); 169 enableAuthorizer(false); 170 171 SQLiteStatement statement(*this, "PRAGMA page_size"); 172 m_pageSize = statement.getColumnInt(0); 173 174 enableAuthorizer(true); 175 } 176 177 return m_pageSize; 178} 179 180int64_t SQLiteDatabase::freeSpaceSize() 181{ 182 int64_t freelistCount = 0; 183 184 { 185 MutexLocker locker(m_authorizerLock); 186 enableAuthorizer(false); 187 // Note: freelist_count was added in SQLite 3.4.1. 188 SQLiteStatement statement(*this, "PRAGMA freelist_count"); 189 freelistCount = statement.getColumnInt64(0); 190 enableAuthorizer(true); 191 } 192 193 return freelistCount * pageSize(); 194} 195 196int64_t SQLiteDatabase::totalSize() 197{ 198 int64_t pageCount = 0; 199 200 { 201 MutexLocker locker(m_authorizerLock); 202 enableAuthorizer(false); 203 SQLiteStatement statement(*this, "PRAGMA page_count"); 204 pageCount = statement.getColumnInt64(0); 205 enableAuthorizer(true); 206 } 207 208 return pageCount * pageSize(); 209} 210 211void SQLiteDatabase::setBusyTimeout(int ms) 212{ 213 if (m_db) 214 sqlite3_busy_timeout(m_db, ms); 215 else 216 WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database"); 217} 218 219bool SQLiteDatabase::executeCommand(const String& sql) 220{ 221 return SQLiteStatement(*this, sql).executeCommand(); 222} 223 224bool SQLiteDatabase::tableExists(const String& tablename) 225{ 226 if (!isOpen()) 227 return false; 228 229 String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';"; 230 231 SQLiteStatement sql(*this, statement); 232 sql.prepare(); 233 return sql.step() == SQLITE_ROW; 234} 235 236int SQLiteDatabase::runVacuumCommand() 237{ 238 if (!executeCommand("VACUUM;")) 239 WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg()); 240 return lastError(); 241} 242 243int SQLiteDatabase::runIncrementalVacuumCommand() 244{ 245 MutexLocker locker(m_authorizerLock); 246 enableAuthorizer(false); 247 248 if (!executeCommand("PRAGMA incremental_vacuum")) 249 WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg()); 250 251 enableAuthorizer(true); 252 return lastError(); 253} 254 255int64_t SQLiteDatabase::lastInsertRowID() 256{ 257 if (!m_db) 258 return 0; 259 return sqlite3_last_insert_rowid(m_db); 260} 261 262void SQLiteDatabase::updateLastChangesCount() 263{ 264 if (!m_db) 265 return; 266 267 m_lastChangesCount = sqlite3_total_changes(m_db); 268} 269 270int SQLiteDatabase::lastChanges() 271{ 272 if (!m_db) 273 return 0; 274 275 return sqlite3_total_changes(m_db) - m_lastChangesCount; 276} 277 278int SQLiteDatabase::lastError() 279{ 280 return m_db ? sqlite3_errcode(m_db) : m_openError; 281} 282 283const char* SQLiteDatabase::lastErrorMsg() 284{ 285 if (m_db) 286 return sqlite3_errmsg(m_db); 287 return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data(); 288} 289 290int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/) 291{ 292 DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData); 293 ASSERT(auth); 294 295 switch (actionCode) { 296 case SQLITE_CREATE_INDEX: 297 return auth->createIndex(parameter1, parameter2); 298 case SQLITE_CREATE_TABLE: 299 return auth->createTable(parameter1); 300 case SQLITE_CREATE_TEMP_INDEX: 301 return auth->createTempIndex(parameter1, parameter2); 302 case SQLITE_CREATE_TEMP_TABLE: 303 return auth->createTempTable(parameter1); 304 case SQLITE_CREATE_TEMP_TRIGGER: 305 return auth->createTempTrigger(parameter1, parameter2); 306 case SQLITE_CREATE_TEMP_VIEW: 307 return auth->createTempView(parameter1); 308 case SQLITE_CREATE_TRIGGER: 309 return auth->createTrigger(parameter1, parameter2); 310 case SQLITE_CREATE_VIEW: 311 return auth->createView(parameter1); 312 case SQLITE_DELETE: 313 return auth->allowDelete(parameter1); 314 case SQLITE_DROP_INDEX: 315 return auth->dropIndex(parameter1, parameter2); 316 case SQLITE_DROP_TABLE: 317 return auth->dropTable(parameter1); 318 case SQLITE_DROP_TEMP_INDEX: 319 return auth->dropTempIndex(parameter1, parameter2); 320 case SQLITE_DROP_TEMP_TABLE: 321 return auth->dropTempTable(parameter1); 322 case SQLITE_DROP_TEMP_TRIGGER: 323 return auth->dropTempTrigger(parameter1, parameter2); 324 case SQLITE_DROP_TEMP_VIEW: 325 return auth->dropTempView(parameter1); 326 case SQLITE_DROP_TRIGGER: 327 return auth->dropTrigger(parameter1, parameter2); 328 case SQLITE_DROP_VIEW: 329 return auth->dropView(parameter1); 330 case SQLITE_INSERT: 331 return auth->allowInsert(parameter1); 332 case SQLITE_PRAGMA: 333 return auth->allowPragma(parameter1, parameter2); 334 case SQLITE_READ: 335 return auth->allowRead(parameter1, parameter2); 336 case SQLITE_SELECT: 337 return auth->allowSelect(); 338 case SQLITE_TRANSACTION: 339 return auth->allowTransaction(); 340 case SQLITE_UPDATE: 341 return auth->allowUpdate(parameter1, parameter2); 342 case SQLITE_ATTACH: 343 return auth->allowAttach(parameter1); 344 case SQLITE_DETACH: 345 return auth->allowDetach(parameter1); 346 case SQLITE_ALTER_TABLE: 347 return auth->allowAlterTable(parameter1, parameter2); 348 case SQLITE_REINDEX: 349 return auth->allowReindex(parameter1); 350#if SQLITE_VERSION_NUMBER >= 3003013 351 case SQLITE_ANALYZE: 352 return auth->allowAnalyze(parameter1); 353 case SQLITE_CREATE_VTABLE: 354 return auth->createVTable(parameter1, parameter2); 355 case SQLITE_DROP_VTABLE: 356 return auth->dropVTable(parameter1, parameter2); 357 case SQLITE_FUNCTION: 358 return auth->allowFunction(parameter2); 359#endif 360 default: 361 ASSERT_NOT_REACHED(); 362 return SQLAuthDeny; 363 } 364} 365 366void SQLiteDatabase::setAuthorizer(DatabaseAuthorizer* auth) 367{ 368 if (!m_db) { 369 WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database"); 370 ASSERT_NOT_REACHED(); 371 return; 372 } 373 374 MutexLocker locker(m_authorizerLock); 375 376 m_authorizer = auth; 377 378 enableAuthorizer(true); 379} 380 381void SQLiteDatabase::enableAuthorizer(bool enable) 382{ 383 if (m_authorizer && enable) 384 sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get()); 385 else 386 sqlite3_set_authorizer(m_db, NULL, 0); 387} 388 389bool SQLiteDatabase::isAutoCommitOn() const 390{ 391 return sqlite3_get_autocommit(m_db); 392} 393 394bool SQLiteDatabase::turnOnIncrementalAutoVacuum() 395{ 396 SQLiteStatement statement(*this, "PRAGMA auto_vacuum"); 397 int autoVacuumMode = statement.getColumnInt(0); 398 int error = lastError(); 399 400 // Check if we got an error while trying to get the value of the auto_vacuum flag. 401 // If we got a SQLITE_BUSY error, then there's probably another transaction in 402 // progress on this database. In this case, keep the current value of the 403 // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this 404 // database. If the error is not SQLITE_BUSY, then we probably ran into a more 405 // serious problem and should return false (to log an error message). 406 if (error != SQLITE_ROW) 407 return false; 408 409 switch (autoVacuumMode) { 410 case AutoVacuumIncremental: 411 return true; 412 case AutoVacuumFull: 413 return executeCommand("PRAGMA auto_vacuum = 2"); 414 case AutoVacuumNone: 415 default: 416 if (!executeCommand("PRAGMA auto_vacuum = 2")) 417 return false; 418 runVacuumCommand(); 419 error = lastError(); 420 return (error == SQLITE_OK); 421 } 422} 423 424void SQLiteDatabase::trace(Visitor* visitor) 425{ 426 visitor->trace(m_authorizer); 427} 428 429} // namespace WebCore 430