1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "modules/webdatabase/Database.h" 28 29#include "core/dom/CrossThreadTask.h" 30#include "core/dom/ExceptionCode.h" 31#include "core/html/VoidCallback.h" 32#include "modules/webdatabase/ChangeVersionData.h" 33#include "modules/webdatabase/ChangeVersionWrapper.h" 34#include "modules/webdatabase/DatabaseAuthorizer.h" 35#include "modules/webdatabase/DatabaseContext.h" 36#include "modules/webdatabase/DatabaseManager.h" 37#include "modules/webdatabase/DatabaseTask.h" 38#include "modules/webdatabase/DatabaseThread.h" 39#include "modules/webdatabase/DatabaseTracker.h" 40#include "modules/webdatabase/SQLError.h" 41#include "modules/webdatabase/SQLTransaction.h" 42#include "modules/webdatabase/SQLTransactionBackend.h" 43#include "modules/webdatabase/SQLTransactionCallback.h" 44#include "modules/webdatabase/SQLTransactionClient.h" 45#include "modules/webdatabase/SQLTransactionCoordinator.h" 46#include "modules/webdatabase/SQLTransactionErrorCallback.h" 47#include "modules/webdatabase/sqlite/SQLiteStatement.h" 48#include "modules/webdatabase/sqlite/SQLiteTransaction.h" 49#include "platform/Logging.h" 50#include "platform/weborigin/DatabaseIdentifier.h" 51#include "public/platform/Platform.h" 52#include "public/platform/WebDatabaseObserver.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 DatabaseContext. 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 blink { 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 WTF_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 WTF_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 WTF_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 WTF_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 RecursiveMutex& guidMutex() 140{ 141 AtomicallyInitializedStatic(RecursiveMutex&, mutex = *new RecursiveMutex); 142 return mutex; 143} 144 145typedef HashMap<DatabaseGuid, String> GuidVersionMap; 146static GuidVersionMap& guidToVersionMap() 147{ 148 // Ensure the the mutex is locked. 149 ASSERT(guidMutex().locked()); 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().locked()); 159 160 // Note: It is not safe to put an empty string into the guidToVersionMap() 161 // map. That's because the map is cross-thread, but empty strings are 162 // per-thread. The copy() function makes a version of the string you can 163 // use on the current thread, but we need a string we can keep in a 164 // cross-thread data structure. 165 // FIXME: This is a quite-awkward restriction to have to program with. 166 167 // Map null string to empty string (see comment above). 168 guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy()); 169} 170 171typedef HashMap<DatabaseGuid, HashSet<Database*>*> GuidDatabaseMap; 172static GuidDatabaseMap& guidToDatabaseMap() 173{ 174 // Ensure the the mutex is locked. 175 ASSERT(guidMutex().locked()); 176 DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); 177 return map; 178} 179 180static DatabaseGuid guidForOriginAndName(const String& origin, const String& name) 181{ 182 // Ensure the the mutex is locked. 183 ASSERT(guidMutex().locked()); 184 185 String stringID = origin + "/" + name; 186 187 typedef HashMap<String, int> IDGuidMap; 188 DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); 189 DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID); 190 if (!guid) { 191 static int currentNewGUID = 1; 192 guid = currentNewGUID++; 193 stringIdentifierToGUIDMap.set(stringID, guid); 194 } 195 196 return guid; 197} 198 199Database::Database(DatabaseContext* databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) 200 : m_databaseContext(databaseContext) 201 , m_name(name.isolatedCopy()) 202 , m_expectedVersion(expectedVersion.isolatedCopy()) 203 , m_displayName(displayName.isolatedCopy()) 204 , m_estimatedSize(estimatedSize) 205 , m_guid(0) 206 , m_opened(false) 207 , m_new(false) 208 , m_transactionInProgress(false) 209 , m_isTransactionQueueEnabled(true) 210{ 211 m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); 212 213 m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); 214 215 if (m_name.isNull()) 216 m_name = ""; 217 218 { 219 SafePointAwareMutexLocker locker(guidMutex()); 220 m_guid = guidForOriginAndName(securityOrigin()->toString(), name); 221 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); 222 if (!hashSet) { 223 hashSet = new HashSet<Database*>; 224 guidToDatabaseMap().set(m_guid, hashSet); 225 } 226 227 hashSet->add(this); 228 } 229 230 m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); 231 232 m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy(); 233 ASSERT(m_databaseContext->databaseThread()); 234 ASSERT(m_databaseContext->isContextThread()); 235} 236 237Database::~Database() 238{ 239 // SQLite is "multi-thread safe", but each database handle can only be used 240 // on a single thread at a time. 241 // 242 // For Database, we open the SQLite database on the DatabaseThread, and 243 // hence we should also close it on that same thread. This means that the 244 // SQLite database need to be closed by another mechanism (see 245 // DatabaseContext::stopDatabases()). By the time we get here, the SQLite 246 // database should have already been closed. 247 248 ASSERT(!m_opened); 249} 250 251void Database::trace(Visitor* visitor) 252{ 253 visitor->trace(m_databaseContext); 254 visitor->trace(m_sqliteDatabase); 255 visitor->trace(m_databaseAuthorizer); 256 visitor->trace(m_transactionQueue); 257} 258 259bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage) 260{ 261 TaskSynchronizer synchronizer; 262 if (!databaseContext()->databaseThreadAvailable()) 263 return false; 264 265 DatabaseTracker::tracker().prepareToOpenDatabase(this); 266 bool success = false; 267 OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, error, errorMessage, success); 268 databaseContext()->databaseThread()->scheduleTask(task.release()); 269 synchronizer.waitForTaskCompletion(); 270 271 return success; 272} 273 274void Database::close() 275{ 276 ASSERT(databaseContext()->databaseThread()); 277 ASSERT(databaseContext()->databaseThread()->isDatabaseThread()); 278 279 { 280 MutexLocker locker(m_transactionInProgressMutex); 281 282 // Clean up transactions that have not been scheduled yet: 283 // Transaction phase 1 cleanup. See comment on "What happens if a 284 // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. 285 RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = nullptr; 286 while (!m_transactionQueue.isEmpty()) { 287 transaction = m_transactionQueue.takeFirst(); 288 transaction->notifyDatabaseThreadIsShuttingDown(); 289 } 290 291 m_isTransactionQueueEnabled = false; 292 m_transactionInProgress = false; 293 } 294 295 closeDatabase(); 296 databaseContext()->databaseThread()->recordDatabaseClosed(this); 297} 298 299PassRefPtrWillBeRawPtr<SQLTransactionBackend> Database::runTransaction(PassRefPtrWillBeRawPtr<SQLTransaction> transaction, 300 bool readOnly, const ChangeVersionData* data) 301{ 302 MutexLocker locker(m_transactionInProgressMutex); 303 if (!m_isTransactionQueueEnabled) 304 return nullptr; 305 306 RefPtrWillBeRawPtr<SQLTransactionWrapper> wrapper = nullptr; 307 if (data) 308 wrapper = ChangeVersionWrapper::create(data->oldVersion(), data->newVersion()); 309 310 RefPtrWillBeRawPtr<SQLTransactionBackend> transactionBackend = SQLTransactionBackend::create(this, transaction, wrapper.release(), readOnly); 311 m_transactionQueue.append(transactionBackend); 312 if (!m_transactionInProgress) 313 scheduleTransaction(); 314 315 return transactionBackend; 316} 317 318void Database::inProgressTransactionCompleted() 319{ 320 MutexLocker locker(m_transactionInProgressMutex); 321 m_transactionInProgress = false; 322 scheduleTransaction(); 323} 324 325void Database::scheduleTransaction() 326{ 327 ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. 328 RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = nullptr; 329 330 if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) 331 transaction = m_transactionQueue.takeFirst(); 332 333 if (transaction && databaseContext()->databaseThreadAvailable()) { 334 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); 335 WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); 336 m_transactionInProgress = true; 337 databaseContext()->databaseThread()->scheduleTask(task.release()); 338 } else { 339 m_transactionInProgress = false; 340 } 341} 342 343void Database::scheduleTransactionStep(SQLTransactionBackend* transaction) 344{ 345 if (!databaseContext()->databaseThreadAvailable()) 346 return; 347 348 OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); 349 WTF_LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); 350 databaseContext()->databaseThread()->scheduleTask(task.release()); 351} 352 353SQLTransactionClient* Database::transactionClient() const 354{ 355 return databaseContext()->databaseThread()->transactionClient(); 356} 357 358SQLTransactionCoordinator* Database::transactionCoordinator() const 359{ 360 return databaseContext()->databaseThread()->transactionCoordinator(); 361} 362 363// static 364const char* Database::databaseInfoTableName() 365{ 366 return infoTableName; 367} 368 369void Database::closeDatabase() 370{ 371 if (!m_opened) 372 return; 373 374 m_sqliteDatabase.close(); 375 m_opened = false; 376 // See comment at the top this file regarding calling removeOpenDatabase(). 377 DatabaseTracker::tracker().removeOpenDatabase(this); 378 { 379 SafePointAwareMutexLocker locker(guidMutex()); 380 381 HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); 382 ASSERT(hashSet); 383 ASSERT(hashSet->contains(this)); 384 hashSet->remove(this); 385 if (hashSet->isEmpty()) { 386 guidToDatabaseMap().remove(m_guid); 387 delete hashSet; 388 guidToVersionMap().remove(m_guid); 389 } 390 } 391} 392 393String Database::version() const 394{ 395 // Note: In multi-process browsers the cached value may be accurate, but we 396 // cannot read the actual version from the database without potentially 397 // inducing a deadlock. 398 // FIXME: Add an async version getter to the DatabaseAPI. 399 return getCachedVersion(); 400} 401 402class DoneCreatingDatabaseOnExitCaller { 403public: 404 DoneCreatingDatabaseOnExitCaller(Database* database) 405 : m_database(database) 406 , m_openSucceeded(false) 407 { 408 } 409 ~DoneCreatingDatabaseOnExitCaller() 410 { 411 if (!m_openSucceeded) 412 DatabaseTracker::tracker().failedToOpenDatabase(m_database); 413 } 414 415 void setOpenSucceeded() { m_openSucceeded = true; } 416 417private: 418 Database* m_database; 419 bool m_openSucceeded; 420}; 421 422bool Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) 423{ 424 DoneCreatingDatabaseOnExitCaller onExitCaller(this); 425 ASSERT(errorMessage.isEmpty()); 426 ASSERT(error == DatabaseError::None); // Better not have any errors already. 427 // Presumed failure. We'll clear it if we succeed below. 428 error = DatabaseError::InvalidDatabaseState; 429 430 const int maxSqliteBusyWaitTime = 30000; 431 432 if (!m_sqliteDatabase.open(m_filename, true)) { 433 reportOpenDatabaseResult(1, InvalidStateError, m_sqliteDatabase.lastError()); 434 errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 435 return false; 436 } 437 if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) 438 WTF_LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 439 440 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); 441 442 String currentVersion; 443 { 444 SafePointAwareMutexLocker locker(guidMutex()); 445 446 GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); 447 if (entry != guidToVersionMap().end()) { 448 // Map null string to empty string (see updateGuidVersionMap()). 449 currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); 450 WTF_LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); 451 452 // Note: In multi-process browsers the cached value may be 453 // inaccurate, but we cannot read the actual version from the 454 // database without potentially inducing a form of deadlock, a 455 // busytimeout error when trying to access the database. So we'll 456 // use the cached value if we're unable to read the value from the 457 // database file without waiting. 458 // FIXME: Add an async openDatabase method to the DatabaseAPI. 459 const int noSqliteBusyWaitTime = 0; 460 m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime); 461 String versionFromDatabase; 462 if (getVersionFromDatabase(versionFromDatabase, false)) { 463 currentVersion = versionFromDatabase; 464 updateGuidVersionMap(m_guid, currentVersion); 465 } 466 m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); 467 } else { 468 WTF_LOG(StorageAPI, "No cached version for guid %i", m_guid); 469 470 SQLiteTransaction transaction(m_sqliteDatabase); 471 transaction.begin(); 472 if (!transaction.inProgress()) { 473 reportOpenDatabaseResult(2, InvalidStateError, m_sqliteDatabase.lastError()); 474 errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 475 m_sqliteDatabase.close(); 476 return false; 477 } 478 479 String tableName(infoTableName); 480 if (!m_sqliteDatabase.tableExists(tableName)) { 481 m_new = true; 482 483 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);")) { 484 reportOpenDatabaseResult(3, InvalidStateError, m_sqliteDatabase.lastError()); 485 errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 486 transaction.rollback(); 487 m_sqliteDatabase.close(); 488 return false; 489 } 490 } else if (!getVersionFromDatabase(currentVersion, false)) { 491 reportOpenDatabaseResult(4, InvalidStateError, m_sqliteDatabase.lastError()); 492 errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 493 transaction.rollback(); 494 m_sqliteDatabase.close(); 495 return false; 496 } 497 498 if (currentVersion.length()) { 499 WTF_LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); 500 } else if (!m_new || shouldSetVersionInNewDatabase) { 501 WTF_LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); 502 if (!setVersionInDatabase(m_expectedVersion, false)) { 503 reportOpenDatabaseResult(5, InvalidStateError, m_sqliteDatabase.lastError()); 504 errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); 505 transaction.rollback(); 506 m_sqliteDatabase.close(); 507 return false; 508 } 509 currentVersion = m_expectedVersion; 510 } 511 updateGuidVersionMap(m_guid, currentVersion); 512 transaction.commit(); 513 } 514 } 515 516 if (currentVersion.isNull()) { 517 WTF_LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); 518 currentVersion = ""; 519 } 520 521 // If the expected version isn't the empty string, ensure that the current 522 // database version we have matches that version. Otherwise, set an 523 // exception. 524 // If the expected version is the empty string, then we always return with 525 // whatever version of the database we have. 526 if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { 527 reportOpenDatabaseResult(6, InvalidStateError, 0); 528 errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; 529 m_sqliteDatabase.close(); 530 return false; 531 } 532 533 ASSERT(m_databaseAuthorizer); 534 m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get()); 535 536 // See comment at the top this file regarding calling addOpenDatabase(). 537 DatabaseTracker::tracker().addOpenDatabase(this); 538 m_opened = true; 539 540 // Declare success: 541 error = DatabaseError::None; // Clear the presumed error from above. 542 onExitCaller.setOpenSucceeded(); 543 544 if (m_new && !shouldSetVersionInNewDatabase) { 545 // The caller provided a creationCallback which will set the expected 546 // version. 547 m_expectedVersion = ""; 548 } 549 550 reportOpenDatabaseResult(0, -1, 0); // OK 551 552 if (databaseContext()->databaseThread()) 553 databaseContext()->databaseThread()->recordDatabaseOpen(this); 554 return true; 555} 556 557String Database::stringIdentifier() const 558{ 559 // Return a deep copy for ref counting thread safety 560 return m_name.isolatedCopy(); 561} 562 563String Database::displayName() const 564{ 565 // Return a deep copy for ref counting thread safety 566 return m_displayName.isolatedCopy(); 567} 568 569unsigned long Database::estimatedSize() const 570{ 571 return m_estimatedSize; 572} 573 574String Database::fileName() const 575{ 576 // Return a deep copy for ref counting thread safety 577 return m_filename.isolatedCopy(); 578} 579 580bool Database::getVersionFromDatabase(String& version, bool shouldCacheVersion) 581{ 582 String query(String("SELECT value FROM ") + infoTableName + " WHERE key = '" + versionKey + "';"); 583 584 m_databaseAuthorizer->disable(); 585 586 bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version); 587 if (result) { 588 if (shouldCacheVersion) 589 setCachedVersion(version); 590 } else { 591 WTF_LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); 592 } 593 594 m_databaseAuthorizer->enable(); 595 596 return result; 597} 598 599bool Database::setVersionInDatabase(const String& version, bool shouldCacheVersion) 600{ 601 // The INSERT will replace an existing entry for the database with the new 602 // version number, due to the UNIQUE ON CONFLICT REPLACE clause in the 603 // CREATE statement (see Database::performOpenAndVerify()). 604 String query(String("INSERT INTO ") + infoTableName + " (key, value) VALUES ('" + versionKey + "', ?);"); 605 606 m_databaseAuthorizer->disable(); 607 608 bool result = setTextValueInDatabase(m_sqliteDatabase, query, version); 609 if (result) { 610 if (shouldCacheVersion) 611 setCachedVersion(version); 612 } else { 613 WTF_LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data()); 614 } 615 616 m_databaseAuthorizer->enable(); 617 618 return result; 619} 620 621void Database::setExpectedVersion(const String& version) 622{ 623 m_expectedVersion = version.isolatedCopy(); 624} 625 626String Database::getCachedVersion() const 627{ 628 SafePointAwareMutexLocker locker(guidMutex()); 629 return guidToVersionMap().get(m_guid).isolatedCopy(); 630} 631 632void Database::setCachedVersion(const String& actualVersion) 633{ 634 // Update the in memory database version map. 635 SafePointAwareMutexLocker locker(guidMutex()); 636 updateGuidVersionMap(m_guid, actualVersion); 637} 638 639bool Database::getActualVersionForTransaction(String& actualVersion) 640{ 641 ASSERT(m_sqliteDatabase.transactionInProgress()); 642 // Note: In multi-process browsers the cached value may be inaccurate. So we 643 // retrieve the value from the database and update the cached value here. 644 return getVersionFromDatabase(actualVersion, true); 645} 646 647void Database::disableAuthorizer() 648{ 649 ASSERT(m_databaseAuthorizer); 650 m_databaseAuthorizer->disable(); 651} 652 653void Database::enableAuthorizer() 654{ 655 ASSERT(m_databaseAuthorizer); 656 m_databaseAuthorizer->enable(); 657} 658 659void Database::setAuthorizerPermissions(int permissions) 660{ 661 ASSERT(m_databaseAuthorizer); 662 m_databaseAuthorizer->setPermissions(permissions); 663} 664 665bool Database::lastActionChangedDatabase() 666{ 667 ASSERT(m_databaseAuthorizer); 668 return m_databaseAuthorizer->lastActionChangedDatabase(); 669} 670 671bool Database::lastActionWasInsert() 672{ 673 ASSERT(m_databaseAuthorizer); 674 return m_databaseAuthorizer->lastActionWasInsert(); 675} 676 677void Database::resetDeletes() 678{ 679 ASSERT(m_databaseAuthorizer); 680 m_databaseAuthorizer->resetDeletes(); 681} 682 683bool Database::hadDeletes() 684{ 685 ASSERT(m_databaseAuthorizer); 686 return m_databaseAuthorizer->hadDeletes(); 687} 688 689void Database::resetAuthorizer() 690{ 691 if (m_databaseAuthorizer) 692 m_databaseAuthorizer->reset(); 693} 694 695unsigned long long Database::maximumSize() const 696{ 697 return DatabaseTracker::tracker().getMaxSizeForDatabase(this); 698} 699 700void Database::incrementalVacuumIfNeeded() 701{ 702 int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); 703 int64_t totalSize = m_sqliteDatabase.totalSize(); 704 if (totalSize <= 10 * freeSpaceSize) { 705 int result = m_sqliteDatabase.runIncrementalVacuumCommand(); 706 reportVacuumDatabaseResult(result); 707 if (result != SQLResultOk) 708 logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg())); 709 } 710} 711 712// These are used to generate histograms of errors seen with websql. 713// See about:histograms in chromium. 714void Database::reportOpenDatabaseResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) 715{ 716 if (Platform::current()->databaseObserver()) { 717 Platform::current()->databaseObserver()->reportOpenDatabaseResult( 718 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 719 stringIdentifier(), false, 720 errorSite, webSqlErrorCode, sqliteErrorCode); 721 } 722} 723 724void Database::reportChangeVersionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) 725{ 726 if (Platform::current()->databaseObserver()) { 727 Platform::current()->databaseObserver()->reportChangeVersionResult( 728 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 729 stringIdentifier(), false, 730 errorSite, webSqlErrorCode, sqliteErrorCode); 731 } 732} 733 734void Database::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) 735{ 736 if (Platform::current()->databaseObserver()) { 737 Platform::current()->databaseObserver()->reportStartTransactionResult( 738 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 739 stringIdentifier(), false, 740 errorSite, webSqlErrorCode, sqliteErrorCode); 741 } 742} 743 744void Database::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) 745{ 746 if (Platform::current()->databaseObserver()) { 747 Platform::current()->databaseObserver()->reportCommitTransactionResult( 748 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 749 stringIdentifier(), false, 750 errorSite, webSqlErrorCode, sqliteErrorCode); 751 } 752} 753 754void Database::reportExecuteStatementResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) 755{ 756 if (Platform::current()->databaseObserver()) { 757 Platform::current()->databaseObserver()->reportExecuteStatementResult( 758 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 759 stringIdentifier(), false, 760 errorSite, webSqlErrorCode, sqliteErrorCode); 761 } 762} 763 764void Database::reportVacuumDatabaseResult(int sqliteErrorCode) 765{ 766 if (Platform::current()->databaseObserver()) { 767 Platform::current()->databaseObserver()->reportVacuumDatabaseResult( 768 createDatabaseIdentifierFromSecurityOrigin(securityOrigin()), 769 stringIdentifier(), false, sqliteErrorCode); 770 } 771} 772 773void Database::logErrorMessage(const String& message) 774{ 775 executionContext()->addConsoleMessage(ConsoleMessage::create(StorageMessageSource, ErrorMessageLevel, message)); 776} 777 778ExecutionContext* Database::executionContext() const 779{ 780 return databaseContext()->executionContext(); 781} 782 783void Database::closeImmediately() 784{ 785 ASSERT(executionContext()->isContextThread()); 786 if (databaseContext()->databaseThreadAvailable() && opened()) { 787 logErrorMessage("forcibly closing database"); 788 databaseContext()->databaseThread()->scheduleTask(DatabaseCloseTask::create(this, 0)); 789 } 790} 791 792void Database::changeVersion( 793 const String& oldVersion, 794 const String& newVersion, 795 SQLTransactionCallback* callback, 796 SQLTransactionErrorCallback* errorCallback, 797 VoidCallback* successCallback) 798{ 799 ChangeVersionData data(oldVersion, newVersion); 800 runTransaction(callback, errorCallback, successCallback, false, &data); 801} 802 803void Database::transaction( 804 SQLTransactionCallback* callback, 805 SQLTransactionErrorCallback* errorCallback, 806 VoidCallback* successCallback) 807{ 808 runTransaction(callback, errorCallback, successCallback, false); 809} 810 811void Database::readTransaction( 812 SQLTransactionCallback* callback, 813 SQLTransactionErrorCallback* errorCallback, 814 VoidCallback* successCallback) 815{ 816 runTransaction(callback, errorCallback, successCallback, true); 817} 818 819static void callTransactionErrorCallback(ExecutionContext*, SQLTransactionErrorCallback* callback, PassOwnPtr<SQLErrorData> errorData) 820{ 821 RefPtrWillBeRawPtr<SQLError> error = SQLError::create(*errorData); 822 callback->handleEvent(error.get()); 823} 824 825void Database::runTransaction( 826 SQLTransactionCallback* callback, 827 SQLTransactionErrorCallback* errorCallback, 828 VoidCallback* successCallback, 829 bool readOnly, 830 const ChangeVersionData* changeVersionData) 831{ 832 // FIXME: Rather than passing errorCallback to SQLTransaction and then 833 // sometimes firing it ourselves, this code should probably be pushed down 834 // into Database so that we only create the SQLTransaction if we're 835 // actually going to run it. 836#if ENABLE(ASSERT) 837 SQLTransactionErrorCallback* originalErrorCallback = errorCallback; 838#endif 839 RefPtrWillBeRawPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallback, readOnly); 840 RefPtrWillBeRawPtr<SQLTransactionBackend> transactionBackend = runTransaction(transaction, readOnly, changeVersionData); 841 if (!transactionBackend) { 842 SQLTransactionErrorCallback* callback = transaction->releaseErrorCallback(); 843 ASSERT(callback == originalErrorCallback); 844 if (callback) { 845 OwnPtr<SQLErrorData> error = SQLErrorData::create(SQLError::UNKNOWN_ERR, "database has been closed"); 846 executionContext()->postTask(createCrossThreadTask(&callTransactionErrorCallback, callback, error.release())); 847 } 848 } 849} 850 851// This object is constructed in a database thread, and destructed in the 852// context thread. 853class DeliverPendingCallbackTask FINAL : public ExecutionContextTask { 854public: 855 static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtrWillBeRawPtr<SQLTransaction> transaction) 856 { 857 return adoptPtr(new DeliverPendingCallbackTask(transaction)); 858 } 859 860 virtual void performTask(ExecutionContext*) OVERRIDE 861 { 862 m_transaction->performPendingCallback(); 863 } 864 865private: 866 DeliverPendingCallbackTask(PassRefPtrWillBeRawPtr<SQLTransaction> transaction) 867 : m_transaction(transaction) 868 { 869 } 870 871 RefPtrWillBeCrossThreadPersistent<SQLTransaction> m_transaction; 872}; 873 874void Database::scheduleTransactionCallback(SQLTransaction* transaction) 875{ 876 executionContext()->postTask(DeliverPendingCallbackTask::create(transaction)); 877} 878 879Vector<String> Database::performGetTableNames() 880{ 881 disableAuthorizer(); 882 883 SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); 884 if (statement.prepare() != SQLResultOk) { 885 WTF_LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); 886 enableAuthorizer(); 887 return Vector<String>(); 888 } 889 890 Vector<String> tableNames; 891 int result; 892 while ((result = statement.step()) == SQLResultRow) { 893 String name = statement.getColumnText(0); 894 if (name != databaseInfoTableName()) 895 tableNames.append(name); 896 } 897 898 enableAuthorizer(); 899 900 if (result != SQLResultDone) { 901 WTF_LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); 902 return Vector<String>(); 903 } 904 905 return tableNames; 906} 907 908Vector<String> Database::tableNames() 909{ 910 // FIXME: Not using isolatedCopy on these strings looks ok since threads 911 // take strict turns in dealing with them. However, if the code changes, 912 // this may not be true anymore. 913 Vector<String> result; 914 TaskSynchronizer synchronizer; 915 if (!databaseContext()->databaseThreadAvailable()) 916 return result; 917 918 OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); 919 databaseContext()->databaseThread()->scheduleTask(task.release()); 920 synchronizer.waitForTaskCompletion(); 921 922 return result; 923} 924 925SecurityOrigin* Database::securityOrigin() const 926{ 927 if (executionContext()->isContextThread()) 928 return m_contextThreadSecurityOrigin.get(); 929 if (databaseContext()->databaseThread()->isDatabaseThread()) 930 return m_databaseThreadSecurityOrigin.get(); 931 return 0; 932} 933 934} // namespace blink 935