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 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 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 "SQLiteDatabase.h" 29 30#include "DatabaseAuthorizer.h" 31#include "Logging.h" 32#include "SQLiteFileSystem.h" 33#include "SQLiteStatement.h" 34#include <sqlite3.h> 35#include <thread> 36#include <wtf/Threading.h> 37#include <wtf/text/CString.h> 38#include <wtf/text/WTFString.h> 39 40namespace WebCore { 41 42const int SQLResultDone = SQLITE_DONE; 43const int SQLResultError = SQLITE_ERROR; 44const int SQLResultOk = SQLITE_OK; 45const int SQLResultRow = SQLITE_ROW; 46const int SQLResultSchema = SQLITE_SCHEMA; 47const int SQLResultFull = SQLITE_FULL; 48const int SQLResultInterrupt = SQLITE_INTERRUPT; 49const int SQLResultConstraint = SQLITE_CONSTRAINT; 50 51static const char notOpenErrorMessage[] = "database is not open"; 52 53SQLiteDatabase::SQLiteDatabase() 54 : m_db(0) 55 , m_pageSize(-1) 56 , m_transactionInProgress(false) 57 , m_sharable(false) 58 , m_openingThread(0) 59 , m_interrupted(false) 60 , m_openError(SQLITE_ERROR) 61 , m_openErrorMessage() 62 , m_lastChangesCount(0) 63{ 64} 65 66SQLiteDatabase::~SQLiteDatabase() 67{ 68 close(); 69} 70 71bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase) 72{ 73 close(); 74 75 m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase); 76 if (m_openError != SQLITE_OK) { 77 m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null"; 78 LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(), 79 m_openErrorMessage.data()); 80 sqlite3_close(m_db); 81 m_db = 0; 82 return false; 83 } 84 85 m_openError = sqlite3_extended_result_codes(m_db, 1); 86 if (m_openError != SQLITE_OK) { 87 m_openErrorMessage = sqlite3_errmsg(m_db); 88 LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data()); 89 sqlite3_close(m_db); 90 m_db = 0; 91 return false; 92 } 93 94 if (isOpen()) 95 m_openingThread = currentThread(); 96 else 97 m_openErrorMessage = "sqlite_open returned null"; 98 99 if (!SQLiteStatement(*this, ASCIILiteral("PRAGMA temp_store = MEMORY;")).executeCommand()) 100 LOG_ERROR("SQLite database could not set temp_store to memory"); 101 102 SQLiteStatement walStatement(*this, ASCIILiteral("PRAGMA journal_mode=WAL;")); 103 int result = walStatement.step(); 104 if (result != SQLITE_OK && result != SQLITE_ROW) 105 LOG_ERROR("SQLite database failed to set journal_mode to WAL, error: %s", lastErrorMsg()); 106 107#ifndef NDEBUG 108 if (result == SQLITE_ROW) { 109 String mode = walStatement.getColumnText(0); 110 if (!equalIgnoringCase(mode, "wal")) 111 LOG_ERROR("journal_mode of database should be 'wal', but is '%s'", mode.utf8().data()); 112 } 113#endif 114 115 return isOpen(); 116} 117 118void SQLiteDatabase::close() 119{ 120 if (m_db) { 121 // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818> 122 // ASSERT(currentThread() == m_openingThread); 123 sqlite3* db = m_db; 124 { 125 MutexLocker locker(m_databaseClosingMutex); 126 m_db = 0; 127 } 128 sqlite3_close(db); 129 } 130 131 m_openingThread = 0; 132 m_openError = SQLITE_ERROR; 133 m_openErrorMessage = CString(); 134} 135 136void SQLiteDatabase::interrupt() 137{ 138 m_interrupted = true; 139 while (!m_lockingMutex.tryLock()) { 140 MutexLocker locker(m_databaseClosingMutex); 141 if (!m_db) 142 return; 143 sqlite3_interrupt(m_db); 144 std::this_thread::yield(); 145 } 146 147 m_lockingMutex.unlock(); 148} 149 150bool SQLiteDatabase::isInterrupted() 151{ 152 ASSERT(!m_lockingMutex.tryLock()); 153 return m_interrupted; 154} 155 156void SQLiteDatabase::setFullsync(bool fsync) 157{ 158 if (fsync) 159 executeCommand(ASCIILiteral("PRAGMA fullfsync = 1;")); 160 else 161 executeCommand(ASCIILiteral("PRAGMA fullfsync = 0;")); 162} 163 164int64_t SQLiteDatabase::maximumSize() 165{ 166 int64_t maxPageCount = 0; 167 168 { 169 MutexLocker locker(m_authorizerLock); 170 enableAuthorizer(false); 171 SQLiteStatement statement(*this, ASCIILiteral("PRAGMA max_page_count")); 172 maxPageCount = statement.getColumnInt64(0); 173 enableAuthorizer(true); 174 } 175 176 return maxPageCount * pageSize(); 177} 178 179void SQLiteDatabase::setMaximumSize(int64_t size) 180{ 181 if (size < 0) 182 size = 0; 183 184 int currentPageSize = pageSize(); 185 186 ASSERT(currentPageSize || !m_db); 187 int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0; 188 189 MutexLocker locker(m_authorizerLock); 190 enableAuthorizer(false); 191 192 SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount)); 193 statement.prepare(); 194 if (statement.step() != SQLResultRow) 195 LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size)); 196 197 enableAuthorizer(true); 198 199} 200 201int SQLiteDatabase::pageSize() 202{ 203 // Since the page size of a database is locked in at creation and therefore cannot be dynamic, 204 // we can cache the value for future use 205 if (m_pageSize == -1) { 206 MutexLocker locker(m_authorizerLock); 207 enableAuthorizer(false); 208 209 SQLiteStatement statement(*this, ASCIILiteral("PRAGMA page_size")); 210 m_pageSize = statement.getColumnInt(0); 211 212 enableAuthorizer(true); 213 } 214 215 return m_pageSize; 216} 217 218int64_t SQLiteDatabase::freeSpaceSize() 219{ 220 int64_t freelistCount = 0; 221 222 { 223 MutexLocker locker(m_authorizerLock); 224 enableAuthorizer(false); 225 // Note: freelist_count was added in SQLite 3.4.1. 226 SQLiteStatement statement(*this, ASCIILiteral("PRAGMA freelist_count")); 227 freelistCount = statement.getColumnInt64(0); 228 enableAuthorizer(true); 229 } 230 231 return freelistCount * pageSize(); 232} 233 234int64_t SQLiteDatabase::totalSize() 235{ 236 int64_t pageCount = 0; 237 238 { 239 MutexLocker locker(m_authorizerLock); 240 enableAuthorizer(false); 241 SQLiteStatement statement(*this, ASCIILiteral("PRAGMA page_count")); 242 pageCount = statement.getColumnInt64(0); 243 enableAuthorizer(true); 244 } 245 246 return pageCount * pageSize(); 247} 248 249void SQLiteDatabase::setSynchronous(SynchronousPragma sync) 250{ 251 executeCommand("PRAGMA synchronous = " + String::number(sync)); 252} 253 254void SQLiteDatabase::setBusyTimeout(int ms) 255{ 256 if (m_db) 257 sqlite3_busy_timeout(m_db, ms); 258 else 259 LOG(SQLDatabase, "BusyTimeout set on non-open database"); 260} 261 262void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int)) 263{ 264 if (m_db) 265 sqlite3_busy_handler(m_db, handler, NULL); 266 else 267 LOG(SQLDatabase, "Busy handler set on non-open database"); 268} 269 270bool SQLiteDatabase::executeCommand(const String& sql) 271{ 272 return SQLiteStatement(*this, sql).executeCommand(); 273} 274 275bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql) 276{ 277 return SQLiteStatement(*this, sql).returnsAtLeastOneResult(); 278} 279 280bool SQLiteDatabase::tableExists(const String& tablename) 281{ 282 if (!isOpen()) 283 return false; 284 285 String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';"; 286 287 SQLiteStatement sql(*this, statement); 288 sql.prepare(); 289 return sql.step() == SQLITE_ROW; 290} 291 292void SQLiteDatabase::clearAllTables() 293{ 294 String query = ASCIILiteral("SELECT name FROM sqlite_master WHERE type='table';"); 295 Vector<String> tables; 296 if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) { 297 LOG(SQLDatabase, "Unable to retrieve list of tables from database"); 298 return; 299 } 300 301 for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) { 302 if (*table == "sqlite_sequence") 303 continue; 304 if (!executeCommand("DROP TABLE " + *table)) 305 LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data()); 306 } 307} 308 309int SQLiteDatabase::runVacuumCommand() 310{ 311 if (!executeCommand(ASCIILiteral("VACUUM;"))) 312 LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg()); 313 return lastError(); 314} 315 316int SQLiteDatabase::runIncrementalVacuumCommand() 317{ 318 MutexLocker locker(m_authorizerLock); 319 enableAuthorizer(false); 320 321 if (!executeCommand(ASCIILiteral("PRAGMA incremental_vacuum"))) 322 LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg()); 323 324 enableAuthorizer(true); 325 return lastError(); 326} 327 328int64_t SQLiteDatabase::lastInsertRowID() 329{ 330 if (!m_db) 331 return 0; 332 return sqlite3_last_insert_rowid(m_db); 333} 334 335void SQLiteDatabase::updateLastChangesCount() 336{ 337 if (!m_db) 338 return; 339 340 m_lastChangesCount = sqlite3_total_changes(m_db); 341} 342 343int SQLiteDatabase::lastChanges() 344{ 345 if (!m_db) 346 return 0; 347 348 return sqlite3_total_changes(m_db) - m_lastChangesCount; 349} 350 351int SQLiteDatabase::lastError() 352{ 353 return m_db ? sqlite3_errcode(m_db) : m_openError; 354} 355 356const char* SQLiteDatabase::lastErrorMsg() 357{ 358 if (m_db) 359 return sqlite3_errmsg(m_db); 360 return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data(); 361} 362 363#ifndef NDEBUG 364void SQLiteDatabase::disableThreadingChecks() 365{ 366 // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes. 367#if SQLITE_VERSION_NUMBER >= 3003001 368 m_sharable = true; 369#else 370 ASSERT(0); // Your SQLite doesn't support sharing handles across threads. 371#endif 372} 373#endif 374 375int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/) 376{ 377 DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData); 378 ASSERT(auth); 379 380 switch (actionCode) { 381 case SQLITE_CREATE_INDEX: 382 return auth->createIndex(parameter1, parameter2); 383 case SQLITE_CREATE_TABLE: 384 return auth->createTable(parameter1); 385 case SQLITE_CREATE_TEMP_INDEX: 386 return auth->createTempIndex(parameter1, parameter2); 387 case SQLITE_CREATE_TEMP_TABLE: 388 return auth->createTempTable(parameter1); 389 case SQLITE_CREATE_TEMP_TRIGGER: 390 return auth->createTempTrigger(parameter1, parameter2); 391 case SQLITE_CREATE_TEMP_VIEW: 392 return auth->createTempView(parameter1); 393 case SQLITE_CREATE_TRIGGER: 394 return auth->createTrigger(parameter1, parameter2); 395 case SQLITE_CREATE_VIEW: 396 return auth->createView(parameter1); 397 case SQLITE_DELETE: 398 return auth->allowDelete(parameter1); 399 case SQLITE_DROP_INDEX: 400 return auth->dropIndex(parameter1, parameter2); 401 case SQLITE_DROP_TABLE: 402 return auth->dropTable(parameter1); 403 case SQLITE_DROP_TEMP_INDEX: 404 return auth->dropTempIndex(parameter1, parameter2); 405 case SQLITE_DROP_TEMP_TABLE: 406 return auth->dropTempTable(parameter1); 407 case SQLITE_DROP_TEMP_TRIGGER: 408 return auth->dropTempTrigger(parameter1, parameter2); 409 case SQLITE_DROP_TEMP_VIEW: 410 return auth->dropTempView(parameter1); 411 case SQLITE_DROP_TRIGGER: 412 return auth->dropTrigger(parameter1, parameter2); 413 case SQLITE_DROP_VIEW: 414 return auth->dropView(parameter1); 415 case SQLITE_INSERT: 416 return auth->allowInsert(parameter1); 417 case SQLITE_PRAGMA: 418 return auth->allowPragma(parameter1, parameter2); 419 case SQLITE_READ: 420 return auth->allowRead(parameter1, parameter2); 421 case SQLITE_SELECT: 422 return auth->allowSelect(); 423 case SQLITE_TRANSACTION: 424 return auth->allowTransaction(); 425 case SQLITE_UPDATE: 426 return auth->allowUpdate(parameter1, parameter2); 427 case SQLITE_ATTACH: 428 return auth->allowAttach(parameter1); 429 case SQLITE_DETACH: 430 return auth->allowDetach(parameter1); 431 case SQLITE_ALTER_TABLE: 432 return auth->allowAlterTable(parameter1, parameter2); 433 case SQLITE_REINDEX: 434 return auth->allowReindex(parameter1); 435#if SQLITE_VERSION_NUMBER >= 3003013 436 case SQLITE_ANALYZE: 437 return auth->allowAnalyze(parameter1); 438 case SQLITE_CREATE_VTABLE: 439 return auth->createVTable(parameter1, parameter2); 440 case SQLITE_DROP_VTABLE: 441 return auth->dropVTable(parameter1, parameter2); 442 case SQLITE_FUNCTION: 443 return auth->allowFunction(parameter2); 444#endif 445 default: 446 ASSERT_NOT_REACHED(); 447 return SQLAuthDeny; 448 } 449} 450 451void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth) 452{ 453 if (!m_db) { 454 LOG_ERROR("Attempt to set an authorizer on a non-open SQL database"); 455 ASSERT_NOT_REACHED(); 456 return; 457 } 458 459 MutexLocker locker(m_authorizerLock); 460 461 m_authorizer = auth; 462 463 enableAuthorizer(true); 464} 465 466void SQLiteDatabase::enableAuthorizer(bool enable) 467{ 468 if (m_authorizer && enable) 469 sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get()); 470 else 471 sqlite3_set_authorizer(m_db, NULL, 0); 472} 473 474bool SQLiteDatabase::isAutoCommitOn() const 475{ 476 return sqlite3_get_autocommit(m_db); 477} 478 479bool SQLiteDatabase::turnOnIncrementalAutoVacuum() 480{ 481 SQLiteStatement statement(*this, ASCIILiteral("PRAGMA auto_vacuum")); 482 int autoVacuumMode = statement.getColumnInt(0); 483 int error = lastError(); 484 485 // Check if we got an error while trying to get the value of the auto_vacuum flag. 486 // If we got a SQLITE_BUSY error, then there's probably another transaction in 487 // progress on this database. In this case, keep the current value of the 488 // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this 489 // database. If the error is not SQLITE_BUSY, then we probably ran into a more 490 // serious problem and should return false (to log an error message). 491 if (error != SQLITE_ROW) 492 return false; 493 494 switch (autoVacuumMode) { 495 case AutoVacuumIncremental: 496 return true; 497 case AutoVacuumFull: 498 return executeCommand(ASCIILiteral("PRAGMA auto_vacuum = 2")); 499 case AutoVacuumNone: 500 default: 501 if (!executeCommand(ASCIILiteral("PRAGMA auto_vacuum = 2"))) 502 return false; 503 runVacuumCommand(); 504 error = lastError(); 505 return (error == SQLITE_OK); 506 } 507} 508 509static void destroyCollationFunction(void* arg) 510{ 511 auto f = static_cast<std::function<int(int, const void*, int, const void*)>*>(arg); 512 delete f; 513} 514 515static int callCollationFunction(void* arg, int aLength, const void* a, int bLength, const void* b) 516{ 517 auto f = static_cast<std::function<int(int, const void*, int, const void*)>*>(arg); 518 return (*f)(aLength, a, bLength, b); 519} 520 521void SQLiteDatabase::setCollationFunction(const String& collationName, std::function<int(int, const void*, int, const void*)> collationFunction) 522{ 523 auto functionObject = new std::function<int(int, const void*, int, const void*)>(collationFunction); 524 sqlite3_create_collation_v2(m_db, collationName.utf8().data(), SQLITE_UTF8, functionObject, callCollationFunction, destroyCollationFunction); 525} 526 527void SQLiteDatabase::removeCollationFunction(const String& collationName) 528{ 529 sqlite3_create_collation_v2(m_db, collationName.utf8().data(), SQLITE_UTF8, nullptr, nullptr, nullptr); 530} 531 532} // namespace WebCore 533