summaryrefslogtreecommitdiffstats
path: root/core/java/android/database
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2012-03-22 14:15:01 -0700
committerJeff Brown <jeffbrown@google.com>2012-03-22 14:53:01 -0700
commitd67c8c67899481682657d41a61f3846b8d77d165 (patch)
tree7e572eb725a3f15b4580201c9a382f41c61739c0 /core/java/android/database
parent9ea77fc821918ea562ff4907945b865e39e0201a (diff)
downloadframeworks_base-d67c8c67899481682657d41a61f3846b8d77d165.zip
frameworks_base-d67c8c67899481682657d41a61f3846b8d77d165.tar.gz
frameworks_base-d67c8c67899481682657d41a61f3846b8d77d165.tar.bz2
Work around problems changing the database journal mode.
Because we always disable WAL mode when a database is first opened (even if we intend to re-enable it), we can encounter problems if there is another open connection to the database somewhere. This can happen for a variety of reasons such as an application opening the same database in multiple processes at the same time or if there is a crashing content provider service that the ActivityManager has removed from its registry but whose process hasn't quite died yet by the time it is restarted in a new process. If we don't change the journal mode, nothing really bad happens. In the worst case, an application that enables WAL might not actually get it, although it can still use connection pooling. Bug: 6124556 Change-Id: Ia2ffdbbc8f82721b170f3bf71bd5242dfd56d9ac
Diffstat (limited to 'core/java/android/database')
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java90
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java28
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java18
4 files changed, 72 insertions, 67 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e2c222b..bf10bcb 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -211,8 +211,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
setPageSize();
- setSyncModeFromConfiguration();
- setJournalModeFromConfiguration();
+ setWalModeFromConfiguration();
setJournalSizeLimit();
setAutoCheckpointInterval();
setLocaleFromConfiguration();
@@ -268,28 +267,69 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
}
- private void setSyncModeFromConfiguration() {
+ private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final String newValue = mConfiguration.syncMode;
- String value = executeForString("PRAGMA synchronous", null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- execute("PRAGMA synchronous=" + newValue, null, null);
+ if (mConfiguration.walEnabled) {
+ setJournalMode("WAL");
+ setSyncMode(SQLiteGlobal.getWALSyncMode());
+ } else {
+ setJournalMode(SQLiteGlobal.getDefaultJournalMode());
+ setSyncMode(SQLiteGlobal.getDefaultSyncMode());
}
}
}
- private void setJournalModeFromConfiguration() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final String newValue = mConfiguration.journalMode;
- String value = executeForString("PRAGMA journal_mode", null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- value = executeForString("PRAGMA journal_mode=" + newValue, null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- Log.e(TAG, "setting journal_mode to " + newValue
- + " failed for db: " + mConfiguration.label
- + " (on pragma set journal_mode, sqlite returned:" + value);
+ private void setSyncMode(String newValue) {
+ String value = executeForString("PRAGMA synchronous", null, null);
+ if (!canonicalizeSyncMode(value).equalsIgnoreCase(
+ canonicalizeSyncMode(newValue))) {
+ execute("PRAGMA synchronous=" + newValue, null, null);
+ }
+ }
+
+ private static String canonicalizeSyncMode(String value) {
+ if (value.equals("0")) {
+ return "OFF";
+ } else if (value.equals("1")) {
+ return "NORMAL";
+ } else if (value.equals("2")) {
+ return "FULL";
+ }
+ return value;
+ }
+
+ private void setJournalMode(String newValue) {
+ String value = executeForString("PRAGMA journal_mode", null, null);
+ if (!value.equalsIgnoreCase(newValue)) {
+ try {
+ String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
+ if (result.equalsIgnoreCase(newValue)) {
+ return;
}
+ // PRAGMA journal_mode silently fails and returns the original journal
+ // mode in some cases if the journal mode could not be changed.
+ } catch (SQLiteDatabaseLockedException ex) {
+ // This error (SQLITE_BUSY) occurs if one connection has the database
+ // open in WAL mode and another tries to change it to non-WAL.
}
+ // Because we always disable WAL mode when a database is first opened
+ // (even if we intend to re-enable it), we can encounter problems if
+ // there is another open connection to the database somewhere.
+ // This can happen for a variety of reasons such as an application opening
+ // the same database in multiple processes at the same time or if there is a
+ // crashing content provider service that the ActivityManager has
+ // removed from its registry but whose process hasn't quite died yet
+ // by the time it is restarted in a new process.
+ //
+ // If we don't change the journal mode, nothing really bad happens.
+ // In the worst case, an application that enables WAL might not actually
+ // get it, although it can still use connection pooling.
+ Log.w(TAG, "Could not change the database journal mode of '"
+ + mConfiguration.label + "' from '" + value + "' to '" + newValue
+ + "' because the database is locked. This usually means that "
+ + "there are other open connections to the database which prevents "
+ + "the database from enabling or disabling write-ahead logging mode. "
+ + "Proceeding without changing the journal mode.");
}
}
@@ -349,10 +389,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
// Remember what changed.
- boolean syncModeChanged = !configuration.syncMode.equalsIgnoreCase(
- mConfiguration.syncMode);
- boolean journalModeChanged = !configuration.journalMode.equalsIgnoreCase(
- mConfiguration.journalMode);
+ boolean walModeChanged = configuration.walEnabled != mConfiguration.walEnabled;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
// Update configuration parameters.
@@ -361,14 +398,9 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
// Update prepared statement cache size.
mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
- // Update sync mode.
- if (syncModeChanged) {
- setSyncModeFromConfiguration();
- }
-
- // Update journal mode.
- if (journalModeChanged) {
- setJournalModeFromConfiguration();
+ // Update WAL.
+ if (walModeChanged) {
+ setWalModeFromConfiguration();
}
// Update locale.
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 00d3309..27c9ee5 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -258,8 +258,7 @@ public final class SQLiteConnectionPool implements Closeable {
throwIfClosedLocked();
boolean restrictToOneConnection = false;
- if (mConfiguration.journalMode.equalsIgnoreCase("WAL")
- != configuration.journalMode.equalsIgnoreCase("WAL")) {
+ if (mConfiguration.walEnabled != configuration.walEnabled) {
// WAL mode can only be changed if there are no acquired connections
// because we need to close all but the primary connection first.
if (!mAcquiredConnections.isEmpty()) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index e575c26..e0a4180 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -127,10 +127,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
// INVARIANT: Guarded by mLock.
private boolean mHasAttachedDbsLocked;
- // True if the database is in WAL mode.
- // INVARIANT: Guarded by mLock.
- private boolean mIsWALEnabledLocked;
-
/**
* When a constraint violation occurs, an immediate ROLLBACK occurs,
* thus ending the current transaction, and the command aborts with a
@@ -1834,7 +1830,7 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
- if (mIsWALEnabledLocked) {
+ if (mConfigurationLocked.walEnabled) {
return true;
}
@@ -1860,21 +1856,15 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
- final String oldSyncMode = mConfigurationLocked.syncMode;
- final String oldJournalMode = mConfigurationLocked.journalMode;
mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
- mConfigurationLocked.syncMode = SQLiteGlobal.getWALSyncMode();
- mConfigurationLocked.journalMode = "WAL";
+ mConfigurationLocked.walEnabled = true;
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
- mConfigurationLocked.syncMode = oldSyncMode;
- mConfigurationLocked.journalMode = oldJournalMode;
+ mConfigurationLocked.walEnabled = false;
throw ex;
}
-
- mIsWALEnabledLocked = true;
}
return true;
}
@@ -1890,26 +1880,20 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
- if (!mIsWALEnabledLocked) {
+ if (!mConfigurationLocked.walEnabled) {
return;
}
final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
- final String oldSyncMode = mConfigurationLocked.syncMode;
- final String oldJournalMode = mConfigurationLocked.journalMode;
mConfigurationLocked.maxConnectionPoolSize = 1;
- mConfigurationLocked.syncMode = SQLiteGlobal.getDefaultSyncMode();
- mConfigurationLocked.journalMode = SQLiteGlobal.getDefaultJournalMode();
+ mConfigurationLocked.walEnabled = false;
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
- mConfigurationLocked.syncMode = oldSyncMode;
- mConfigurationLocked.journalMode = oldJournalMode;
+ mConfigurationLocked.walEnabled = true;
throw ex;
}
-
- mIsWALEnabledLocked = false;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index efbcaca..e06a5ee 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -85,18 +85,11 @@ public final class SQLiteDatabaseConfiguration {
public Locale locale;
/**
- * The database synchronization mode.
+ * True if WAL mode is enabled.
*
- * Default is {@link SQLiteGlobal#getDefaultSyncMode()}.
+ * Default is false.
*/
- public String syncMode;
-
- /**
- * The database journal mode.
- *
- * Default is {@link SQLiteGlobal#getDefaultJournalMode()}.
- */
- public String journalMode;
+ public boolean walEnabled;
/**
* The custom functions to register.
@@ -124,8 +117,6 @@ public final class SQLiteDatabaseConfiguration {
maxConnectionPoolSize = 1;
maxSqlCacheSize = 25;
locale = Locale.getDefault();
- syncMode = SQLiteGlobal.getDefaultSyncMode();
- journalMode = SQLiteGlobal.getDefaultJournalMode();
}
/**
@@ -162,8 +153,7 @@ public final class SQLiteDatabaseConfiguration {
maxConnectionPoolSize = other.maxConnectionPoolSize;
maxSqlCacheSize = other.maxSqlCacheSize;
locale = other.locale;
- syncMode = other.syncMode;
- journalMode = other.journalMode;
+ walEnabled = other.walEnabled;
customFunctions.clear();
customFunctions.addAll(other.customFunctions);
}