bool DatabaseBackendBase::getActualVersionForTransaction(String &actualVersion) { ASSERT(m_sqliteDatabase.transactionInProgress()); // Note: In multi-process browsers the cached value may be inaccurate. // So we retrieve the value from the database and update the cached value here. return getVersionFromDatabase(actualVersion, true); }
void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassOwnPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionState& exceptionState) { ASSERT(m_executionContext->isContextThread()); if (sqliteDatabase().transactionInProgress()) { reportChangeVersionResult(1, SQLError::DATABASE_ERR, 0); setLastErrorMessage("unable to changeVersion from within a transaction"); exceptionState.throwUninformativeAndGenericDOMException(SQLDatabaseError); return; } RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false); transaction->begin(exceptionState); if (exceptionState.hadException()) { ASSERT(!lastErrorMessage().isEmpty()); return; } String actualVersion; if (!getVersionFromDatabase(actualVersion)) { reportChangeVersionResult(2, SQLError::UNKNOWN_ERR, sqliteDatabase().lastError()); setLastErrorMessage("unable to read the current version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); return; } if (actualVersion != oldVersion) { reportChangeVersionResult(3, SQLError::VERSION_ERR, 0); setLastErrorMessage("current version of the database and `oldVersion` argument do not match"); exceptionState.throwDOMException(VersionError, SQLError::versionErrorMessage); return; } transaction->execute(exceptionState); if (exceptionState.hadException()) { ASSERT(!lastErrorMessage().isEmpty()); return; } if (!setVersionInDatabase(newVersion)) { reportChangeVersionResult(4, SQLError::UNKNOWN_ERR, sqliteDatabase().lastError()); setLastErrorMessage("unable to set the new version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); exceptionState.throwDOMException(UnknownError, SQLError::unknownErrorMessage); return; } transaction->commit(exceptionState); if (exceptionState.hadException()) { ASSERT(!lastErrorMessage().isEmpty()); setCachedVersion(oldVersion); return; } reportChangeVersionResult(0, -1, 0); // OK setExpectedVersion(newVersion); setLastErrorMessage(""); }
void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionCode& ec) { ASSERT(m_scriptExecutionContext->isContextThread()); if (sqliteDatabase().transactionInProgress()) { reportChangeVersionResult(1, SQLException::DATABASE_ERR, 0); setLastErrorMessage("unable to changeVersion from within a transaction"); ec = SQLException::DATABASE_ERR; return; } RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false); if ((ec = transaction->begin())) { ASSERT(!lastErrorMessage().isEmpty()); return; } String actualVersion; if (!getVersionFromDatabase(actualVersion)) { reportChangeVersionResult(2, SQLException::UNKNOWN_ERR, sqliteDatabase().lastError()); setLastErrorMessage("unable to read the current version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); ec = SQLException::UNKNOWN_ERR; return; } if (actualVersion != oldVersion) { reportChangeVersionResult(3, SQLException::VERSION_ERR, 0); setLastErrorMessage("current version of the database and `oldVersion` argument do not match"); ec = SQLException::VERSION_ERR; return; } if ((ec = transaction->execute())) { ASSERT(!lastErrorMessage().isEmpty()); return; } if (!setVersionInDatabase(newVersion)) { reportChangeVersionResult(4, SQLException::UNKNOWN_ERR, sqliteDatabase().lastError()); setLastErrorMessage("unable to set the new version", sqliteDatabase().lastError(), sqliteDatabase().lastErrorMsg()); ec = SQLException::UNKNOWN_ERR; return; } if ((ec = transaction->commit())) { ASSERT(!lastErrorMessage().isEmpty()); setCachedVersion(oldVersion); return; } reportChangeVersionResult(0, -1, 0); // OK setExpectedVersion(newVersion); setLastErrorMessage(""); }
bool AbstractDatabase::getActualVersionForTransaction(String &actualVersion) { ASSERT(m_sqliteDatabase.transactionInProgress()); #if PLATFORM(CHROMIUM) // Note: In multi-process browsers the cached value may be inaccurate. // So we retrieve the value from the database and update the cached value here. return getVersionFromDatabase(actualVersion, true); #else actualVersion = getCachedVersion(); return true; #endif }
void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionCode& ec) { ASSERT(m_scriptExecutionContext->isContextThread()); if (sqliteDatabase().transactionInProgress()) { ec = SQLException::DATABASE_ERR; return; } RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false); if ((ec = transaction->begin())) return; String actualVersion; if (!getVersionFromDatabase(actualVersion)) { ec = SQLException::UNKNOWN_ERR; return; } if (actualVersion != oldVersion) { ec = SQLException::VERSION_ERR; return; } if ((ec = transaction->execute())) return; if (!setVersionInDatabase(newVersion)) { ec = SQLException::UNKNOWN_ERR; return; } if ((ec = transaction->commit())) return; setExpectedVersion(newVersion); }
bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec) { const int maxSqliteBusyWaitTime = 30000; if (!m_sqliteDatabase.open(m_filename, true)) { LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data()); ec = INVALID_STATE_ERR; return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { MutexLocker locker(guidMutex()); GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->second.isNull() ? String("") : entry->second.threadsafeCopy(); LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); #if PLATFORM(CHROMIUM) // 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); #endif } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { LOG_ERROR("Unable to begin transaction while opening %s", databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; 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);")) { LOG_ERROR("Unable to create table %s in database %s", infoTableName, databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } } else if (!getVersionFromDatabase(currentVersion, false)) { LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } updateGuidVersionMap(m_guid, currentVersion); transaction.commit(); } } if (currentVersion.isNull()) { 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) { LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data(), currentVersion.ascii().data()); ec = INVALID_STATE_ERR; m_sqliteDatabase.close(); return false; } ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); m_opened = true; if (m_new && !shouldSetVersionInNewDatabase) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. return true; }
bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DoneCreatingDatabaseOnExitCaller onExitCaller(this); ASSERT(errorMessage.isEmpty()); ASSERT(error == DatabaseError::None); // Better not have any errors already. error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. const int maxSqliteBusyWaitTime = 30000; #if PLATFORM(IOS) { // Make sure we wait till the background removal of the empty database files finished before trying to open any database. MutexLocker locker(DatabaseTracker::openDatabaseMutex()); } #endif SQLiteTransactionInProgressAutoCounter transactionCounter; if (!m_sqliteDatabase.open(m_filename, true)) { errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { std::lock_guard<std::mutex> locker(guidMutex()); auto 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(); LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { 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);")) { 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)) { 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()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { 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()) { 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) { 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); // 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) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. return true; }
bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DoneCreatingDatabaseOnExitCaller onExitCaller(this); ASSERT(errorMessage.isEmpty()); ASSERT(error == DatabaseError::None); // Better not have any errors already. error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. const int maxSqliteBusyWaitTime = 30000; if (!m_sqliteDatabase.open(m_filename, true)) { reportOpenDatabaseResult(1, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { MutexLocker 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(); 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 { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { reportOpenDatabaseResult(2, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); 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, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); 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, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); 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()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { 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, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); 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()) { 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, INVALID_STATE_ERR, 0); 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); // 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) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. reportOpenDatabaseResult(0, -1, 0); // OK return true; }