Esempio n. 1
0
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("");
}
Esempio n. 2
0
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("");
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
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;
}
Esempio n. 6
0
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;
}