Skip to content

Commit e67ca42

Browse files
author
Jeff Brown
committed
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
1 parent 3e67922 commit e67ca42

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7312,6 +7312,7 @@ package android.database.sqlite {
73127312
method public static android.database.sqlite.SQLiteDatabase create(android.database.sqlite.SQLiteDatabase.CursorFactory);
73137313
method public int delete(java.lang.String, java.lang.String, java.lang.String[]);
73147314
method public static boolean deleteDatabase(java.io.File);
7315+
method public void disableWriteAheadLogging();
73157316
method public boolean enableWriteAheadLogging();
73167317
method public void endTransaction();
73177318
method public void execSQL(java.lang.String) throws android.database.SQLException;

core/java/android/database/sqlite/SQLiteConnectionPool.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,34 @@ public void reconfigure(SQLiteDatabaseConfiguration configuration) {
257257
synchronized (mLock) {
258258
throwIfClosedLocked();
259259

260+
boolean restrictToOneConnection = false;
261+
if (mConfiguration.journalMode.equalsIgnoreCase("WAL")
262+
!= configuration.journalMode.equalsIgnoreCase("WAL")) {
263+
// WAL mode can only be changed if there are no acquired connections
264+
// because we need to close all but the primary connection first.
265+
if (!mAcquiredConnections.isEmpty()) {
266+
throw new IllegalStateException("Write Ahead Logging (WAL) mode cannot "
267+
+ "be enabled or disabled while there are transactions in "
268+
+ "progress. Finish all transactions and release all active "
269+
+ "database connections first.");
270+
}
271+
272+
// Close all non-primary connections. This should happen immediately
273+
// because none of them are in use.
274+
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
275+
assert mAvailableNonPrimaryConnections.isEmpty();
276+
277+
restrictToOneConnection = true;
278+
}
279+
260280
if (mConfiguration.openFlags != configuration.openFlags) {
281+
// If we are changing open flags and WAL mode at the same time, then
282+
// we have no choice but to close the primary connection beforehand
283+
// because there can only be one connection open when we change WAL mode.
284+
if (restrictToOneConnection) {
285+
closeAvailableConnectionsAndLogExceptionsLocked();
286+
}
287+
261288
// Try to reopen the primary connection using the new open flags then
262289
// close and discard all existing connections.
263290
// This might throw if the database is corrupt or cannot be opened in
@@ -453,18 +480,23 @@ void onConnectionLeaked() {
453480

454481
// Can't throw.
455482
private void closeAvailableConnectionsAndLogExceptionsLocked() {
456-
final int count = mAvailableNonPrimaryConnections.size();
457-
for (int i = 0; i < count; i++) {
458-
closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
459-
}
460-
mAvailableNonPrimaryConnections.clear();
483+
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
461484

462485
if (mAvailablePrimaryConnection != null) {
463486
closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
464487
mAvailablePrimaryConnection = null;
465488
}
466489
}
467490

491+
// Can't throw.
492+
private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
493+
final int count = mAvailableNonPrimaryConnections.size();
494+
for (int i = 0; i < count; i++) {
495+
closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
496+
}
497+
mAvailableNonPrimaryConnections.clear();
498+
}
499+
468500
// Can't throw.
469501
private void closeExcessConnectionsAndLogExceptionsLocked() {
470502
int availableCount = mAvailableNonPrimaryConnections.size();

core/java/android/database/sqlite/SQLiteDatabase.java

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -834,8 +834,14 @@ public void addCustomFunction(String name, int numArgs, CustomFunction function)
834834

835835
synchronized (mLock) {
836836
throwIfNotOpenLocked();
837+
837838
mConfigurationLocked.customFunctions.add(wrapper);
838-
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
839+
try {
840+
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
841+
} catch (RuntimeException ex) {
842+
mConfigurationLocked.customFunctions.remove(wrapper);
843+
throw ex;
844+
}
839845
}
840846
}
841847

@@ -1733,8 +1739,15 @@ public void setLocale(Locale locale) {
17331739

17341740
synchronized (mLock) {
17351741
throwIfNotOpenLocked();
1742+
1743+
final Locale oldLocale = mConfigurationLocked.locale;
17361744
mConfigurationLocked.locale = locale;
1737-
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1745+
try {
1746+
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1747+
} catch (RuntimeException ex) {
1748+
mConfigurationLocked.locale = oldLocale;
1749+
throw ex;
1750+
}
17381751
}
17391752
}
17401753

@@ -1759,8 +1772,15 @@ public void setMaxSqlCacheSize(int cacheSize) {
17591772

17601773
synchronized (mLock) {
17611774
throwIfNotOpenLocked();
1775+
1776+
final int oldMaxSqlCacheSize = mConfigurationLocked.maxSqlCacheSize;
17621777
mConfigurationLocked.maxSqlCacheSize = cacheSize;
1763-
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1778+
try {
1779+
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1780+
} catch (RuntimeException ex) {
1781+
mConfigurationLocked.maxSqlCacheSize = oldMaxSqlCacheSize;
1782+
throw ex;
1783+
}
17641784
}
17651785
}
17661786

@@ -1805,6 +1825,10 @@ public void setMaxSqlCacheSize(int cacheSize) {
18051825
* </p>
18061826
*
18071827
* @return true if write-ahead-logging is set. false otherwise
1828+
*
1829+
* @throw IllegalStateException if there are transactions in progress at the
1830+
* time this method is called. WAL mode can only be changed when there are no
1831+
* transactions in progress.
18081832
*/
18091833
public boolean enableWriteAheadLogging() {
18101834
synchronized (mLock) {
@@ -1835,18 +1859,32 @@ public boolean enableWriteAheadLogging() {
18351859
return false;
18361860
}
18371861

1838-
mIsWALEnabledLocked = true;
1862+
final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
1863+
final String oldSyncMode = mConfigurationLocked.syncMode;
1864+
final String oldJournalMode = mConfigurationLocked.journalMode;
18391865
mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
18401866
mConfigurationLocked.syncMode = SQLiteGlobal.getWALSyncMode();
18411867
mConfigurationLocked.journalMode = "WAL";
1842-
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1868+
try {
1869+
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1870+
} catch (RuntimeException ex) {
1871+
mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
1872+
mConfigurationLocked.syncMode = oldSyncMode;
1873+
mConfigurationLocked.journalMode = oldJournalMode;
1874+
throw ex;
1875+
}
1876+
1877+
mIsWALEnabledLocked = true;
18431878
}
18441879
return true;
18451880
}
18461881

18471882
/**
18481883
* This method disables the features enabled by {@link #enableWriteAheadLogging()}.
1849-
* @hide
1884+
*
1885+
* @throw IllegalStateException if there are transactions in progress at the
1886+
* time this method is called. WAL mode can only be changed when there are no
1887+
* transactions in progress.
18501888
*/
18511889
public void disableWriteAheadLogging() {
18521890
synchronized (mLock) {
@@ -1856,11 +1894,22 @@ public void disableWriteAheadLogging() {
18561894
return;
18571895
}
18581896

1859-
mIsWALEnabledLocked = false;
1897+
final int oldMaxConnectionPoolSize = mConfigurationLocked.maxConnectionPoolSize;
1898+
final String oldSyncMode = mConfigurationLocked.syncMode;
1899+
final String oldJournalMode = mConfigurationLocked.journalMode;
18601900
mConfigurationLocked.maxConnectionPoolSize = 1;
18611901
mConfigurationLocked.syncMode = SQLiteGlobal.getDefaultSyncMode();
18621902
mConfigurationLocked.journalMode = SQLiteGlobal.getDefaultJournalMode();
1863-
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1903+
try {
1904+
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
1905+
} catch (RuntimeException ex) {
1906+
mConfigurationLocked.maxConnectionPoolSize = oldMaxConnectionPoolSize;
1907+
mConfigurationLocked.syncMode = oldSyncMode;
1908+
mConfigurationLocked.journalMode = oldJournalMode;
1909+
throw ex;
1910+
}
1911+
1912+
mIsWALEnabledLocked = false;
18641913
}
18651914
}
18661915

0 commit comments

Comments
 (0)