1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * Copyright (C) 2013 Apple Inc. All rights reserved. 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 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include "config.h" 31#include "DatabaseBackendBase.h" 32 33#if ENABLE(SQL_DATABASE) 34 35#include "DatabaseAuthorizer.h" 36#include "DatabaseBackendContext.h" 37#include "DatabaseBase.h" 38#include "DatabaseContext.h" 39#include "DatabaseManager.h" 40#include "DatabaseTracker.h" 41#include "ExceptionCode.h" 42#include "Logging.h" 43#include "SQLiteStatement.h" 44#include "SQLiteTransaction.h" 45#include "SecurityOrigin.h" 46#include <wtf/HashMap.h> 47#include <wtf/HashSet.h> 48#include <wtf/PassRefPtr.h> 49#include <wtf/RefPtr.h> 50#include <wtf/StdLibExtras.h> 51#include <wtf/text/CString.h> 52#include <wtf/text/StringHash.h> 53 54// Registering "opened" databases with the DatabaseTracker 55// ======================================================= 56// The DatabaseTracker maintains a list of databases that have been 57// "opened" so that the client can call interrupt or delete on every database 58// associated with a DatabaseBackendContext. 59// 60// We will only call DatabaseTracker::addOpenDatabase() to add the database 61// to the tracker as opened when we've succeeded in opening the database, 62// and will set m_opened to true. Similarly, we only call 63// DatabaseTracker::removeOpenDatabase() to remove the database from the 64// tracker when we set m_opened to false in closeDatabase(). This sets up 65// a simple symmetry between open and close operations, and a direct 66// correlation to adding and removing databases from the tracker's list, 67// thus ensuring that we have a correct list for the interrupt and 68// delete operations to work on. 69// 70// The only databases instances not tracked by the tracker's open database 71// list are the ones that have not been added yet, or the ones that we 72// attempted an open on but failed to. Such instances only exist in the 73// DatabaseServer's factory methods for creating database backends. 74// 75// The factory methods will either call openAndVerifyVersion() or 76// performOpenAndVerify(). These methods will add the newly instantiated 77// database backend if they succeed in opening the requested database. 78// In the case of failure to open the database, the factory methods will 79// simply discard the newly instantiated database backend when they return. 80// The ref counting mechanims will automatically destruct the un-added 81// (and un-returned) databases instances. 82 83namespace WebCore { 84 85static const char versionKey[] = "WebKitDatabaseVersionKey"; 86static const char infoTableName[] = "__WebKitDatabaseInfoTable__"; 87 88static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage) 89{ 90 return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage); 91} 92 93static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) 94{ 95 SQLiteStatement statement(db, query); 96 int result = statement.prepare(); 97 98 if (result != SQLResultOk) { 99 LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); 100 return false; 101 } 102 103 result = statement.step(); 104 if (result == SQLResultRow) { 105 resultString = statement.getColumnText(0); 106 return true; 107 } 108 if (result == SQLResultDone) { 109 resultString = String(); 110 return true; 111 } 112 113 LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); 114 return false; 115} 116 117static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) 118{ 119 SQLiteStatement statement(db, query); 120 int result = statement.prepare(); 121 122 if (result != SQLResultOk) { 123 LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); 124 return false; 125 } 126 127 statement.bindText(1, value); 128 129 result = statement.step(); 130 if (result != SQLResultDone) { 131 LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); 132 return false; 133 } 134 135 return true; 136} 137 138// FIXME: move all guid-related functions to a DatabaseVersionTracker class. 139static Mutex& guidMutex() 140{ 141 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); 142 return mutex; 143} 144 145typedef HashMap<DatabaseGuid, String> GuidVersionMap; 146static GuidVersionMap& guidToVersionMap() 147{ 148 // Ensure the the mutex is locked. 149 ASSERT(!guidMutex().tryLock()); 150 DEFINE_STATIC_LOCAL(GuidVersionMap, map, ()); 151 return map; 152} 153 154// NOTE: Caller must lock guidMutex(). 155static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion) 156{ 157 // Ensure the the mutex is locked. 158 ASSERT(!guidMutex().tryLock()); 159 160 // Note: It is not safe to put an empty string into the guidToVersionMap() map. 161 // That's because the map is cross-thread, but empty strings are per-thread. 162 // The copy() function makes a version of the string you can use on the current 163 // thread, but we need a string we can keep in a cross-thread data structure. 164 // FIXME: This is a quite-awkward restriction to have to program with. 165 166 // Map null string to empty string (see comment above). 167 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy()); 168} 169 170typedef HashMap<DatabaseGuid, HashSet<DatabaseBackendBase*>*> GuidDatabaseMap; 171static GuidDatabaseMap& guidToDatabaseMap() 172{ 173 // Ensure the the mutex is locked. 174 ASSERT(!guidMutex().tryLock()); 175 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); 176 return map; 177} 178 179static DatabaseGuid guidForOriginAndName(const String& origin, const String& name) 180{ 181 // Ensure the the mutex is locked. 182 ASSERT(!guidMutex().tryLock()); 183 184 String stringID = origin + "/" + name; 185 186 typedef HashMap<String, int> IDGuidMap; 187 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); 188 DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID); 189 if (!guid) { 190 static int currentNewGUID = 1; 191 guid = currentNewGUID++; 192 stringIdentifierToGUIDMap.set(stringID, guid); 193 } 194 195 return guid; 196} 197 198// static 199const char* DatabaseBackendBase::databaseInfoTableName() 200{ 201 return infoTableName; 202} 203 204#if !LOG_DISABLED || !ERROR_DISABLED 205String DatabaseBackendBase::databaseDebugName() const 206{ 207 return m_contextThreadSecurityOrigin->toString() + "::" + m_name; 208} 209#endif 210 211DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, 212 const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType) 213 : m_databaseContext(databaseContext) 214 , m_name(name.isolatedCopy()) 215 , m_expectedVersion(expectedVersion.isolatedCopy()) 216 , m_displayName(displayName.isolatedCopy()) 217 , m_estimatedSize(estimatedSize) 218 , m_guid(0) 219 , m_opened(false) 220 , m_new(false) 221 , m_isSyncDatabase(databaseType == DatabaseType::Sync) 222{ 223 m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); 224 225 m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); 226 227 if (m_name.isNull()) 228 m_name = ""; 229 230 { 231 MutexLocker locker(guidMutex()); 232 m_guid = guidForOriginAndName(securityOrigin()->toString(), name); 233 HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid); 234 if (!hashSet) { 235 hashSet = new HashSet<DatabaseBackendBase*>; 236 guidToDatabaseMap().set(m_guid, hashSet); 237 } 238 239 hashSet->add(this); 240 } 241 242 m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); 243} 244 245DatabaseBackendBase::~DatabaseBackendBase() 246{ 247 // SQLite is "multi-thread safe", but each database handle can only be used 248 // on a single thread at a time. 249 // 250 // For DatabaseBackend, we open the SQLite database on the DatabaseThread, 251 // and hence we should also close it on that same thread. This means that the 252 // SQLite database need to be closed by another mechanism (see 253 // DatabaseContext::stopDatabases()). By the time we get here, the SQLite 254 // database should have already been closed. 255 256 ASSERT(!m_opened); 257} 258 259void DatabaseBackendBase::closeDatabase() 260{ 261 if (!m_opened) 262 return; 263 264 m_sqliteDatabase.close(); 265 m_opened = false; 266 // See comment at the top this file regarding calling removeOpenDatabase(). 267 DatabaseTracker::tracker().removeOpenDatabase(this); 268 { 269 MutexLocker locker(guidMutex()); 270 271 HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid); 272 ASSERT(hashSet); 273 ASSERT(hashSet->contains(this)); 274 hashSet->remove(this); 275 if (hashSet->isEmpty()) { 276 guidToDatabaseMap().remove(m_guid); 277 delete hashSet; 278 guidToVersionMap().remove(m_guid); 279 } 280 } 281} 282 283String DatabaseBackendBase::version() const 284{ 285 // Note: In multi-process browsers the cached value may be accurate, but we cannot read the 286 // actual version from the database without potentially inducing a deadlock. 287 // FIXME: Add an async version getter to the DatabaseAPI. 288 return getCachedVersion(); 289} 290 291class DoneCreatingDatabaseOnExitCaller { 292public: 293 DoneCreatingDatabaseOnExitCaller(DatabaseBackendBase* database) 294 : m_database(database) 295 , m_openSucceeded(false) 296 { 297 } 298 ~DoneCreatingDatabaseOnExitCaller() 299 { 300 DatabaseTracker::tracker().doneCreatingDatabase(m_database); 301 } 302 303 void setOpenSucceeded() { m_openSucceeded = true; } 304 305private: 306 DatabaseBackendBase* m_database; 307 bool m_openSucceeded; 308}; 309 310bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) 311{ 312 DoneCreatingDatabaseOnExitCaller onExitCaller(this); 313 ASSERT(errorMessage.isEmpty()); 314 ASSERT(error == DatabaseError::None); // Better not have any errors already. 315 error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. 316 317 const int maxSqliteBusyWaitTime = 30000; 318 319 if (!m_sqliteDatabase.open(m_filename, true)) { 320 errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 321 return false; 322 } 323 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) 324 LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 325 326 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); 327 328 String currentVersion; 329 { 330 MutexLocker locker(guidMutex()); 331 332 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); 333 if (entry != guidToVersionMap().end()) { 334 // Map null string to empty string (see updateGuidVersionMap()). 335 currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); 336 LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); 337 } else { 338 LOG(StorageAPI, "No cached version for guid %i", m_guid); 339 340 SQLiteTransaction transaction(m_sqliteDatabase); 341 transaction.begin(); 342 if (!transaction.inProgress()) { 343 errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 344 m_sqliteDatabase.close(); 345 return false; 346 } 347 348 String tableName(infoTableName); 349 if (!m_sqliteDatabase.tableExists(tableName)) { 350 m_new = true; 351 352 if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { 353 errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 354 transaction.rollback(); 355 m_sqliteDatabase.close(); 356 return false; 357 } 358 } else if (!getVersionFromDatabase(currentVersion, false)) { 359 errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 360 transaction.rollback(); 361 m_sqliteDatabase.close(); 362 return false; 363 } 364 365 if (currentVersion.length()) { 366 LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); 367 } else if (!m_new || shouldSetVersionInNewDatabase) { 368 LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 369 if (!setVersionInDatabase(m_expectedVersion, false)) { 370 errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 371 transaction.rollback(); 372 m_sqliteDatabase.close(); 373 return false; 374 } 375 currentVersion = m_expectedVersion; 376 } 377 updateGuidVersionMap(m_guid, currentVersion); 378 transaction.commit(); 379 } 380 } 381 382 if (currentVersion.isNull()) { 383 LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); 384 currentVersion = ""; 385 } 386 387 // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. 388 // If the expected version is the empty string, then we always return with whatever version of the database we have. 389 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { 390 errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; 391 m_sqliteDatabase.close(); 392 return false; 393 } 394 395 ASSERT(m_databaseAuthorizer); 396 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); 397 398 // See comment at the top this file regarding calling addOpenDatabase(). 399 DatabaseTracker::tracker().addOpenDatabase(this); 400 m_opened = true; 401 402 // Declare success: 403 error = DatabaseError::None; // Clear the presumed error from above. 404 onExitCaller.setOpenSucceeded(); 405 406 if (m_new && !shouldSetVersionInNewDatabase) 407 m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. 408 return true; 409} 410 411SecurityOrigin* DatabaseBackendBase::securityOrigin() const 412{ 413 return m_contextThreadSecurityOrigin.get(); 414} 415 416String DatabaseBackendBase::stringIdentifier() const 417{ 418 // Return a deep copy for ref counting thread safety 419 return m_name.isolatedCopy(); 420} 421 422String DatabaseBackendBase::displayName() const 423{ 424 // Return a deep copy for ref counting thread safety 425 return m_displayName.isolatedCopy(); 426} 427 428unsigned long DatabaseBackendBase::estimatedSize() const 429{ 430 return m_estimatedSize; 431} 432 433String DatabaseBackendBase::fileName() const 434{ 435 // Return a deep copy for ref counting thread safety 436 return m_filename.isolatedCopy(); 437} 438 439DatabaseDetails DatabaseBackendBase::details() const 440{ 441 return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0); 442} 443 444bool DatabaseBackendBase::getVersionFromDatabase(String& version, bool shouldCacheVersion) 445{ 446 String query(String("SELECT value FROM ") + infoTableName + " WHERE key = '" + versionKey + "';"); 447 448 m_databaseAuthorizer->disable(); 449 450 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version); 451 if (result) { 452 if (shouldCacheVersion) 453 setCachedVersion(version); 454 } else 455 LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); 456 457 m_databaseAuthorizer->enable(); 458 459 return result; 460} 461 462bool DatabaseBackendBase::setVersionInDatabase(const String& version, bool shouldCacheVersion) 463{ 464 // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE 465 // clause in the CREATE statement (see Database::performOpenAndVerify()). 466 String query(String("INSERT INTO ") + infoTableName + " (key, value) VALUES ('" + versionKey + "', ?);"); 467 468 m_databaseAuthorizer->disable(); 469 470 bool result = setTextValueInDatabase(m_sqliteDatabase, query, version); 471 if (result) { 472 if (shouldCacheVersion) 473 setCachedVersion(version); 474 } else 475 LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data()); 476 477 m_databaseAuthorizer->enable(); 478 479 return result; 480} 481 482void DatabaseBackendBase::setExpectedVersion(const String& version) 483{ 484 m_expectedVersion = version.isolatedCopy(); 485} 486 487String DatabaseBackendBase::getCachedVersion() const 488{ 489 MutexLocker locker(guidMutex()); 490 return guidToVersionMap().get(m_guid).isolatedCopy(); 491} 492 493void DatabaseBackendBase::setCachedVersion(const String& actualVersion) 494{ 495 // Update the in memory database version map. 496 MutexLocker locker(guidMutex()); 497 updateGuidVersionMap(m_guid, actualVersion); 498} 499 500bool DatabaseBackendBase::getActualVersionForTransaction(String &actualVersion) 501{ 502 ASSERT(m_sqliteDatabase.transactionInProgress()); 503 // Note: In multi-process browsers the cached value may be inaccurate. 504 // So we retrieve the value from the database and update the cached value here. 505 return getVersionFromDatabase(actualVersion, true); 506} 507 508void DatabaseBackendBase::disableAuthorizer() 509{ 510 ASSERT(m_databaseAuthorizer); 511 m_databaseAuthorizer->disable(); 512} 513 514void DatabaseBackendBase::enableAuthorizer() 515{ 516 ASSERT(m_databaseAuthorizer); 517 m_databaseAuthorizer->enable(); 518} 519 520void DatabaseBackendBase::setAuthorizerReadOnly() 521{ 522 ASSERT(m_databaseAuthorizer); 523 m_databaseAuthorizer->setReadOnly(); 524} 525 526void DatabaseBackendBase::setAuthorizerPermissions(int permissions) 527{ 528 ASSERT(m_databaseAuthorizer); 529 m_databaseAuthorizer->setPermissions(permissions); 530} 531 532bool DatabaseBackendBase::lastActionChangedDatabase() 533{ 534 ASSERT(m_databaseAuthorizer); 535 return m_databaseAuthorizer->lastActionChangedDatabase(); 536} 537 538bool DatabaseBackendBase::lastActionWasInsert() 539{ 540 ASSERT(m_databaseAuthorizer); 541 return m_databaseAuthorizer->lastActionWasInsert(); 542} 543 544void DatabaseBackendBase::resetDeletes() 545{ 546 ASSERT(m_databaseAuthorizer); 547 m_databaseAuthorizer->resetDeletes(); 548} 549 550bool DatabaseBackendBase::hadDeletes() 551{ 552 ASSERT(m_databaseAuthorizer); 553 return m_databaseAuthorizer->hadDeletes(); 554} 555 556void DatabaseBackendBase::resetAuthorizer() 557{ 558 if (m_databaseAuthorizer) 559 m_databaseAuthorizer->reset(); 560} 561 562unsigned long long DatabaseBackendBase::maximumSize() const 563{ 564 return DatabaseTracker::tracker().getMaxSizeForDatabase(this); 565} 566 567void DatabaseBackendBase::incrementalVacuumIfNeeded() 568{ 569 int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); 570 int64_t totalSize = m_sqliteDatabase.totalSize(); 571 if (totalSize <= 10 * freeSpaceSize) { 572 int result = m_sqliteDatabase.runIncrementalVacuumCommand(); 573 if (result != SQLResultOk) 574 m_frontend->logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg())); 575 } 576} 577 578void DatabaseBackendBase::interrupt() 579{ 580 m_sqliteDatabase.interrupt(); 581} 582 583bool DatabaseBackendBase::isInterrupted() 584{ 585 MutexLocker locker(m_sqliteDatabase.databaseMutex()); 586 return m_sqliteDatabase.isInterrupted(); 587} 588 589} // namespace WebCore 590 591#endif // ENABLE(SQL_DATABASE) 592