diff options
author | Jeff Brown <jeffbrown@google.com> | 2012-03-23 14:38:06 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2012-03-23 14:49:39 -0700 |
commit | 96496adb611ced49ed1c2c778c616d1f8a5d0e6b (patch) | |
tree | 1b9a6dc212632c5514a0b914bafedcaed899ba98 /core/java/android/database | |
parent | 47847f3f4dcf2a0dbea0bc0e4f02528e21d37a88 (diff) | |
download | frameworks_base-96496adb611ced49ed1c2c778c616d1f8a5d0e6b.zip frameworks_base-96496adb611ced49ed1c2c778c616d1f8a5d0e6b.tar.gz frameworks_base-96496adb611ced49ed1c2c778c616d1f8a5d0e6b.tar.bz2 |
Provide an API for enabling foreign key constraints.
Also provide a lifecycle method on SQLiteOpenHelper so that
applications can configure things like this before the onCreate,
onUpgrade, onDowngrade and onOpen callbacks run.
Change-Id: If3d1396720bd2e032dd9e034733fb1ff9a9733dd
Diffstat (limited to 'core/java/android/database')
5 files changed, 127 insertions, 2 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index e999316..254f652 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -211,6 +211,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME); setPageSize(); + setForeignKeyModeFromConfiguration(); setWalModeFromConfiguration(); setJournalSizeLimit(); setAutoCheckpointInterval(); @@ -267,6 +268,16 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } } + private void setForeignKeyModeFromConfiguration() { + if (!mIsReadOnlyConnection) { + final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0; + long value = executeForLong("PRAGMA foreign_keys", null, null); + if (value != newValue) { + execute("PRAGMA foreign_keys=" + newValue, null, null); + } + } + } + private void setWalModeFromConfiguration() { if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) { if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) { @@ -389,6 +400,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } // Remember what changed. + boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled + != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); @@ -399,6 +412,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // Update prepared statement cache size. mPreparedStatementCache.resize(configuration.maxSqlCacheSize); + // Update foreign key mode. + if (foreignKeyModeChanged) { + setForeignKeyModeFromConfiguration(); + } + // Update WAL. if (walModeChanged) { setWalModeFromConfiguration(); diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index 0538ce4..5c8e38b 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -277,6 +277,20 @@ public final class SQLiteConnectionPool implements Closeable { assert mAvailableNonPrimaryConnections.isEmpty(); } + boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled + != mConfiguration.foreignKeyConstraintsEnabled; + if (foreignKeyModeChanged) { + // Foreign key constraints can only be changed if there are no transactions + // in progress. To make this clear, we throw an exception if there are + // any acquired connections. + if (!mAcquiredConnections.isEmpty()) { + throw new IllegalStateException("Foreign Key Constraints cannot " + + "be enabled or disabled while there are transactions in " + + "progress. Finish all transactions and release all active " + + "database connections first."); + } + } + 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 diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 049a615..7bd0c8d 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1793,6 +1793,53 @@ public final class SQLiteDatabase extends SQLiteClosable { } /** + * Sets whether foreign key constraints are enabled for the database. + * <p> + * By default, foreign key constraints are not enforced by the database. + * This method allows an application to enable foreign key constraints. + * It must be called each time the database is opened to ensure that foreign + * key constraints are enabled for the session. + * </p><p> + * A good time to call this method is right after calling {@link #openOrCreateDatabase} + * or in the {@link SQLiteOpenHelper#onConfigure} callback. + * </p><p> + * When foreign key constraints are disabled, the database does not check whether + * changes to the database will violate foreign key constraints. Likewise, when + * foreign key constraints are disabled, the database will not execute cascade + * delete or update triggers. As a result, it is possible for the database + * state to become inconsistent. To perform a database integrity check, + * call {@link #isDatabaseIntegrityOk}. + * </p><p> + * This method must not be called while a transaction is in progress. + * </p><p> + * See also <a href="http://sqlite.org/foreignkeys.html">SQLite Foreign Key Constraints</a> + * for more details about foreign key constraint support. + * </p> + * + * @param enable True to enable foreign key constraints, false to disable them. + * + * @throws IllegalStateException if the are transactions is in progress + * when this method is called. + */ + public void setForeignKeyConstraintsEnabled(boolean enable) { + synchronized (mLock) { + throwIfNotOpenLocked(); + + if (mConfigurationLocked.foreignKeyConstraintsEnabled == enable) { + return; + } + + mConfigurationLocked.foreignKeyConstraintsEnabled = enable; + try { + mConnectionPoolLocked.reconfigure(mConfigurationLocked); + } catch (RuntimeException ex) { + mConfigurationLocked.foreignKeyConstraintsEnabled = !enable; + throw ex; + } + } + } + + /** * This method enables parallel execution of queries from multiple threads on the * same database. It does this by opening multiple connections to the database * and using a different database connection for each query. The database diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 123c2c6..549ab90 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -77,6 +77,13 @@ public final class SQLiteDatabaseConfiguration { public Locale locale; /** + * True if foreign key constraints are enabled. + * + * Default is false. + */ + public boolean foreignKeyConstraintsEnabled; + + /** * The custom functions to register. */ public final ArrayList<SQLiteCustomFunction> customFunctions = @@ -136,6 +143,7 @@ public final class SQLiteDatabaseConfiguration { openFlags = other.openFlags; maxSqlCacheSize = other.maxSqlCacheSize; locale = other.locale; + foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled; customFunctions.clear(); customFunctions.addAll(other.customFunctions); } diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index fe37b8f..431eca2 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -237,6 +237,8 @@ public abstract class SQLiteOpenHelper { } } + onConfigure(db); + final int version = db.getVersion(); if (version != mNewVersion) { if (db.isReadOnly()) { @@ -261,6 +263,7 @@ public abstract class SQLiteOpenHelper { db.endTransaction(); } } + onOpen(db); if (db.isReadOnly()) { @@ -290,6 +293,25 @@ public abstract class SQLiteOpenHelper { } /** + * Called when the database connection is being configured, to enable features + * such as write-ahead logging or foreign key support. + * <p> + * This method is called before {@link #onCreate}, {@link #onUpgrade}, + * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify + * the database except to configure the database connection as required. + * </p><p> + * This method should only call methods that configure the parameters of the + * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging} + * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, + * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize}, + * or executing PRAGMA statements. + * </p> + * + * @param db The database. + */ + public void onConfigure(SQLiteDatabase db) {} + + /** * Called when the database is created for the first time. This is where the * creation of tables and the initial population of the tables should happen. * @@ -302,11 +324,16 @@ public abstract class SQLiteOpenHelper { * should use this method to drop tables, add tables, or do anything else it * needs to upgrade to the new schema version. * - * <p>The SQLite ALTER TABLE documentation can be found + * <p> + * The SQLite ALTER TABLE documentation can be found * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns * you can use ALTER TABLE to rename the old table, then create the new table and then * populate the new table with the contents of the old table. + * </p><p> + * This method executes within a transaction. If an exception is thrown, all changes + * will automatically be rolled back. + * </p> * * @param db The database. * @param oldVersion The old database version. @@ -316,11 +343,16 @@ public abstract class SQLiteOpenHelper { /** * Called when the database needs to be downgraded. This is strictly similar to - * onUpgrade() method, but is called whenever current version is newer than requested one. + * {@link #onUpgrade} method, but is called whenever current version is newer than requested one. * However, this method is not abstract, so it is not mandatory for a customer to * implement it. If not overridden, default implementation will reject downgrade and * throws SQLiteException * + * <p> + * This method executes within a transaction. If an exception is thrown, all changes + * will automatically be rolled back. + * </p> + * * @param db The database. * @param oldVersion The old database version. * @param newVersion The new database version. @@ -334,6 +366,12 @@ public abstract class SQLiteOpenHelper { * Called when the database has been opened. The implementation * should check {@link SQLiteDatabase#isReadOnly} before updating the * database. + * <p> + * This method is called after the database connection has been configured + * and after the database schema has been created, upgraded or downgraded as necessary. + * If the database connection must be configured in some way before the schema + * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead. + * </p> * * @param db The database. */ |