blob: 5f71baf424a007d4d62b349d31314d21b8c54bc0 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.arch.persistence.db;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteTransactionListener;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.support.annotation.RequiresApi;
import android.util.Pair;
import java.io.Closeable;
import java.util.List;
import java.util.Locale;
/**
* A database abstraction which removes the framework dependency and allows swapping underlying
* sql versions. It mimics the behavior of {@link android.database.sqlite.SQLiteDatabase}
*/
@SuppressWarnings("unused")
public interface SupportSQLiteDatabase extends Closeable {
/**
* Compiles the given SQL statement.
*
* @param sql The sql query.
* @return Compiled statement.
*/
SupportSQLiteStatement compileStatement(String sql);
/**
* Begins a transaction in EXCLUSIVE mode.
* <p>
* Transactions can be nested.
* When the outer transaction is ended all of
* the work done in that transaction and all of the nested transactions will be committed or
* rolled back. The changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
* </p>
* <p>Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransaction();
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*/
void beginTransaction();
/**
* Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
* the outer transaction is ended all of the work done in that transaction
* and all of the nested transactions will be committed or rolled back. The
* changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they
* will be committed.
* <p>
* Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionNonExclusive();
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*/
void beginTransactionNonExclusive();
/**
* Begins a transaction in EXCLUSIVE mode.
* <p>
* Transactions can be nested.
* When the outer transaction is ended all of
* the work done in that transaction and all of the nested transactions will be committed or
* rolled back. The changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
* </p>
* <p>Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionWithListener(listener);
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*
* @param transactionListener listener that should be notified when the transaction begins,
* commits, or is rolled back, either explicitly or by a call to
* {@link #yieldIfContendedSafely}.
*/
void beginTransactionWithListener(SQLiteTransactionListener transactionListener);
/**
* Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
* the outer transaction is ended all of the work done in that transaction
* and all of the nested transactions will be committed or rolled back. The
* changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they
* will be committed.
* <p>
* Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionWithListenerNonExclusive(listener);
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*
* @param transactionListener listener that should be notified when the
* transaction begins, commits, or is rolled back, either
* explicitly or by a call to {@link #yieldIfContendedSafely}.
*/
void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener);
/**
* End a transaction. See beginTransaction for notes about how to use this and when transactions
* are committed and rolled back.
*/
void endTransaction();
/**
* Marks the current transaction as successful. Do not do any more database work between
* calling this and calling endTransaction. Do as little non-database work as possible in that
* situation too. If any errors are encountered between this and endTransaction the transaction
* will still be committed.
*
* @throws IllegalStateException if the current thread is not in a transaction or the
* transaction is already marked as successful.
*/
void setTransactionSuccessful();
/**
* Returns true if the current thread has a transaction pending.
*
* @return True if the current thread is in a transaction.
*/
boolean inTransaction();
/**
* Returns true if the current thread is holding an active connection to the database.
* <p>
* The name of this method comes from a time when having an active connection
* to the database meant that the thread was holding an actual lock on the
* database. Nowadays, there is no longer a true "database lock" although threads
* may block if they cannot acquire a database connection to perform a
* particular operation.
* </p>
*
* @return True if the current thread is holding an active connection to the database.
*/
boolean isDbLockedByCurrentThread();
/**
* Temporarily end the transaction to let other threads run. The transaction is assumed to be
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful. This assumes
* that there are no nested transactions (beginTransaction has only been called once) and will
* throw an exception if that is not the case.
*
* @return true if the transaction was yielded
*/
boolean yieldIfContendedSafely();
/**
* Temporarily end the transaction to let other threads run. The transaction is assumed to be
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful. This assumes
* that there are no nested transactions (beginTransaction has only been called once) and will
* throw an exception if that is not the case.
*
* @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
* the lock was actually yielded. This will allow other background
* threads to make some
* more progress than they would if we started the transaction
* immediately.
* @return true if the transaction was yielded
*/
boolean yieldIfContendedSafely(long sleepAfterYieldDelay);
/**
* Gets the database version.
*
* @return the database version
*/
int getVersion();
/**
* Sets the database version.
*
* @param version the new database version
*/
void setVersion(int version);
/**
* Returns the maximum size the database may grow to.
*
* @return the new maximum database size
*/
long getMaximumSize();
/**
* Sets the maximum size the database will grow to. The maximum size cannot
* be set below the current size.
*
* @param numBytes the maximum database size, in bytes
* @return the new maximum database size
*/
long setMaximumSize(long numBytes);
/**
* Returns the current database page size, in bytes.
*
* @return the database page size, in bytes
*/
long getPageSize();
/**
* Sets the database page size. The page size must be a power of two. This
* method does not work if any data has been written to the database file,
* and must be called right after the database has been created.
*
* @param numBytes the database page size, in bytes
*/
void setPageSize(long numBytes);
/**
* Runs the given query on the database. If you would like to have typed bind arguments,
* use {@link #query(SupportSQLiteQuery)}.
*
* @param query The SQL query that includes the query and can bind into a given compiled
* program.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see #query(SupportSQLiteQuery)
*/
Cursor query(String query);
/**
* Runs the given query on the database. If you would like to have bind arguments,
* use {@link #query(SupportSQLiteQuery)}.
*
* @param query The SQL query that includes the query and can bind into a given compiled
* program.
* @param bindArgs The query arguments to bind.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see #query(SupportSQLiteQuery)
*/
Cursor query(String query, Object[] bindArgs);
/**
* Runs the given query on the database.
* <p>
* This class allows using type safe sql program bindings while running queries.
*
* @param query The SQL query that includes the query and can bind into a given compiled
* program.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
* @see SimpleSQLiteQuery
*/
Cursor query(SupportSQLiteQuery query);
/**
* Runs the given query on the database.
* <p>
* This class allows using type safe sql program bindings while running queries.
*
* @param query The SQL query that includes the query and can bind into a given compiled
* program.
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
* when the query is executed.
* @return A {@link Cursor} object, which is positioned before the first entry. Note that
* {@link Cursor}s are not synchronized, see the documentation for more details.
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
Cursor query(SupportSQLiteQuery query, CancellationSignal cancellationSignal);
/**
* Convenience method for inserting a row into the database.
*
* @param table the table to insert the row into
* @param values this map contains the initial column values for the
* row. The keys should be the column names and the values the
* column values
* @param conflictAlgorithm for insert conflict resolver. One of
* {@link SQLiteDatabase#CONFLICT_NONE}, {@link SQLiteDatabase#CONFLICT_ROLLBACK},
* {@link SQLiteDatabase#CONFLICT_ABORT}, {@link SQLiteDatabase#CONFLICT_FAIL},
* {@link SQLiteDatabase#CONFLICT_IGNORE}, {@link SQLiteDatabase#CONFLICT_REPLACE}.
* @return the row ID of the newly inserted row, or -1 if an error occurred
* @throws SQLException If the insert fails
*/
long insert(String table, int conflictAlgorithm, ContentValues values) throws SQLException;
/**
* Convenience method for deleting rows in the database.
*
* @param table the table to delete from
* @param whereClause the optional WHERE clause to apply when deleting.
* Passing null will delete all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
* will be bound as Strings.
* @return the number of rows affected if a whereClause is passed in, 0
* otherwise. To remove all rows and get a count pass "1" as the
* whereClause.
*/
int delete(String table, String whereClause, Object[] whereArgs);
/**
* Convenience method for updating rows in the database.
*
* @param table the table to update in
* @param conflictAlgorithm for update conflict resolver. One of
* {@link SQLiteDatabase#CONFLICT_NONE}, {@link SQLiteDatabase#CONFLICT_ROLLBACK},
* {@link SQLiteDatabase#CONFLICT_ABORT}, {@link SQLiteDatabase#CONFLICT_FAIL},
* {@link SQLiteDatabase#CONFLICT_IGNORE}, {@link SQLiteDatabase#CONFLICT_REPLACE}.
* @param values a map from column names to new column values. null is a
* valid value that will be translated to NULL.
* @param whereClause the optional WHERE clause to apply when updating.
* Passing null will update all rows.
* @param whereArgs You may include ?s in the where clause, which
* will be replaced by the values from whereArgs. The values
* will be bound as Strings.
* @return the number of rows affected
*/
int update(String table, int conflictAlgorithm,
ContentValues values, String whereClause, Object[] whereArgs);
/**
* Execute a single SQL statement that does not return any data.
* <p>
* When using {@link #enableWriteAheadLogging()}, journal_mode is
* automatically managed by this class. So, do not set journal_mode
* using "PRAGMA journal_mode'<value>" statement if your app is using
* {@link #enableWriteAheadLogging()}
* </p>
*
* @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
* not supported.
* @throws SQLException if the SQL string is invalid
* @see #query(SupportSQLiteQuery)
*/
void execSQL(String sql) throws SQLException;
/**
* Execute a single SQL statement that does not return any data.
* <p>
* When using {@link #enableWriteAheadLogging()}, journal_mode is
* automatically managed by this class. So, do not set journal_mode
* using "PRAGMA journal_mode'<value>" statement if your app is using
* {@link #enableWriteAheadLogging()}
* </p>
*
* @param sql the SQL statement to be executed. Multiple statements separated by semicolons
* are
* not supported.
* @param bindArgs only byte[], String, Long and Double are supported in selectionArgs.
* @throws SQLException if the SQL string is invalid
* @see #query(SupportSQLiteQuery)
*/
void execSQL(String sql, Object[] bindArgs) throws SQLException;
/**
* Returns true if the database is opened as read only.
*
* @return True if database is opened as read only.
*/
boolean isReadOnly();
/**
* Returns true if the database is currently open.
*
* @return True if the database is currently open (has not been closed).
*/
boolean isOpen();
/**
* Returns true if the new version code is greater than the current database version.
*
* @param newVersion The new version code.
* @return True if the new version code is greater than the current database version.
*/
boolean needUpgrade(int newVersion);
/**
* Gets the path to the database file.
*
* @return The path to the database file.
*/
String getPath();
/**
* Sets the locale for this database. Does nothing if this database has
* the {@link SQLiteDatabase#NO_LOCALIZED_COLLATORS} flag set or was opened read only.
*
* @param locale The new locale.
* @throws SQLException if the locale could not be set. The most common reason
* for this is that there is no collator available for the locale you
* requested.
* In this case the database remains unchanged.
*/
void setLocale(Locale locale);
/**
* Sets the maximum size of the prepared-statement cache for this database.
* (size of the cache = number of compiled-sql-statements stored in the cache).
* <p>
* Maximum cache size can ONLY be increased from its current size (default = 10).
* If this method is called with smaller size than the current maximum value,
* then IllegalStateException is thrown.
* <p>
* This method is thread-safe.
*
* @param cacheSize the size of the cache. can be (0 to
* {@link SQLiteDatabase#MAX_SQL_CACHE_SIZE})
* @throws IllegalStateException if input cacheSize gt;
* {@link SQLiteDatabase#MAX_SQL_CACHE_SIZE}.
*/
void setMaxSqlCacheSize(int cacheSize);
/**
* 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 {@code #openOrCreateDatabase}
* or in the {@link SupportSQLiteOpenHelper.Callback#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.
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
void setForeignKeyConstraintsEnabled(boolean enable);
/**
* 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
* journal mode is also changed to enable writes to proceed concurrently with reads.
* <p>
* When write-ahead logging is not enabled (the default), it is not possible for
* reads and writes to occur on the database at the same time. Before modifying the
* database, the writer implicitly acquires an exclusive lock on the database which
* prevents readers from accessing the database until the write is completed.
* </p><p>
* In contrast, when write-ahead logging is enabled (by calling this method), write
* operations occur in a separate log file which allows reads to proceed concurrently.
* While a write is in progress, readers on other threads will perceive the state
* of the database as it was before the write began. When the write completes, readers
* on other threads will then perceive the new state of the database.
* </p><p>
* It is a good idea to enable write-ahead logging whenever a database will be
* concurrently accessed and modified by multiple threads at the same time.
* However, write-ahead logging uses significantly more memory than ordinary
* journaling because there are multiple connections to the same database.
* So if a database will only be used by a single thread, or if optimizing
* concurrency is not very important, then write-ahead logging should be disabled.
* </p><p>
* After calling this method, execution of queries in parallel is enabled as long as
* the database remains open. To disable execution of queries in parallel, either
* call {@link #disableWriteAheadLogging} or close the database and reopen it.
* </p><p>
* The maximum number of connections used to execute queries in parallel is
* dependent upon the device memory and possibly other properties.
* </p><p>
* If a query is part of a transaction, then it is executed on the same database handle the
* transaction was begun.
* </p><p>
* Writers should use {@link #beginTransactionNonExclusive()} or
* {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
* to start a transaction. Non-exclusive mode allows database file to be in readable
* by other threads executing queries.
* </p><p>
* If the database has any attached databases, then execution of queries in parallel is NOT
* possible. Likewise, write-ahead logging is not supported for read-only databases
* or memory databases. In such cases, {@code enableWriteAheadLogging} returns false.
* </p><p>
* The best way to enable write-ahead logging is to pass the
* {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} flag to
* {@link SQLiteDatabase#openDatabase}. This is more efficient than calling
* <code><pre>
* SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
* SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
* myDatabaseErrorHandler);
* db.enableWriteAheadLogging();
* </pre></code>
* </p><p>
* Another way to enable write-ahead logging is to call {@code enableWriteAheadLogging}
* after opening the database.
* <code><pre>
* SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
* SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler);
* db.enableWriteAheadLogging();
* </pre></code>
* </p><p>
* See also <a href="http://sqlite.org/wal.html">SQLite Write-Ahead Logging</a> for
* more details about how write-ahead logging works.
* </p>
*
* @return True if write-ahead logging is enabled.
* @throws 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.
* @see SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING
* @see #disableWriteAheadLogging
*/
boolean enableWriteAheadLogging();
/**
* This method disables the features enabled by {@link #enableWriteAheadLogging()}.
*
* @throws 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.
* @see #enableWriteAheadLogging
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
void disableWriteAheadLogging();
/**
* Returns true if write-ahead logging has been enabled for this database.
*
* @return True if write-ahead logging has been enabled for this database.
* @see #enableWriteAheadLogging
* @see SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
boolean isWriteAheadLoggingEnabled();
/**
* Returns list of full path names of all attached databases including the main database
* by executing 'pragma database_list' on the database.
*
* @return ArrayList of pairs of (database name, database file path) or null if the database
* is not open.
*/
List<Pair<String, String>> getAttachedDbs();
/**
* Runs 'pragma integrity_check' on the given database (and all the attached databases)
* and returns true if the given database (and all its attached databases) pass integrity_check,
* false otherwise.
* <p>
* If the result is false, then this method logs the errors reported by the integrity_check
* command execution.
* <p>
* Note that 'pragma integrity_check' on a database can take a long time.
*
* @return true if the given database (and all its attached databases) pass integrity_check,
* false otherwise.
*/
boolean isDatabaseIntegrityOk();
}