android support! thanks to TachiWeb devs.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A basic implementation of {@link SupportSQLiteQuery} which receives a query and its args and
|
||||
* binds args based on the passed in Object type.
|
||||
*/
|
||||
public final class SimpleSQLiteQuery implements SupportSQLiteQuery {
|
||||
private final String mQuery;
|
||||
@Nullable
|
||||
private final Object[] mBindArgs;
|
||||
|
||||
/**
|
||||
* Creates an SQL query with the sql string and the bind arguments.
|
||||
*
|
||||
* @param query The query string, can include bind arguments (.e.g ?).
|
||||
* @param bindArgs The bind argument value that will replace the placeholders in the query.
|
||||
*/
|
||||
public SimpleSQLiteQuery(String query, @Nullable Object[] bindArgs) {
|
||||
mQuery = query;
|
||||
mBindArgs = bindArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an SQL query without any bind arguments.
|
||||
*
|
||||
* @param query The SQL query to execute. Cannot include bind parameters.
|
||||
*/
|
||||
public SimpleSQLiteQuery(String query) {
|
||||
this(query, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the given arguments into the given sqlite statement.
|
||||
*
|
||||
* @param statement The sqlite statement
|
||||
* @param bindArgs The list of bind arguments
|
||||
*/
|
||||
public static void bind(SupportSQLiteProgram statement, Object[] bindArgs) {
|
||||
if (bindArgs == null) {
|
||||
return;
|
||||
}
|
||||
final int limit = bindArgs.length;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
final Object arg = bindArgs[i];
|
||||
bind(statement, i + 1, arg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void bind(SupportSQLiteProgram statement, int index, Object arg) {
|
||||
// extracted from android.database.sqlite.SQLiteConnection
|
||||
if (arg == null) {
|
||||
statement.bindNull(index);
|
||||
} else if (arg instanceof byte[]) {
|
||||
statement.bindBlob(index, (byte[]) arg);
|
||||
} else if (arg instanceof Float) {
|
||||
statement.bindDouble(index, (Float) arg);
|
||||
} else if (arg instanceof Double) {
|
||||
statement.bindDouble(index, (Double) arg);
|
||||
} else if (arg instanceof Long) {
|
||||
statement.bindLong(index, (Long) arg);
|
||||
} else if (arg instanceof Integer) {
|
||||
statement.bindLong(index, (Integer) arg);
|
||||
} else if (arg instanceof Short) {
|
||||
statement.bindLong(index, (Short) arg);
|
||||
} else if (arg instanceof Byte) {
|
||||
statement.bindLong(index, (Byte) arg);
|
||||
} else if (arg instanceof String) {
|
||||
statement.bindString(index, (String) arg);
|
||||
} else if (arg instanceof Boolean) {
|
||||
statement.bindLong(index, ((Boolean) arg) ? 1 : 0);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot bind " + arg + " at index " + index
|
||||
+ " Supported types: null, byte[], float, double, long, int, short, byte,"
|
||||
+ " string");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSql() {
|
||||
return mQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindTo(SupportSQLiteProgram statement) {
|
||||
bind(statement, mBindArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArgCount() {
|
||||
return mBindArgs == null ? 0 : mBindArgs.length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* 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.util.Pair;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
package android.arch.persistence.db;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
// TODO Replace with androidx variant once Tachiyomi updates
|
||||
|
||||
/**
|
||||
* An interface to map the behavior of {@link android.database.sqlite.SQLiteOpenHelper}.
|
||||
* Note that since that class requires overriding certain methods, support implementation
|
||||
* uses {@link Factory#create(Configuration)} to create this and {@link Callback} to implement
|
||||
* the methods that should be overridden.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface SupportSQLiteOpenHelper {
|
||||
/**
|
||||
* Return the name of the SQLite database being opened, as given to
|
||||
* the constructor.
|
||||
*/
|
||||
String getDatabaseName();
|
||||
|
||||
/**
|
||||
* Enables or disables the use of write-ahead logging for the database.
|
||||
* <p>
|
||||
* Write-ahead logging cannot be used with read-only databases so the value of
|
||||
* this flag is ignored if the database is opened read-only.
|
||||
*
|
||||
* @param enabled True if write-ahead logging should be enabled, false if it
|
||||
* should be disabled.
|
||||
* @see SupportSQLiteDatabase#enableWriteAheadLogging()
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
void setWriteAheadLoggingEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Create and/or open a database that will be used for reading and writing.
|
||||
* The first time this is called, the database will be opened and
|
||||
* {@link Callback#onCreate}, {@link Callback#onUpgrade} and/or {@link Callback#onOpen} will be
|
||||
* called.
|
||||
*
|
||||
* <p>Once opened successfully, the database is cached, so you can
|
||||
* call this method every time you need to write to the database.
|
||||
* (Make sure to call {@link #close} when you no longer need the database.)
|
||||
* Errors such as bad permissions or a full disk may cause this method
|
||||
* to fail, but future attempts may succeed if the problem is fixed.</p>
|
||||
*
|
||||
* <p class="caution">Database upgrade may take a long time, you
|
||||
* should not call this method from the application main thread, including
|
||||
* from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
|
||||
*
|
||||
* @return a read/write database object valid until {@link #close} is called
|
||||
* @throws SQLiteException if the database cannot be opened for writing
|
||||
*/
|
||||
SupportSQLiteDatabase getWritableDatabase();
|
||||
|
||||
/**
|
||||
* Create and/or open a database. This will be the same object returned by
|
||||
* {@link #getWritableDatabase} unless some problem, such as a full disk,
|
||||
* requires the database to be opened read-only. In that case, a read-only
|
||||
* database object will be returned. If the problem is fixed, a future call
|
||||
* to {@link #getWritableDatabase} may succeed, in which case the read-only
|
||||
* database object will be closed and the read/write object will be returned
|
||||
* in the future.
|
||||
*
|
||||
* <p class="caution">Like {@link #getWritableDatabase}, this method may
|
||||
* take a long time to return, so you should not call it from the
|
||||
* application main thread, including from
|
||||
* {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
|
||||
*
|
||||
* @return a database object valid until {@link #getWritableDatabase}
|
||||
* or {@link #close} is called.
|
||||
* @throws SQLiteException if the database cannot be opened
|
||||
*/
|
||||
SupportSQLiteDatabase getReadableDatabase();
|
||||
|
||||
/**
|
||||
* Close any open database object.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Factory class to create instances of {@link SupportSQLiteOpenHelper} using
|
||||
* {@link Configuration}.
|
||||
*/
|
||||
interface Factory {
|
||||
/**
|
||||
* Creates an instance of {@link SupportSQLiteOpenHelper} using the given configuration.
|
||||
*
|
||||
* @param configuration The configuration to use while creating the open helper.
|
||||
* @return A SupportSQLiteOpenHelper which can be used to open a database.
|
||||
*/
|
||||
SupportSQLiteOpenHelper create(Configuration configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles various lifecycle events for the SQLite connection, similar to
|
||||
* {@link android.database.sqlite.SQLiteOpenHelper}.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||
abstract class Callback {
|
||||
private static final String TAG = "SupportSQLite";
|
||||
/**
|
||||
* Version number of the database (starting at 1); if the database is older,
|
||||
* {@link SupportSQLiteOpenHelper.Callback#onUpgrade(SupportSQLiteDatabase, int, int)}
|
||||
* will be used to upgrade the database; if the database is newer,
|
||||
* {@link SupportSQLiteOpenHelper.Callback#onDowngrade(SupportSQLiteDatabase, int, int)}
|
||||
* will be used to downgrade the database.
|
||||
*/
|
||||
public final int version;
|
||||
|
||||
/**
|
||||
* Creates a new Callback to get database lifecycle events.
|
||||
*
|
||||
* @param version The version for the database instance. See {@link #version}.
|
||||
*/
|
||||
public Callback(int version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 SupportSQLiteDatabase#enableWriteAheadLogging}
|
||||
* {@link SupportSQLiteDatabase#setForeignKeyConstraintsEnabled},
|
||||
* {@link SupportSQLiteDatabase#setLocale},
|
||||
* {@link SupportSQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
|
||||
* </p>
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public void onConfigure(SupportSQLiteDatabase 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.
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public abstract void onCreate(SupportSQLiteDatabase db);
|
||||
|
||||
/**
|
||||
* Called when the database needs to be upgraded. The implementation
|
||||
* 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
|
||||
* <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.
|
||||
* @param newVersion The new database version.
|
||||
*/
|
||||
public abstract void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion);
|
||||
|
||||
/**
|
||||
* Called when the database needs to be downgraded. This is strictly similar to
|
||||
* {@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.
|
||||
*/
|
||||
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
throw new SQLiteException("Can't downgrade database from version "
|
||||
+ oldVersion + " to " + newVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the database has been opened. The implementation
|
||||
* should check {@link SupportSQLiteDatabase#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.
|
||||
*/
|
||||
public void onOpen(SupportSQLiteDatabase db) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The method invoked when database corruption is detected. Default implementation will
|
||||
* delete the database file.
|
||||
*
|
||||
* @param db the {@link SupportSQLiteDatabase} object representing the database on which
|
||||
* corruption is detected.
|
||||
*/
|
||||
public void onCorruption(SupportSQLiteDatabase db) {
|
||||
// the following implementation is taken from {@link DefaultDatabaseErrorHandler}.
|
||||
Log.e(TAG, "Corruption reported by sqlite on database: " + db.getPath());
|
||||
// is the corruption detected even before database could be 'opened'?
|
||||
if (!db.isOpen()) {
|
||||
// database files are not even openable. delete this database file.
|
||||
// NOTE if the database has attached databases, then any of them could be corrupt.
|
||||
// and not deleting all of them could cause corrupted database file to remain and
|
||||
// make the application crash on database open operation. To avoid this problem,
|
||||
// the application should provide its own {@link DatabaseErrorHandler} impl class
|
||||
// to delete ALL files of the database (including the attached databases).
|
||||
deleteDatabaseFile(db.getPath());
|
||||
return;
|
||||
}
|
||||
List<Pair<String, String>> attachedDbs = null;
|
||||
try {
|
||||
// Close the database, which will cause subsequent operations to fail.
|
||||
// before that, get the attached database list first.
|
||||
try {
|
||||
attachedDbs = db.getAttachedDbs();
|
||||
} catch (SQLiteException e) {
|
||||
/* ignore */
|
||||
}
|
||||
try {
|
||||
db.close();
|
||||
} catch (IOException e) {
|
||||
/* ignore */
|
||||
}
|
||||
} finally {
|
||||
// Delete all files of this corrupt database and/or attached databases
|
||||
if (attachedDbs != null) {
|
||||
for (Pair<String, String> p : attachedDbs) {
|
||||
deleteDatabaseFile(p.second);
|
||||
}
|
||||
} else {
|
||||
// attachedDbs = null is possible when the database is so corrupt that even
|
||||
// "PRAGMA database_list;" also fails. delete the main database file
|
||||
deleteDatabaseFile(db.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDatabaseFile(String fileName) {
|
||||
if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
|
||||
return;
|
||||
}
|
||||
Log.w(TAG, "deleting the database file: " + fileName);
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
SQLiteDatabase.deleteDatabase(new File(fileName));
|
||||
} else {
|
||||
try {
|
||||
final boolean deleted = new File(fileName).delete();
|
||||
if (!deleted) {
|
||||
Log.e(TAG, "Could not delete the database file " + fileName);
|
||||
}
|
||||
} catch (Exception error) {
|
||||
Log.e(TAG, "error while deleting corrupted database file", error);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
/* print warning and ignore exception */
|
||||
Log.w(TAG, "delete failed: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration to create an SQLite open helper object using {@link Factory}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
class Configuration {
|
||||
/**
|
||||
* Context to use to open or create the database.
|
||||
*/
|
||||
@NonNull
|
||||
public final Context context;
|
||||
/**
|
||||
* Name of the database file, or null for an in-memory database.
|
||||
*/
|
||||
@Nullable
|
||||
public final String name;
|
||||
/**
|
||||
* The callback class to handle creation, upgrade and downgrade.
|
||||
*/
|
||||
@NonNull
|
||||
public final SupportSQLiteOpenHelper.Callback callback;
|
||||
|
||||
Configuration(@NonNull Context context, @Nullable String name, @NonNull Callback callback) {
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Configuration.Builder to create an instance of Configuration.
|
||||
*
|
||||
* @param context to use to open or create the database.
|
||||
*/
|
||||
public static Builder builder(Context context) {
|
||||
return new Builder(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link Configuration}.
|
||||
*/
|
||||
public static class Builder {
|
||||
Context mContext;
|
||||
String mName;
|
||||
SupportSQLiteOpenHelper.Callback mCallback;
|
||||
|
||||
Builder(@NonNull Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Configuration build() {
|
||||
if (mCallback == null) {
|
||||
throw new IllegalArgumentException("Must set a callback to create the"
|
||||
+ " configuration.");
|
||||
}
|
||||
if (mContext == null) {
|
||||
throw new IllegalArgumentException("Must set a non-null context to create"
|
||||
+ " the configuration.");
|
||||
}
|
||||
return new Configuration(mContext, mName, mCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of the database file, or null for an in-memory database.
|
||||
* @return This
|
||||
*/
|
||||
public Builder name(@Nullable String name) {
|
||||
mName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback The callback class to handle creation, upgrade and downgrade.
|
||||
* @return this
|
||||
*/
|
||||
public Builder callback(@NonNull Callback callback) {
|
||||
mCallback = callback;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 java.io.Closeable;
|
||||
|
||||
/**
|
||||
* An interface to map the behavior of {@link android.database.sqlite.SQLiteProgram}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface SupportSQLiteProgram extends Closeable {
|
||||
/**
|
||||
* Bind a NULL value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind null to
|
||||
*/
|
||||
void bindNull(int index);
|
||||
|
||||
/**
|
||||
* Bind a long value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
* addToBindArgs
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
void bindLong(int index, long value);
|
||||
|
||||
/**
|
||||
* Bind a double value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
void bindDouble(int index, double value);
|
||||
|
||||
/**
|
||||
* Bind a String value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind, must not be null
|
||||
*/
|
||||
void bindString(int index, String value);
|
||||
|
||||
/**
|
||||
* Bind a byte array value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind, must not be null
|
||||
*/
|
||||
void bindBlob(int index, byte[] value);
|
||||
|
||||
/**
|
||||
* Clears all existing bindings. Unset bindings are treated as NULL.
|
||||
*/
|
||||
void clearBindings();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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;
|
||||
|
||||
/**
|
||||
* A query with typed bindings. It is better to use this API instead of
|
||||
* {@link android.database.sqlite.SQLiteDatabase#rawQuery(String, String[])} because it allows
|
||||
* binding type safe parameters.
|
||||
*/
|
||||
public interface SupportSQLiteQuery {
|
||||
/**
|
||||
* The SQL query. This query can have placeholders(?) for bind arguments.
|
||||
*
|
||||
* @return The SQL query to compile
|
||||
*/
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Callback to bind the query parameters to the compiled statement.
|
||||
*
|
||||
* @param statement The compiled statement
|
||||
*/
|
||||
void bindTo(SupportSQLiteProgram statement);
|
||||
|
||||
/**
|
||||
* Returns the number of arguments in this query. This is equal to the number of placeholders
|
||||
* in the query string. See: https://www.sqlite.org/c3ref/bind_blob.html for details.
|
||||
*
|
||||
* @return The number of arguments in the query.
|
||||
*/
|
||||
int getArgCount();
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A simple query builder to create SQL SELECT queries.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class SupportSQLiteQueryBuilder {
|
||||
private static final Pattern sLimitPattern =
|
||||
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
|
||||
private final String mTable;
|
||||
private boolean mDistinct = false;
|
||||
private String[] mColumns = null;
|
||||
private String mSelection;
|
||||
private Object[] mBindArgs;
|
||||
private String mGroupBy = null;
|
||||
private String mHaving = null;
|
||||
private String mOrderBy = null;
|
||||
private String mLimit = null;
|
||||
|
||||
private SupportSQLiteQueryBuilder(String table) {
|
||||
mTable = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query for the given table name.
|
||||
*
|
||||
* @param tableName The table name(s) to query.
|
||||
* @return A builder to create a query.
|
||||
*/
|
||||
public static SupportSQLiteQueryBuilder builder(String tableName) {
|
||||
return new SupportSQLiteQueryBuilder(tableName);
|
||||
}
|
||||
|
||||
private static void appendClause(StringBuilder s, String name, String clause) {
|
||||
if (!isEmpty(clause)) {
|
||||
s.append(name);
|
||||
s.append(clause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the names that are non-null in columns to s, separating
|
||||
* them with commas.
|
||||
*/
|
||||
private static void appendColumns(StringBuilder s, String[] columns) {
|
||||
int n = columns.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
String column = columns[i];
|
||||
if (i > 0) {
|
||||
s.append(", ");
|
||||
}
|
||||
s.append(column);
|
||||
}
|
||||
s.append(' ');
|
||||
}
|
||||
|
||||
private static boolean isEmpty(String input) {
|
||||
return input == null || input.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds DISTINCT keyword to the query.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder distinct() {
|
||||
mDistinct = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given list of columns as the columns that will be returned.
|
||||
*
|
||||
* @param columns The list of column names that should be returned.
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder columns(String[] columns) {
|
||||
mColumns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the arguments for the WHERE clause.
|
||||
*
|
||||
* @param selection The list of selection columns
|
||||
* @param bindArgs The list of bind arguments to match against these columns
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder selection(String selection, Object[] bindArgs) {
|
||||
mSelection = selection;
|
||||
mBindArgs = bindArgs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a GROUP BY statement.
|
||||
*
|
||||
* @param groupBy The value of the GROUP BY statement.
|
||||
* @return this
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public SupportSQLiteQueryBuilder groupBy(String groupBy) {
|
||||
mGroupBy = groupBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a HAVING statement. You must also provide {@link #groupBy(String)} for this to work.
|
||||
*
|
||||
* @param having The having clause.
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder having(String having) {
|
||||
mHaving = having;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an ORDER BY statement.
|
||||
*
|
||||
* @param orderBy The order clause.
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder orderBy(String orderBy) {
|
||||
mOrderBy = orderBy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a LIMIT statement.
|
||||
*
|
||||
* @param limit The limit value.
|
||||
* @return this
|
||||
*/
|
||||
public SupportSQLiteQueryBuilder limit(String limit) {
|
||||
if (!isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
|
||||
throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
|
||||
}
|
||||
mLimit = limit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link SupportSQLiteQuery} that can be passed into
|
||||
* {@link SupportSQLiteDatabase#query(SupportSQLiteQuery)}.
|
||||
*
|
||||
* @return a new query
|
||||
*/
|
||||
public SupportSQLiteQuery create() {
|
||||
if (isEmpty(mGroupBy) && !isEmpty(mHaving)) {
|
||||
throw new IllegalArgumentException(
|
||||
"HAVING clauses are only permitted when using a groupBy clause");
|
||||
}
|
||||
StringBuilder query = new StringBuilder(120);
|
||||
query.append("SELECT ");
|
||||
if (mDistinct) {
|
||||
query.append("DISTINCT ");
|
||||
}
|
||||
if (mColumns != null && mColumns.length != 0) {
|
||||
appendColumns(query, mColumns);
|
||||
} else {
|
||||
query.append(" * ");
|
||||
}
|
||||
query.append(" FROM ");
|
||||
query.append(mTable);
|
||||
appendClause(query, " WHERE ", mSelection);
|
||||
appendClause(query, " GROUP BY ", mGroupBy);
|
||||
appendClause(query, " HAVING ", mHaving);
|
||||
appendClause(query, " ORDER BY ", mOrderBy);
|
||||
appendClause(query, " LIMIT ", mLimit);
|
||||
return new SimpleSQLiteQuery(query.toString(), mBindArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An interface to map the behavior of {@link android.database.sqlite.SQLiteStatement}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface SupportSQLiteStatement extends SupportSQLiteProgram {
|
||||
/**
|
||||
* Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
|
||||
* CREATE / DROP table, view, trigger, index etc.
|
||||
*
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
void execute();
|
||||
|
||||
/**
|
||||
* Execute this SQL statement, if the the number of rows affected by execution of this SQL
|
||||
* statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
|
||||
*
|
||||
* @return the number of rows affected by this SQL statement execution.
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
int executeUpdateDelete();
|
||||
|
||||
/**
|
||||
* Execute this SQL statement and return the ID of the row inserted due to this call.
|
||||
* The SQL statement should be an INSERT for this to be a useful call.
|
||||
*
|
||||
* @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
long executeInsert();
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a numeric value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
long simpleQueryForLong();
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a text value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
String simpleQueryForString();
|
||||
}
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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.framework;
|
||||
|
||||
import android.arch.persistence.db.SimpleSQLiteQuery;
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.arch.persistence.db.SupportSQLiteQuery;
|
||||
import android.arch.persistence.db.SupportSQLiteStatement;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.*;
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
/**
|
||||
* Delegates all calls to an implementation of {@link SQLiteDatabase}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class FrameworkSQLiteDatabase implements SupportSQLiteDatabase {
|
||||
private static final String[] CONFLICT_VALUES = new String[]
|
||||
{"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private final SQLiteDatabase mDelegate;
|
||||
|
||||
/**
|
||||
* Creates a wrapper around {@link SQLiteDatabase}.
|
||||
*
|
||||
* @param delegate The delegate to receive all calls.
|
||||
*/
|
||||
FrameworkSQLiteDatabase(SQLiteDatabase delegate) {
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteStatement compileStatement(String sql) {
|
||||
return new FrameworkSQLiteStatement(mDelegate.compileStatement(sql));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTransaction() {
|
||||
mDelegate.beginTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTransactionNonExclusive() {
|
||||
mDelegate.beginTransactionNonExclusive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
|
||||
mDelegate.beginTransactionWithListener(transactionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginTransactionWithListenerNonExclusive(
|
||||
SQLiteTransactionListener transactionListener) {
|
||||
mDelegate.beginTransactionWithListenerNonExclusive(transactionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
mDelegate.endTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionSuccessful() {
|
||||
mDelegate.setTransactionSuccessful();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inTransaction() {
|
||||
return mDelegate.inTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDbLockedByCurrentThread() {
|
||||
return mDelegate.isDbLockedByCurrentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean yieldIfContendedSafely() {
|
||||
return mDelegate.yieldIfContendedSafely();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
|
||||
return mDelegate.yieldIfContendedSafely(sleepAfterYieldDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return mDelegate.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersion(int version) {
|
||||
mDelegate.setVersion(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximumSize() {
|
||||
return mDelegate.getMaximumSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long setMaximumSize(long numBytes) {
|
||||
return mDelegate.setMaximumSize(numBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPageSize() {
|
||||
return mDelegate.getPageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPageSize(long numBytes) {
|
||||
mDelegate.setPageSize(numBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(String query) {
|
||||
return query(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(String query, Object[] bindArgs) {
|
||||
return query(new SimpleSQLiteQuery(query, bindArgs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(final SupportSQLiteQuery supportQuery) {
|
||||
return mDelegate.rawQueryWithFactory(new SQLiteDatabase.CursorFactory() {
|
||||
@Override
|
||||
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
|
||||
String editTable, SQLiteQuery query) {
|
||||
supportQuery.bindTo(new FrameworkSQLiteProgram(query));
|
||||
return new SQLiteCursor(masterQuery, editTable, query);
|
||||
}
|
||||
}, supportQuery.getSql(), EMPTY_STRING_ARRAY, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
public Cursor query(final SupportSQLiteQuery supportQuery,
|
||||
CancellationSignal cancellationSignal) {
|
||||
return mDelegate.rawQueryWithFactory(new SQLiteDatabase.CursorFactory() {
|
||||
@Override
|
||||
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
|
||||
String editTable, SQLiteQuery query) {
|
||||
supportQuery.bindTo(new FrameworkSQLiteProgram(query));
|
||||
return new SQLiteCursor(masterQuery, editTable, query);
|
||||
}
|
||||
}, supportQuery.getSql(), EMPTY_STRING_ARRAY, null, cancellationSignal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long insert(String table, int conflictAlgorithm, ContentValues values)
|
||||
throws SQLException {
|
||||
return mDelegate.insertWithOnConflict(table, null, values,
|
||||
conflictAlgorithm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(String table, String whereClause, Object[] whereArgs) {
|
||||
String query = "DELETE FROM " + table
|
||||
+ (isEmpty(whereClause) ? "" : " WHERE " + whereClause);
|
||||
SupportSQLiteStatement statement = compileStatement(query);
|
||||
SimpleSQLiteQuery.bind(statement, whereArgs);
|
||||
return statement.executeUpdateDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(String table, int conflictAlgorithm, ContentValues values, String whereClause,
|
||||
Object[] whereArgs) {
|
||||
// taken from SQLiteDatabase class.
|
||||
if (values == null || values.size() == 0) {
|
||||
throw new IllegalArgumentException("Empty values");
|
||||
}
|
||||
StringBuilder sql = new StringBuilder(120);
|
||||
sql.append("UPDATE ");
|
||||
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
|
||||
sql.append(table);
|
||||
sql.append(" SET ");
|
||||
// move all bind args to one array
|
||||
int setValuesSize = values.size();
|
||||
int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
|
||||
Object[] bindArgs = new Object[bindArgsSize];
|
||||
int i = 0;
|
||||
for (String colName : values.keySet()) {
|
||||
sql.append((i > 0) ? "," : "");
|
||||
sql.append(colName);
|
||||
bindArgs[i++] = values.get(colName);
|
||||
sql.append("=?");
|
||||
}
|
||||
if (whereArgs != null) {
|
||||
for (i = setValuesSize; i < bindArgsSize; i++) {
|
||||
bindArgs[i] = whereArgs[i - setValuesSize];
|
||||
}
|
||||
}
|
||||
if (!isEmpty(whereClause)) {
|
||||
sql.append(" WHERE ");
|
||||
sql.append(whereClause);
|
||||
}
|
||||
SupportSQLiteStatement stmt = compileStatement(sql.toString());
|
||||
SimpleSQLiteQuery.bind(stmt, bindArgs);
|
||||
return stmt.executeUpdateDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execSQL(String sql) throws SQLException {
|
||||
mDelegate.execSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
|
||||
mDelegate.execSQL(sql, bindArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return mDelegate.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return mDelegate.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needUpgrade(int newVersion) {
|
||||
return mDelegate.needUpgrade(newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return mDelegate.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocale(Locale locale) {
|
||||
mDelegate.setLocale(locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxSqlCacheSize(int cacheSize) {
|
||||
mDelegate.setMaxSqlCacheSize(cacheSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
||||
mDelegate.setForeignKeyConstraintsEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableWriteAheadLogging() {
|
||||
return mDelegate.enableWriteAheadLogging();
|
||||
}
|
||||
|
||||
@Override
|
||||
@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
public void disableWriteAheadLogging() {
|
||||
mDelegate.disableWriteAheadLogging();
|
||||
}
|
||||
|
||||
@Override
|
||||
@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
public boolean isWriteAheadLoggingEnabled() {
|
||||
return mDelegate.isWriteAheadLoggingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<String, String>> getAttachedDbs() {
|
||||
return mDelegate.getAttachedDbs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDatabaseIntegrityOk() {
|
||||
return mDelegate.isDatabaseIntegrityOk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mDelegate.close();
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.framework;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteDatabase;
|
||||
import android.arch.persistence.db.SupportSQLiteOpenHelper;
|
||||
import android.content.Context;
|
||||
import android.database.DatabaseErrorHandler;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Build;
|
||||
|
||||
class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper {
|
||||
private final OpenHelper mDelegate;
|
||||
|
||||
FrameworkSQLiteOpenHelper(Context context, String name,
|
||||
Callback callback) {
|
||||
mDelegate = createDelegate(context, name, callback);
|
||||
}
|
||||
|
||||
private OpenHelper createDelegate(Context context, String name, Callback callback) {
|
||||
final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
|
||||
return new OpenHelper(context, name, dbRef, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return mDelegate.getDatabaseName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
|
||||
public void setWriteAheadLoggingEnabled(boolean enabled) {
|
||||
mDelegate.setWriteAheadLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteDatabase getWritableDatabase() {
|
||||
return mDelegate.getWritableSupportDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteDatabase getReadableDatabase() {
|
||||
return mDelegate.getReadableSupportDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mDelegate.close();
|
||||
}
|
||||
|
||||
static class OpenHelper extends SQLiteOpenHelper {
|
||||
/**
|
||||
* This is used as an Object reference so that we can access the wrapped database inside
|
||||
* the constructor. SQLiteOpenHelper requires the error handler to be passed in the
|
||||
* constructor.
|
||||
*/
|
||||
final FrameworkSQLiteDatabase[] mDbRef;
|
||||
final Callback mCallback;
|
||||
// see b/78359448
|
||||
private boolean mMigrated;
|
||||
|
||||
OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,
|
||||
final Callback callback) {
|
||||
super(context, name, null, callback.version,
|
||||
new DatabaseErrorHandler() {
|
||||
@Override
|
||||
public void onCorruption(SQLiteDatabase dbObj) {
|
||||
FrameworkSQLiteDatabase db = dbRef[0];
|
||||
if (db != null) {
|
||||
callback.onCorruption(db);
|
||||
}
|
||||
}
|
||||
});
|
||||
mCallback = callback;
|
||||
mDbRef = dbRef;
|
||||
}
|
||||
|
||||
synchronized SupportSQLiteDatabase getWritableSupportDatabase() {
|
||||
mMigrated = false;
|
||||
SQLiteDatabase db = super.getWritableDatabase();
|
||||
if (mMigrated) {
|
||||
// there might be a connection w/ stale structure, we should re-open.
|
||||
close();
|
||||
return getWritableSupportDatabase();
|
||||
}
|
||||
return getWrappedDb(db);
|
||||
}
|
||||
|
||||
synchronized SupportSQLiteDatabase getReadableSupportDatabase() {
|
||||
mMigrated = false;
|
||||
SQLiteDatabase db = super.getReadableDatabase();
|
||||
if (mMigrated) {
|
||||
// there might be a connection w/ stale structure, we should re-open.
|
||||
close();
|
||||
return getReadableSupportDatabase();
|
||||
}
|
||||
return getWrappedDb(db);
|
||||
}
|
||||
|
||||
FrameworkSQLiteDatabase getWrappedDb(SQLiteDatabase sqLiteDatabase) {
|
||||
FrameworkSQLiteDatabase dbRef = mDbRef[0];
|
||||
if (dbRef == null) {
|
||||
dbRef = new FrameworkSQLiteDatabase(sqLiteDatabase);
|
||||
mDbRef[0] = dbRef;
|
||||
}
|
||||
return mDbRef[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
||||
mCallback.onCreate(getWrappedDb(sqLiteDatabase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
|
||||
mMigrated = true;
|
||||
mCallback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigure(SQLiteDatabase db) {
|
||||
mCallback.onConfigure(getWrappedDb(db));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
mMigrated = true;
|
||||
mCallback.onDowngrade(getWrappedDb(db), oldVersion, newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
if (!mMigrated) {
|
||||
// if we've migrated, we'll re-open the db so we should not call the callback.
|
||||
mCallback.onOpen(getWrappedDb(db));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
super.close();
|
||||
mDbRef[0] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.framework;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteOpenHelper;
|
||||
|
||||
/**
|
||||
* Implements {@link SupportSQLiteOpenHelper.Factory} using the SQLite implementation in the
|
||||
* framework.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
|
||||
@Override
|
||||
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
|
||||
return new FrameworkSQLiteOpenHelper(
|
||||
configuration.context, configuration.name, configuration.callback);
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.framework;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteProgram;
|
||||
import android.database.sqlite.SQLiteProgram;
|
||||
|
||||
/**
|
||||
* An wrapper around {@link SQLiteProgram} to implement {@link SupportSQLiteProgram} API.
|
||||
*/
|
||||
class FrameworkSQLiteProgram implements SupportSQLiteProgram {
|
||||
private final SQLiteProgram mDelegate;
|
||||
|
||||
FrameworkSQLiteProgram(SQLiteProgram delegate) {
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(int index) {
|
||||
mDelegate.bindNull(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindLong(int index, long value) {
|
||||
mDelegate.bindLong(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindDouble(int index, double value) {
|
||||
mDelegate.bindDouble(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindString(int index, String value) {
|
||||
mDelegate.bindString(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindBlob(int index, byte[] value) {
|
||||
mDelegate.bindBlob(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBindings() {
|
||||
mDelegate.clearBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mDelegate.close();
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.framework;
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteStatement;
|
||||
import android.database.sqlite.SQLiteStatement;
|
||||
|
||||
/**
|
||||
* Delegates all calls to a {@link SQLiteStatement}.
|
||||
*/
|
||||
class FrameworkSQLiteStatement extends FrameworkSQLiteProgram implements SupportSQLiteStatement {
|
||||
private final SQLiteStatement mDelegate;
|
||||
|
||||
/**
|
||||
* Creates a wrapper around a framework {@link SQLiteStatement}.
|
||||
*
|
||||
* @param delegate The SQLiteStatement to delegate calls to.
|
||||
*/
|
||||
FrameworkSQLiteStatement(SQLiteStatement delegate) {
|
||||
super(delegate);
|
||||
mDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mDelegate.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdateDelete() {
|
||||
return mDelegate.executeUpdateDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long executeInsert() {
|
||||
return mDelegate.executeInsert();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long simpleQueryForLong() {
|
||||
return mDelegate.simpleQueryForLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String simpleQueryForString() {
|
||||
return mDelegate.simpleQueryForString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user