summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Brown <jeffbrown@google.com> 2012-03-21 17:24:05 -0700
committer Jeff Brown <jeffbrown@google.com> 2012-03-21 18:08:09 -0700
commite67ca420e4eb6ddf8ceefeb0d9dcc47d9ca189fc (patch)
treed8d0206806fc245148ac7cf12ad3c8f7312f1d09
parent3e6792232aa0ce3e650eaa03529c9eb2fe023ca9 (diff)
Throw if WAL enabled/disabled when connections are in use.
Changing WAL mode requires obtaining an exclusive lock on the database and can only be done when there are NO other active database connections. Check that this is really the case, and bail with a useful error message if an application attempts to change WAL mode while transactions are in progress. Expose disableWriteAheadLogging() in the API. Change-Id: I87599de3b88c53dcd75677aefd72e40de216c2c1
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java42
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java65
3 files changed, 95 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt
index 665b0548adba..018226ce4e9f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7312,6 +7312,7 @@ package android.database.sqlite {
method public static android.database.sqlite.SQLiteDatabase create(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public int delete(java.lang.String, java.lang.String, java.lang.String[]);
method public static boolean deleteDatabase(java.io.File);
+ method public void disableWriteAheadLogging();
method public boolean enableWriteAheadLogging();
method public void endTransaction();
method public void execSQL(java.lang.String) throws android.database.SQLException;
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 3562e8956911..00d3309b03dc 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -257,7 +257,34 @@ public final class SQLiteConnectionPool implements Closeable {
synchronized (mLock) {
throwIfClosedLocked();
+ boolean restrictToOneConnection = false;
+ if (mConfiguration.journalMode.equalsIgnoreCase("WAL")
+ != configuration.journalMode.equalsIgnoreCase("WAL")) {
+ // 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()) {
+ throw new IllegalStateException("Write Ahead Logging (WAL) mode cannot "
+ + "be enabled or disabled while there are transactions in "
+ + "progress. Finish all transactions and release all active "
+ + "database connections first.");
+ }
+
+ // Close all non-primary connections. This should happen immediately
+ // because none of them are in use.
+ closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
+ assert mAvailableNonPrimaryConnections.isEmpty();
+
+ restrictToOneConnection = true;
+ }
+
if (mConfiguration.openFlags != configuration.openFlags) {
+ // If we are changing open flags and WAL mode at the same time, then
+ // we have no choice but to close the primary connection beforehand
+ // because there can only be one connection open when we change WAL mode.
+ if (restrictToOneConnection) {
+ closeAvailableConnectionsAndLogExceptionsLocked();
+ }
+
// Try to reopen the primary connection using the new open flags then
// close and discard all existing connections.
// This might throw if the database is corrupt or cannot be opened in
@@ -453,11 +480,7 @@ public final class SQLiteConnectionPool implements Closeable {
// Can't throw.
private void closeAvailableConnectionsAndLogExceptionsLocked() {
- final int count = mAvailableNonPrimaryConnections.size();
- for (int i = 0; i < count; i++) {
- closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
- }
- mAvailableNonPrimaryConnections.clear();
+ closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
if (mAvailablePrimaryConnection != null) {
closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
@@ -466,6 +489,15 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
+ final int count = mAvailableNonPrimaryConnections.size();
+ for (int i = 0; i < count; i++) {
+ closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
+ }
+ mAvailableNonPrimaryConnections.clear();
+ }
+
+ // Can't throw.
private void closeExcessConnectionsAndLogExceptionsLocked() {
int availableCount = mAvailableNonPrimaryConnections.size();
while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index bf32ea722d80..e575c26ff3c5 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -834,8 +834,14 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
mConfigurationLocked.customFunctions.add(wrapper);
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.customFunctions.remove(wrapper);
+ throw ex;
+ }
}
}
@@ -1733,8 +1739,15 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
+ final Locale oldLocale = mConfigurationLocked.locale;
mConfigurationLocked.locale = locale;
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.locale = oldLocale;
+ throw ex;
+ }
}
}
@@ -1759,8 +1772,15 @@ public final class SQLiteDatabase extends SQLiteClosable {
synchronized (mLock) {
throwIfNotOpenLocked();
+
+ final int oldMaxSqlCacheSize = mConfigurationLocked.maxSqlCacheSize;
mConfigurationLocked.maxSqlCacheSize = cacheSize;
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.maxSqlCacheSize = oldMaxSqlCacheSize;
+ throw ex;
+ }
}
}
@@ -1805,6 +1825,10 @@ public final class SQLiteDatabase extends SQLiteClosable {
* </p>
*
* @return true if write-ahead-logging is set. false otherwise
+ *
+ * @throw IllegalStateException if there are transactions in progress at the
+ * time this method is called. WAL mode can only be changed when there are no
+ * transactions in progress.
*/
public boolean enableWriteAheadLogging() {
synchronized (mLock) {
@@ -1835,18 +1859,32 @@ public final class SQLiteDatabase extends SQLiteClosable {
return false;
}
- mIsWALEnabledLocked = true;
+ 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";
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
+ mConfigurationLocked.syncMode = oldSyncMode;
+ mConfigurationLocked.journalMode = oldJournalMode;
+ throw ex;
+ }
+
+ mIsWALEnabledLocked = true;
}
return true;
}
/**
* This method disables the features enabled by {@link #enableWriteAheadLogging()}.
- * @hide
+ *
+ * @throw IllegalStateException if there are transactions in progress at the
+ * time this method is called. WAL mode can only be changed when there are no
+ * transactions in progress.
*/
public void disableWriteAheadLogging() {
synchronized (mLock) {
@@ -1856,11 +1894,22 @@ public final class SQLiteDatabase extends SQLiteClosable {
return;
}
- mIsWALEnabledLocked = false;
+ 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();
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
+ mConfigurationLocked.syncMode = oldSyncMode;
+ mConfigurationLocked.journalMode = oldJournalMode;
+ throw ex;
+ }
+
+ mIsWALEnabledLocked = false;
}
}