Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96496adb authored by Jeff Brown's avatar Jeff Brown
Browse files

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
parent 47847f3f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -7358,6 +7358,7 @@ package android.database.sqlite {
    method public static int releaseMemory();
    method public long replace(java.lang.String, java.lang.String, android.content.ContentValues);
    method public long replaceOrThrow(java.lang.String, java.lang.String, android.content.ContentValues) throws android.database.SQLException;
    method public void setForeignKeyConstraintsEnabled(boolean);
    method public void setLocale(java.util.Locale);
    method public deprecated void setLockingEnabled(boolean);
    method public void setMaxSqlCacheSize(int);
@@ -7437,6 +7438,7 @@ package android.database.sqlite {
    method public java.lang.String getDatabaseName();
    method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
    method public android.database.sqlite.SQLiteDatabase getWritableDatabase();
    method public void onConfigure(android.database.sqlite.SQLiteDatabase);
    method public abstract void onCreate(android.database.sqlite.SQLiteDatabase);
    method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
    method public void onOpen(android.database.sqlite.SQLiteDatabase);
+18 −0
Original line number Diff line number Diff line
@@ -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();
+14 −0
Original line number Diff line number Diff line
@@ -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
+47 −0
Original line number Diff line number Diff line
@@ -1792,6 +1792,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
+8 −0
Original line number Diff line number Diff line
@@ -76,6 +76,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.
     */
@@ -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);
    }
Loading