Index: Source/WebCore/storage/AbstractDatabase.cpp |
=================================================================== |
--- Source/WebCore/storage/AbstractDatabase.cpp (revision 92303) |
+++ Source/WebCore/storage/AbstractDatabase.cpp (working copy) |
@@ -34,6 +34,7 @@ |
#include "DatabaseTracker.h" |
#include "Logging.h" |
#include "SQLiteStatement.h" |
+#include "SQLiteTransaction.h" |
#include "ScriptExecutionContext.h" |
#include "SecurityOrigin.h" |
#include <wtf/HashMap.h> |
@@ -235,13 +236,16 @@ |
String AbstractDatabase::version() const |
{ |
- MutexLocker locker(guidMutex()); |
- return guidToVersionMap().get(m_guid).threadsafeCopy(); |
+ // 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(); |
} |
-static const int maxSqliteBusyWaitTime = 30000; |
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; |
@@ -250,8 +254,6 @@ |
if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) |
LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); |
- ASSERT(m_databaseAuthorizer); |
- m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); |
m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); |
String currentVersion; |
@@ -263,43 +265,68 @@ |
// Map null string to empty string (see updateGuidVersionMap()). |
currentVersion = entry->second.isNull() ? String("") : entry->second; |
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 able to read |
+ // the value without waiting, and otherwise use the cached value (which may be off). |
+ // 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; |
+ } |
+ |
if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { |
m_new = true; |
if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (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", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); |
ec = INVALID_STATE_ERR; |
- // Close the handle to the database file. |
+ transaction.rollback(); |
m_sqliteDatabase.close(); |
return false; |
} |
- } |
- |
- if (!getVersionFromDatabase(currentVersion)) { |
+ } else if (!getVersionFromDatabase(currentVersion, false)) { |
LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); |
ec = INVALID_STATE_ERR; |
- // Close the handle to the database file. |
+ 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)) { |
+ 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; |
- // Close the handle to the database file. |
+ transaction.rollback(); |
m_sqliteDatabase.close(); |
return false; |
} |
currentVersion = m_expectedVersion; |
} |
- |
updateGuidVersionMap(m_guid, currentVersion); |
+ transaction.commit(); |
} |
} |
@@ -314,13 +341,18 @@ |
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; |
- // Close the handle to the database file. |
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; |
} |
@@ -364,14 +396,17 @@ |
return key; |
} |
-bool AbstractDatabase::getVersionFromDatabase(String& version) |
+bool AbstractDatabase::getVersionFromDatabase(String& version, bool shouldCacheVersion) |
{ |
DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';")); |
m_databaseAuthorizer->disable(); |
bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version); |
- if (!result) |
+ if (result) { |
+ if (shouldCacheVersion) |
+ setCachedVersion(version); |
+ } else |
LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); |
m_databaseAuthorizer->enable(); |
@@ -379,7 +414,7 @@ |
return result; |
} |
-bool AbstractDatabase::setVersionInDatabase(const String& version) |
+bool AbstractDatabase::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()). |
@@ -388,7 +423,10 @@ |
m_databaseAuthorizer->disable(); |
bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version); |
- if (!result) |
+ if (result) { |
+ if (shouldCacheVersion) |
+ setCachedVersion(version); |
+ } else |
LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data()); |
m_databaseAuthorizer->enable(); |
@@ -396,24 +434,37 @@ |
return result; |
} |
-bool AbstractDatabase::versionMatchesExpected() const |
+void AbstractDatabase::setExpectedVersion(const String& version) |
{ |
- if (!m_expectedVersion.isEmpty()) { |
- MutexLocker locker(guidMutex()); |
- return m_expectedVersion == guidToVersionMap().get(m_guid); |
- } |
+ m_expectedVersion = version.threadsafeCopy(); |
+} |
- return true; |
+String AbstractDatabase::getCachedVersion() const |
+{ |
+ MutexLocker locker(guidMutex()); |
+ return guidToVersionMap().get(m_guid).threadsafeCopy(); |
} |
-void AbstractDatabase::setExpectedVersion(const String& version) |
+void AbstractDatabase::setCachedVersion(const String& actualVersion) |
{ |
- m_expectedVersion = version.threadsafeCopy(); |
// Update the in memory database version map. |
MutexLocker locker(guidMutex()); |
- updateGuidVersionMap(m_guid, version); |
+ updateGuidVersionMap(m_guid, actualVersion); |
} |
+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 AbstractDatabase::disableAuthorizer() |
{ |
ASSERT(m_databaseAuthorizer); |