void Database::markAsDeletedAndClose() { if (m_deleted || !databaseContext()->databaseThread()) return; LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); m_deleted = true; DatabaseTaskSynchronizer synchronizer; if (databaseContext()->databaseThread()->terminationRequested(&synchronizer)) { LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); return; } OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer); databaseContext()->databaseThread()->scheduleImmediateTask(task.release()); synchronizer.waitForTaskCompletion(); }
void Database::closeImmediately() { ASSERT(m_scriptExecutionContext->isContextThread()); DatabaseThread* databaseThread = databaseContext()->databaseThread(); if (databaseThread && !databaseThread->terminationRequested() && opened()) { logErrorMessage("forcibly closing database"); databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); } }
Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize, AsyncDatabase) , m_transactionInProgress(false) , m_isTransactionQueueEnabled(true) , m_deleted(false) { m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy(); ScriptController::initializeThreading(); ASSERT(databaseContext()->databaseThread()); }
void Database::close() { ASSERT(databaseContext()->databaseThread()); ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID()); { MutexLocker locker(m_transactionInProgressMutex); m_isTransactionQueueEnabled = false; m_transactionInProgress = false; m_transactionQueue.clear(); } closeDatabase(); // Must ref() before calling databaseThread()->recordDatabaseClosed(). RefPtr<Database> protect = this; databaseContext()->databaseThread()->recordDatabaseClosed(this); databaseContext()->databaseThread()->unscheduleDatabaseTasks(this); DatabaseManager::manager().removeOpenDatabase(this); }
void DatabaseBackend::close() { ASSERT(databaseContext()->databaseThread()); ASSERT(databaseContext()->databaseThread()->isDatabaseThread()); { MutexLocker locker(m_transactionInProgressMutex); // Clean up transactions that have not been scheduled yet: // Transaction phase 1 cleanup. See comment on "What happens if a // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. RefPtrWillBeRawPtr<SQLTransactionBackend> transaction = nullptr; while (!m_transactionQueue.isEmpty()) { transaction = m_transactionQueue.takeFirst(); transaction->notifyDatabaseThreadIsShuttingDown(); } m_isTransactionQueueEnabled = false; m_transactionInProgress = false; } closeDatabase(); databaseContext()->databaseThread()->recordDatabaseClosed(this); }
SQLTransactionCoordinator* DatabaseBackend::transactionCoordinator() const { return databaseContext()->databaseThread()->transactionCoordinator(); }
SQLTransactionClient* DatabaseBackend::transactionClient() const { return databaseContext()->databaseThread()->transactionClient(); }
ExecutionContext* Database::executionContext() const { return databaseContext()->executionContext(); }
bool Database::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) { double callStartTime = WTF::monotonicallyIncreasingTime(); DoneCreatingDatabaseOnExitCaller onExitCaller(this); ASSERT(errorMessage.isEmpty()); ASSERT(error == DatabaseError::None); // Better not have any errors already. // Presumed failure. We'll clear it if we succeed below. error = DatabaseError::InvalidDatabaseState; const int maxSqliteBusyWaitTime = 30000; if (!m_sqliteDatabase.open(m_filename)) { reportOpenDatabaseResult(1, InvalidStateError, m_sqliteDatabase.lastError(), WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) WTF_LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { SafePointAwareMutexLocker locker(guidMutex()); GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); WTF_LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); // Note: In multi-process browsers the cached value may be // inaccurate, but we cannot read the actual version from the // database without potentially inducing a form of deadlock, a // busytimeout error when trying to access the database. So we'll // use the cached value if we're unable to read the value from the // database file without waiting. // FIXME: Add an async openDatabase method to the DatabaseAPI. const int noSqliteBusyWaitTime = 0; m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime); String versionFromDatabase; if (getVersionFromDatabase(versionFromDatabase, false)) { currentVersion = versionFromDatabase; updateGuidVersionMap(m_guid, currentVersion); } m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); } else { WTF_LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { reportOpenDatabaseResult(2, InvalidStateError, m_sqliteDatabase.lastError(), WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.close(); return false; } String tableName(infoTableName); if (!m_sqliteDatabase.tableExists(tableName)) { m_new = true; 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);")) { reportOpenDatabaseResult(3, InvalidStateError, m_sqliteDatabase.lastError(), WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } } else if (!getVersionFromDatabase(currentVersion, false)) { reportOpenDatabaseResult(4, InvalidStateError, m_sqliteDatabase.lastError(), WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { WTF_LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { WTF_LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { reportOpenDatabaseResult(5, InvalidStateError, m_sqliteDatabase.lastError(), WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } updateGuidVersionMap(m_guid, currentVersion); transaction.commit(); } } if (currentVersion.isNull()) { WTF_LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); currentVersion = ""; } // If the expected version isn't the empty string, ensure that the current // database version we have matches that version. Otherwise, set an // exception. // If the expected version is the empty string, then we always return with // whatever version of the database we have. if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { reportOpenDatabaseResult(6, InvalidStateError, 0, WTF::monotonicallyIncreasingTime() - callStartTime); errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; m_sqliteDatabase.close(); return false; } ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer.get()); // See comment at the top this file regarding calling addOpenDatabase(). DatabaseTracker::tracker().addOpenDatabase(this); m_opened = true; // Declare success: error = DatabaseError::None; // Clear the presumed error from above. onExitCaller.setOpenSucceeded(); if (m_new && !shouldSetVersionInNewDatabase) { // The caller provided a creationCallback which will set the expected // version. m_expectedVersion = ""; } reportOpenDatabaseResult(0, -1, 0, WTF::monotonicallyIncreasingTime() - callStartTime); // OK if (databaseContext()->databaseThread()) databaseContext()->databaseThread()->recordDatabaseOpen(this); return true; }