Merge "Apps can now specify journal/synchronous mode"
diff --git a/api/current.txt b/api/current.txt
index 387e815..be2c71f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12136,9 +12136,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12150,8 +12152,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12198,6 +12202,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
diff --git a/api/system-current.txt b/api/system-current.txt
index fb9c73d..5f37686 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12883,9 +12883,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12897,8 +12899,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12945,6 +12949,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
diff --git a/api/test-current.txt b/api/test-current.txt
index 9f5ab62..c68676b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12226,9 +12226,11 @@
method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
method public android.database.DatabaseErrorHandler getErrorHandler();
method public long getIdleConnectionTimeout();
+ method public java.lang.String getJournalMode();
method public int getLookasideSlotCount();
method public int getLookasideSlotSize();
method public int getOpenFlags();
+ method public java.lang.String getSynchronousMode();
}
public static final class SQLiteDatabase.OpenParams.Builder {
@@ -12240,8 +12242,10 @@
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setErrorHandler(android.database.DatabaseErrorHandler);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setIdleConnectionTimeout(long);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setJournalMode(java.lang.String);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setLookasideConfig(int, int);
method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setOpenFlags(int);
+ method public android.database.sqlite.SQLiteDatabase.OpenParams.Builder setSynchronousMode(java.lang.String);
}
public class SQLiteDatabaseCorruptException extends android.database.sqlite.SQLiteException {
@@ -12335,6 +12339,7 @@
public abstract class SQLiteOpenHelper {
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
+ ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams);
method public synchronized void close();
method public java.lang.String getDatabaseName();
method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index b692039..2c93a7f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -289,14 +289,19 @@
private void setWalModeFromConfiguration() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- boolean walEnabled =
+ final boolean walEnabled =
(mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
- if (walEnabled || mConfiguration.useCompatibilityWal) {
+ // Use compatibility WAL unless an app explicitly set journal/synchronous mode
+ final boolean useCompatibilityWal = mConfiguration.journalMode == null
+ && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal;
+ if (walEnabled || useCompatibilityWal) {
setJournalMode("WAL");
setSyncMode(SQLiteGlobal.getWALSyncMode());
} else {
- setJournalMode(SQLiteGlobal.getDefaultJournalMode());
- setSyncMode(SQLiteGlobal.getDefaultSyncMode());
+ setJournalMode(mConfiguration.journalMode == null
+ ? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
+ setSyncMode(mConfiguration.syncMode == null
+ ? SQLiteGlobal.getDefaultSyncMode() : mConfiguration.syncMode);
}
}
}
@@ -310,12 +315,10 @@
}
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";
+ switch (value) {
+ case "0": return "OFF";
+ case "1": return "NORMAL";
+ case "2": return "FULL";
}
return value;
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 83b8dc7..863fb19 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -262,7 +262,8 @@
private SQLiteDatabase(final String path, final int openFlags,
CursorFactory cursorFactory, DatabaseErrorHandler errorHandler,
- int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs) {
+ int lookasideSlotSize, int lookasideSlotCount, long idleConnectionTimeoutMs,
+ String journalMode, String syncMode) {
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
@@ -285,6 +286,8 @@
}
}
mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
+ mConfigurationLocked.journalMode = journalMode;
+ mConfigurationLocked.syncMode = syncMode;
mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
}
@@ -721,7 +724,7 @@
SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
openParams.mCursorFactory, openParams.mErrorHandler,
openParams.mLookasideSlotSize, openParams.mLookasideSlotCount,
- openParams.mIdleConnectionTimeout);
+ openParams.mIdleConnectionTimeout, openParams.mJournalMode, openParams.mSyncMode);
db.open();
return db;
}
@@ -747,7 +750,8 @@
*/
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory,
@DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) {
- SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1);
+ SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1, null,
+ null);
db.open();
return db;
}
@@ -2302,17 +2306,21 @@
private final DatabaseErrorHandler mErrorHandler;
private final int mLookasideSlotSize;
private final int mLookasideSlotCount;
- private long mIdleConnectionTimeout;
+ private final long mIdleConnectionTimeout;
+ private final String mJournalMode;
+ private final String mSyncMode;
private OpenParams(int openFlags, CursorFactory cursorFactory,
DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount,
- long idleConnectionTimeout) {
+ long idleConnectionTimeout, String journalMode, String syncMode) {
mOpenFlags = openFlags;
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler;
mLookasideSlotSize = lookasideSlotSize;
mLookasideSlotCount = lookasideSlotCount;
mIdleConnectionTimeout = idleConnectionTimeout;
+ mJournalMode = journalMode;
+ mSyncMode = syncMode;
}
/**
@@ -2379,6 +2387,28 @@
}
/**
+ * Returns <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>.
+ * This journal mode will only be used if {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING}
+ * flag is not set, otherwise a platform will use "WAL" journal mode.
+ * @see Builder#setJournalMode(String)
+ */
+ @Nullable
+ public String getJournalMode() {
+ return mJournalMode;
+ }
+
+ /**
+ * Returns <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>.
+ * This value will only be used when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag
+ * is not set, otherwise a system wide default will be used.
+ * @see Builder#setSynchronousMode(String)
+ */
+ @Nullable
+ public String getSynchronousMode() {
+ return mSyncMode;
+ }
+
+ /**
* Creates a new instance of builder {@link Builder#Builder(OpenParams) initialized} with
* {@code this} parameters.
* @hide
@@ -2398,6 +2428,8 @@
private int mOpenFlags;
private CursorFactory mCursorFactory;
private DatabaseErrorHandler mErrorHandler;
+ private String mJournalMode;
+ private String mSyncMode;
public Builder() {
}
@@ -2408,6 +2440,8 @@
mOpenFlags = params.mOpenFlags;
mCursorFactory = params.mCursorFactory;
mErrorHandler = params.mErrorHandler;
+ mJournalMode = params.mJournalMode;
+ mSyncMode = params.mSyncMode;
}
/**
@@ -2539,6 +2573,30 @@
return this;
}
+
+ /**
+ * Sets <a href="https://sqlite.org/pragma.html#pragma_journal_mode">journal mode</a>
+ * to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag is not set.
+ */
+ @NonNull
+ public Builder setJournalMode(@NonNull String journalMode) {
+ Preconditions.checkNotNull(journalMode);
+ mJournalMode = journalMode;
+ return this;
+ }
+
+ /**
+ * Sets <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>
+ * to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag is not set.
+ * @return
+ */
+ @NonNull
+ public Builder setSynchronousMode(@NonNull String syncMode) {
+ Preconditions.checkNotNull(syncMode);
+ mSyncMode = syncMode;
+ return this;
+ }
+
/**
* Creates an instance of {@link OpenParams} with the options that were previously set
* on this builder
@@ -2546,7 +2604,7 @@
@NonNull
public OpenParams build() {
return new OpenParams(mOpenFlags, mCursorFactory, mErrorHandler, mLookasideSlotSize,
- mLookasideSlotCount, mIdleConnectionTimeout);
+ mLookasideSlotCount, mIdleConnectionTimeout, mJournalMode, mSyncMode);
}
}
}
@@ -2561,4 +2619,6 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface DatabaseOpenFlags {}
+
}
+
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 905da724..a14df1e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -120,6 +120,18 @@
public boolean useCompatibilityWal;
/**
+ * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
+ * <p>Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()}
+ */
+ public String journalMode;
+
+ /**
+ * Synchronous mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
+ * <p>Default is returned by {@link SQLiteGlobal#getDefaultSyncMode()}
+ */
+ public String syncMode;
+
+ /**
* Creates a database configuration with the required parameters for opening a
* database and default values for all other parameters.
*
@@ -180,6 +192,8 @@
lookasideSlotCount = other.lookasideSlotCount;
idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
useCompatibilityWal = other.useCompatibilityWal;
+ journalMode = other.journalMode;
+ syncMode = other.syncMode;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index cc9e0f4..d4699a8 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -17,6 +17,8 @@
package android.database.sqlite;
import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.SQLException;
@@ -24,6 +26,8 @@
import android.os.FileUtils;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.io.File;
/**
@@ -69,7 +73,8 @@
* {@link #onUpgrade} will be used to upgrade the database; if the database is
* newer, {@link #onDowngrade} will be used to downgrade the database
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
+ public SQLiteOpenHelper(@NonNull Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
@@ -90,12 +95,33 @@
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
* corruption, or null to use the default error handler.
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
- DatabaseErrorHandler errorHandler) {
+ public SQLiteOpenHelper(@NonNull Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version,
+ @Nullable DatabaseErrorHandler errorHandler) {
this(context, name, factory, version, 0, errorHandler);
}
/**
+ * Create a helper object to create, open, and/or manage a database.
+ * This method always returns very quickly. The database is not actually
+ * created or opened until one of {@link #getWritableDatabase} or
+ * {@link #getReadableDatabase} is called.
+ *
+ * @param context to use to open or create the database
+ * @param name of the database file, or null for an in-memory database
+ * @param version number of the database (starting at 1); if the database is older,
+ * {@link #onUpgrade} will be used to upgrade the database; if the database is
+ * newer, {@link #onDowngrade} will be used to downgrade the database
+ * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
+ * Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
+ * set when the helper opens the database
+ */
+ public SQLiteOpenHelper(@NonNull Context context, @Nullable String name, int version,
+ @NonNull SQLiteDatabase.OpenParams openParams) {
+ this(context, name, version, 0, openParams.toBuilder());
+ }
+
+ /**
* Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
* but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
* versions of this database that are no longer supported. If a database with older version that
@@ -118,17 +144,27 @@
* @see #onUpgrade(SQLiteDatabase, int, int)
* @hide
*/
- public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
- int minimumSupportedVersion, DatabaseErrorHandler errorHandler) {
+ public SQLiteOpenHelper(@NonNull Context context, @Nullable String name,
+ @Nullable CursorFactory factory, int version,
+ int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
+ this(context, name, version, minimumSupportedVersion,
+ new SQLiteDatabase.OpenParams.Builder());
+ mOpenParamsBuilder.setCursorFactory(factory);
+ mOpenParamsBuilder.setErrorHandler(errorHandler);
+ }
+
+ private SQLiteOpenHelper(@NonNull Context context, @Nullable String name, int version,
+ int minimumSupportedVersion,
+ @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(openParamsBuilder);
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
mContext = context;
mName = name;
mNewVersion = version;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
- mOpenParamsBuilder = new SQLiteDatabase.OpenParams.Builder();
- mOpenParamsBuilder.setCursorFactory(factory);
- mOpenParamsBuilder.setErrorHandler(errorHandler);
+ mOpenParamsBuilder = openParamsBuilder;
mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
}
diff --git a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
index 75eeb93..9ed3f11b 100644
--- a/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
+++ b/core/tests/coretests/src/android/database/SQLiteOpenHelperTest.java
@@ -16,6 +16,7 @@
package android.database;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -61,6 +62,10 @@
super(context, name, null, 1);
}
+ TestHelper(Context context, String name, int version, SQLiteDatabase.OpenParams params) {
+ super(context, name, version, params);
+ }
+
@Override
public void onCreate(SQLiteDatabase db) {
}
@@ -168,4 +173,25 @@
}
assertTrue("No dbstat found for " + dbName, dbStatFound);
}
+
+ @Test
+ public void testOpenParamsConstructor() {
+ SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+ .setJournalMode("DELETE")
+ .setSynchronousMode("OFF")
+ .build();
+
+ TestHelper helper = new TestHelper(mContext, "openhelper_test_constructor", 1, params);
+ mHelpersToClose.add(helper);
+
+ String journalMode = DatabaseUtils
+ .stringForQuery(helper.getReadableDatabase(), "PRAGMA journal_mode", null);
+
+ assertEquals("DELETE", journalMode.toUpperCase());
+ String syncMode = DatabaseUtils
+ .stringForQuery(helper.getReadableDatabase(), "PRAGMA synchronous", null);
+
+ assertEquals("0", syncMode);
+ }
+
}