| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * Copyright (C) 2013 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "DatabaseBackend.h" |
| |
| #if ENABLE(SQL_DATABASE) |
| |
| #include "DatabaseAuthorizer.h" |
| #include "DatabaseBackendContext.h" |
| #include "DatabaseBase.h" |
| #include "DatabaseContext.h" |
| #include "DatabaseManager.h" |
| #include "DatabaseTracker.h" |
| #include "ExceptionCode.h" |
| #include "Logging.h" |
| #include "SQLiteStatement.h" |
| #include "SQLiteTransaction.h" |
| #include "SecurityOrigin.h" |
| #include <wtf/HashMap.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/PassRefPtr.h> |
| #include <wtf/RefPtr.h> |
| #include <wtf/StdLibExtras.h> |
| #include <wtf/text/CString.h> |
| #include <wtf/text/StringHash.h> |
| |
| #if PLATFORM(CHROMIUM) |
| #include "DatabaseObserver.h" // For error reporting. |
| #endif |
| |
| // Registering "opened" databases with the DatabaseTracker |
| // ======================================================= |
| // The DatabaseTracker maintains a list of databases that have been |
| // "opened" so that the client can call interrupt or delete on every database |
| // associated with a DatabaseBackendContext. |
| // |
| // We will only call DatabaseTracker::addOpenDatabase() to add the database |
| // to the tracker as opened when we've succeeded in opening the database, |
| // and will set m_opened to true. Similarly, we only call |
| // DatabaseTracker::removeOpenDatabase() to remove the database from the |
| // tracker when we set m_opened to false in closeDatabase(). This sets up |
| // a simple symmetry between open and close operations, and a direct |
| // correlation to adding and removing databases from the tracker's list, |
| // thus ensuring that we have a correct list for the interrupt and |
| // delete operations to work on. |
| // |
| // The only databases instances not tracked by the tracker's open database |
| // list are the ones that have not been added yet, or the ones that we |
| // attempted an open on but failed to. Such instances only exist in the |
| // DatabaseServer's factory methods for creating DatabaseBackends. |
| // |
| // The factory methods will either call openAndVerifyVersion() or |
| // performOpenAndVerify(). These methods will add the newly instantiated |
| // DatabaseBackend if they succeed in opening the requested database. |
| // In the case of failure to open the database, the factory methods will |
| // simply discard the newly instantiated DatabaseBackend when they return. |
| // The ref counting mechanims will automatically destruct the un-added |
| // (and un-returned) databases instances. |
| |
| namespace WebCore { |
| |
| static const char versionKey[] = "WebKitDatabaseVersionKey"; |
| static const char infoTableName[] = "__WebKitDatabaseInfoTable__"; |
| |
| static String formatErrorMessage(const char* message, int sqliteErrorCode, const char* sqliteErrorMessage) |
| { |
| return String::format("%s (%d %s)", message, sqliteErrorCode, sqliteErrorMessage); |
| } |
| |
| static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) |
| { |
| SQLiteStatement statement(db, query); |
| int result = statement.prepare(); |
| |
| if (result != SQLResultOk) { |
| LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); |
| return false; |
| } |
| |
| result = statement.step(); |
| if (result == SQLResultRow) { |
| resultString = statement.getColumnText(0); |
| return true; |
| } |
| if (result == SQLResultDone) { |
| resultString = String(); |
| return true; |
| } |
| |
| LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); |
| return false; |
| } |
| |
| static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) |
| { |
| SQLiteStatement statement(db, query); |
| int result = statement.prepare(); |
| |
| if (result != SQLResultOk) { |
| LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); |
| return false; |
| } |
| |
| statement.bindText(1, value); |
| |
| result = statement.step(); |
| if (result != SQLResultDone) { |
| LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // FIXME: move all guid-related functions to a DatabaseVersionTracker class. |
| static Mutex& guidMutex() |
| { |
| AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); |
| return mutex; |
| } |
| |
| typedef HashMap<DatabaseGuid, String> GuidVersionMap; |
| static GuidVersionMap& guidToVersionMap() |
| { |
| // Ensure the the mutex is locked. |
| ASSERT(!guidMutex().tryLock()); |
| DEFINE_STATIC_LOCAL(GuidVersionMap, map, ()); |
| return map; |
| } |
| |
| // NOTE: Caller must lock guidMutex(). |
| static inline void updateGuidVersionMap(DatabaseGuid guid, String newVersion) |
| { |
| // Ensure the the mutex is locked. |
| ASSERT(!guidMutex().tryLock()); |
| |
| // Note: It is not safe to put an empty string into the guidToVersionMap() map. |
| // That's because the map is cross-thread, but empty strings are per-thread. |
| // The copy() function makes a version of the string you can use on the current |
| // thread, but we need a string we can keep in a cross-thread data structure. |
| // FIXME: This is a quite-awkward restriction to have to program with. |
| |
| // Map null string to empty string (see comment above). |
| guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.isolatedCopy()); |
| } |
| |
| typedef HashMap<DatabaseGuid, HashSet<DatabaseBackend*>*> GuidDatabaseMap; |
| static GuidDatabaseMap& guidToDatabaseMap() |
| { |
| // Ensure the the mutex is locked. |
| ASSERT(!guidMutex().tryLock()); |
| DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); |
| return map; |
| } |
| |
| static DatabaseGuid guidForOriginAndName(const String& origin, const String& name) |
| { |
| // Ensure the the mutex is locked. |
| ASSERT(!guidMutex().tryLock()); |
| |
| String stringID = origin + "/" + name; |
| |
| typedef HashMap<String, int> IDGuidMap; |
| DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); |
| DatabaseGuid guid = stringIdentifierToGUIDMap.get(stringID); |
| if (!guid) { |
| static int currentNewGUID = 1; |
| guid = currentNewGUID++; |
| stringIdentifierToGUIDMap.set(stringID, guid); |
| } |
| |
| return guid; |
| } |
| |
| // static |
| const char* DatabaseBackend::databaseInfoTableName() |
| { |
| return infoTableName; |
| } |
| |
| DatabaseBackend::DatabaseBackend(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, |
| const String& displayName, unsigned long estimatedSize, DatabaseType databaseType) |
| : m_databaseContext(databaseContext) |
| , m_name(name.isolatedCopy()) |
| , m_expectedVersion(expectedVersion.isolatedCopy()) |
| , m_displayName(displayName.isolatedCopy()) |
| , m_estimatedSize(estimatedSize) |
| , m_guid(0) |
| , m_opened(false) |
| , m_new(false) |
| , m_isSyncDatabase(databaseType == DatabaseType::Sync) |
| { |
| m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); |
| |
| m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); |
| |
| if (m_name.isNull()) |
| m_name = ""; |
| |
| { |
| MutexLocker locker(guidMutex()); |
| m_guid = guidForOriginAndName(securityOrigin()->toString(), name); |
| HashSet<DatabaseBackend*>* hashSet = guidToDatabaseMap().get(m_guid); |
| if (!hashSet) { |
| hashSet = new HashSet<DatabaseBackend*>; |
| guidToDatabaseMap().set(m_guid, hashSet); |
| } |
| |
| hashSet->add(this); |
| } |
| |
| m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); |
| } |
| |
| DatabaseBackend::~DatabaseBackend() |
| { |
| // SQLite is "multi-thread safe", but each database handle can only be used |
| // on a single thread at a time. |
| // |
| // For DatabaseBackendAsync, we open the SQLite database on the DatabaseThread, |
| // and hence we should also close it on that same thread. This means that the |
| // SQLite database need to be closed by another mechanism (see |
| // DatabaseContext::stopDatabases()). By the time we get here, the SQLite |
| // database should have already been closed. |
| |
| ASSERT(!m_opened); |
| } |
| |
| void DatabaseBackend::closeDatabase() |
| { |
| if (!m_opened) |
| return; |
| |
| m_sqliteDatabase.close(); |
| m_opened = false; |
| // See comment at the top this file regarding calling removeOpenDatabase(). |
| DatabaseTracker::tracker().removeOpenDatabase(this); |
| { |
| MutexLocker locker(guidMutex()); |
| |
| HashSet<DatabaseBackend*>* hashSet = guidToDatabaseMap().get(m_guid); |
| ASSERT(hashSet); |
| ASSERT(hashSet->contains(this)); |
| hashSet->remove(this); |
| if (hashSet->isEmpty()) { |
| guidToDatabaseMap().remove(m_guid); |
| delete hashSet; |
| guidToVersionMap().remove(m_guid); |
| } |
| } |
| } |
| |
| String DatabaseBackend::version() const |
| { |
| // Note: In multi-process browsers the cached value may be accurate, but we cannot read the |
| // actual version from the database without potentially inducing a deadlock. |
| // FIXME: Add an async version getter to the DatabaseAPI. |
| return getCachedVersion(); |
| } |
| |
| class DoneCreatingDatabaseOnExitCaller { |
| public: |
| DoneCreatingDatabaseOnExitCaller(DatabaseBackend* database) |
| : m_database(database) |
| , m_openSucceeded(false) |
| { |
| } |
| ~DoneCreatingDatabaseOnExitCaller() |
| { |
| #if !PLATFORM(CHROMIUM) |
| DatabaseTracker::tracker().doneCreatingDatabase(m_database); |
| #else |
| if (!m_openSucceeded) |
| DatabaseTracker::tracker().failedToOpenDatabase(m_database); |
| #endif |
| } |
| |
| void setOpenSucceeded() { m_openSucceeded = true; } |
| |
| private: |
| DatabaseBackend* m_database; |
| bool m_openSucceeded; |
| }; |
| |
| bool DatabaseBackend::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()); |
| |
| #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()) { |
| 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; |
| } |
| |
| SecurityOrigin* DatabaseBackend::securityOrigin() const |
| { |
| return m_contextThreadSecurityOrigin.get(); |
| } |
| |
| String DatabaseBackend::stringIdentifier() const |
| { |
| // Return a deep copy for ref counting thread safety |
| return m_name.isolatedCopy(); |
| } |
| |
| String DatabaseBackend::displayName() const |
| { |
| // Return a deep copy for ref counting thread safety |
| return m_displayName.isolatedCopy(); |
| } |
| |
| unsigned long DatabaseBackend::estimatedSize() const |
| { |
| return m_estimatedSize; |
| } |
| |
| String DatabaseBackend::fileName() const |
| { |
| // Return a deep copy for ref counting thread safety |
| return m_filename.isolatedCopy(); |
| } |
| |
| DatabaseDetails DatabaseBackend::details() const |
| { |
| return DatabaseDetails(stringIdentifier(), displayName(), estimatedSize(), 0); |
| } |
| |
| bool DatabaseBackend::getVersionFromDatabase(String& version, bool shouldCacheVersion) |
| { |
| String query(String("SELECT value FROM ") + infoTableName + " WHERE key = '" + versionKey + "';"); |
| |
| m_databaseAuthorizer->disable(); |
| |
| bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, query, version); |
| if (result) { |
| if (shouldCacheVersion) |
| setCachedVersion(version); |
| } else |
| LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); |
| |
| m_databaseAuthorizer->enable(); |
| |
| return result; |
| } |
| |
| bool DatabaseBackend::setVersionInDatabase(const String& version, bool shouldCacheVersion) |
| { |
| // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE |
| // clause in the CREATE statement (see Database::performOpenAndVerify()). |
| String query(String("INSERT INTO ") + infoTableName + " (key, value) VALUES ('" + versionKey + "', ?);"); |
| |
| m_databaseAuthorizer->disable(); |
| |
| bool result = setTextValueInDatabase(m_sqliteDatabase, query, version); |
| if (result) { |
| if (shouldCacheVersion) |
| setCachedVersion(version); |
| } else |
| LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), query.ascii().data()); |
| |
| m_databaseAuthorizer->enable(); |
| |
| return result; |
| } |
| |
| void DatabaseBackend::setExpectedVersion(const String& version) |
| { |
| m_expectedVersion = version.isolatedCopy(); |
| } |
| |
| String DatabaseBackend::getCachedVersion() const |
| { |
| MutexLocker locker(guidMutex()); |
| return guidToVersionMap().get(m_guid).isolatedCopy(); |
| } |
| |
| void DatabaseBackend::setCachedVersion(const String& actualVersion) |
| { |
| // Update the in memory database version map. |
| MutexLocker locker(guidMutex()); |
| updateGuidVersionMap(m_guid, actualVersion); |
| } |
| |
| bool DatabaseBackend::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 DatabaseBackend::disableAuthorizer() |
| { |
| ASSERT(m_databaseAuthorizer); |
| m_databaseAuthorizer->disable(); |
| } |
| |
| void DatabaseBackend::enableAuthorizer() |
| { |
| ASSERT(m_databaseAuthorizer); |
| m_databaseAuthorizer->enable(); |
| } |
| |
| void DatabaseBackend::setAuthorizerReadOnly() |
| { |
| ASSERT(m_databaseAuthorizer); |
| m_databaseAuthorizer->setReadOnly(); |
| } |
| |
| void DatabaseBackend::setAuthorizerPermissions(int permissions) |
| { |
| ASSERT(m_databaseAuthorizer); |
| m_databaseAuthorizer->setPermissions(permissions); |
| } |
| |
| bool DatabaseBackend::lastActionChangedDatabase() |
| { |
| ASSERT(m_databaseAuthorizer); |
| return m_databaseAuthorizer->lastActionChangedDatabase(); |
| } |
| |
| bool DatabaseBackend::lastActionWasInsert() |
| { |
| ASSERT(m_databaseAuthorizer); |
| return m_databaseAuthorizer->lastActionWasInsert(); |
| } |
| |
| void DatabaseBackend::resetDeletes() |
| { |
| ASSERT(m_databaseAuthorizer); |
| m_databaseAuthorizer->resetDeletes(); |
| } |
| |
| bool DatabaseBackend::hadDeletes() |
| { |
| ASSERT(m_databaseAuthorizer); |
| return m_databaseAuthorizer->hadDeletes(); |
| } |
| |
| void DatabaseBackend::resetAuthorizer() |
| { |
| if (m_databaseAuthorizer) |
| m_databaseAuthorizer->reset(); |
| } |
| |
| unsigned long long DatabaseBackend::maximumSize() const |
| { |
| return DatabaseManager::manager().getMaxSizeForDatabase(this); |
| } |
| |
| void DatabaseBackend::incrementalVacuumIfNeeded() |
| { |
| int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); |
| int64_t totalSize = m_sqliteDatabase.totalSize(); |
| if (totalSize <= 10 * freeSpaceSize) { |
| int result = m_sqliteDatabase.runIncrementalVacuumCommand(); |
| reportVacuumDatabaseResult(result); |
| if (result != SQLResultOk) |
| m_frontend->logErrorMessage(formatErrorMessage("error vacuuming database", result, m_sqliteDatabase.lastErrorMsg())); |
| } |
| } |
| |
| void DatabaseBackend::interrupt() |
| { |
| m_sqliteDatabase.interrupt(); |
| } |
| |
| bool DatabaseBackend::isInterrupted() |
| { |
| MutexLocker locker(m_sqliteDatabase.databaseMutex()); |
| return m_sqliteDatabase.isInterrupted(); |
| } |
| |
| #if PLATFORM(CHROMIUM) |
| // These are used to generate histograms of errors seen with websql. |
| // See about:histograms in chromium. |
| void DatabaseBackend::reportOpenDatabaseResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) |
| { |
| DatabaseObserver::reportOpenDatabaseResult(this, errorSite, webSqlErrorCode, sqliteErrorCode); |
| } |
| |
| void DatabaseBackend::reportChangeVersionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) |
| { |
| DatabaseObserver::reportChangeVersionResult(this, errorSite, webSqlErrorCode, sqliteErrorCode); |
| } |
| |
| void DatabaseBackend::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) |
| { |
| DatabaseObserver::reportStartTransactionResult(this, errorSite, webSqlErrorCode, sqliteErrorCode); |
| } |
| |
| void DatabaseBackend::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) |
| { |
| DatabaseObserver::reportCommitTransactionResult(this, errorSite, webSqlErrorCode, sqliteErrorCode); |
| } |
| |
| void DatabaseBackend::reportExecuteStatementResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode) |
| { |
| DatabaseObserver::reportExecuteStatementResult(this, errorSite, webSqlErrorCode, sqliteErrorCode); |
| } |
| |
| void DatabaseBackend::reportVacuumDatabaseResult(int sqliteErrorCode) |
| { |
| DatabaseObserver::reportVacuumDatabaseResult(this, sqliteErrorCode); |
| } |
| |
| #endif // PLATFORM(CHROMIUM) |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(SQL_DATABASE) |