1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package androidx.sqlite.db
17 
18 import android.annotation.SuppressLint
19 import android.content.ContentValues
20 import android.database.Cursor
21 import android.database.SQLException
22 import android.database.sqlite.SQLiteTransactionListener
23 import android.os.CancellationSignal
24 import android.util.Pair
25 import java.io.Closeable
26 import java.util.Locale
27 
28 /**
29  * A database abstraction which removes the framework dependency and allows swapping underlying sql
30  * versions. It mimics the behavior of [android.database.sqlite.SQLiteDatabase]
31  */
32 public interface SupportSQLiteDatabase : Closeable {
33     /**
34      * Compiles the given SQL statement.
35      *
36      * @param sql The sql query.
37      * @return Compiled statement.
38      */
compileStatementnull39     public fun compileStatement(sql: String): SupportSQLiteStatement
40 
41     /**
42      * Begins a transaction in EXCLUSIVE mode.
43      *
44      * Transactions can be nested. When the outer transaction is ended all of the work done in that
45      * transaction and all of the nested transactions will be committed or rolled back. The changes
46      * will be rolled back if any transaction is ended without being marked as clean (by calling
47      * setTransactionSuccessful). Otherwise they will be committed.
48      *
49      * Here is the standard idiom for transactions:
50      * ```
51      *  db.beginTransaction()
52      *  try {
53      *      ...
54      *      db.setTransactionSuccessful()
55      *  } finally {
56      *      db.endTransaction()
57      *  }
58      * ```
59      */
60     public fun beginTransaction()
61 
62     /**
63      * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When the outer
64      * transaction is ended all of the work done in that transaction and all of the nested
65      * transactions will be committed or rolled back. The changes will be rolled back if any
66      * transaction is ended without being marked as clean (by calling setTransactionSuccessful).
67      * Otherwise they will be committed.
68      *
69      * Here is the standard idiom for transactions:
70      * ```
71      *  db.beginTransactionNonExclusive()
72      *  try {
73      *      ...
74      *      db.setTransactionSuccessful()
75      *  } finally {
76      *      db.endTransaction()
77      *  }
78      *  ```
79      */
80     public fun beginTransactionNonExclusive()
81 
82     /**
83      * Begins a transaction in DEFERRED mode, with the android-specific constraint that the
84      * transaction is read-only. The database may not be modified inside a read-only transaction
85      * otherwise a [android.database.sqlite.SQLiteDatabaseLockedException] might be thrown.
86      *
87      * Read-only transactions may run concurrently with other read-only transactions, and if they
88      * database is in WAL mode, they may also run concurrently with IMMEDIATE or EXCLUSIVE
89      * transactions.
90      *
91      * Transactions can be nested. However, the behavior of the transaction is not altered by nested
92      * transactions. A nested transaction may be any of the three transaction types but if the
93      * outermost type is read-only then nested transactions remain read-only, regardless of how they
94      * are started.
95      *
96      * Here is the standard idiom for read-only transactions:
97      * ```
98      *   db.beginTransactionReadOnly();
99      *   try {
100      *     ...
101      *   } finally {
102      *     db.endTransaction();
103      *   }
104      * ```
105      *
106      * If the implementation does not support read-only transactions then the default implementation
107      * delegates to [beginTransaction].
108      */
109     public fun beginTransactionReadOnly() {
110         beginTransaction()
111     }
112 
113     /**
114      * Begins a transaction in EXCLUSIVE mode.
115      *
116      * Transactions can be nested. When the outer transaction is ended all of the work done in that
117      * transaction and all of the nested transactions will be committed or rolled back. The changes
118      * will be rolled back if any transaction is ended without being marked as clean (by calling
119      * setTransactionSuccessful). Otherwise they will be committed.
120      *
121      * Here is the standard idiom for transactions:
122      * ```
123      *  db.beginTransactionWithListener(listener)
124      *  try {
125      *      ...
126      *      db.setTransactionSuccessful()
127      *  } finally {
128      *      db.endTransaction()
129      *  }
130      * ```
131      *
132      * @param transactionListener listener that should be notified when the transaction begins,
133      *   commits, or is rolled back, either explicitly or by a call to [yieldIfContendedSafely].
134      */
beginTransactionWithListenernull135     public fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener)
136 
137     /**
138      * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When the outer
139      * transaction is ended all of the work done in that transaction and all of the nested
140      * transactions will be committed or rolled back. The changes will be rolled back if any
141      * transaction is ended without being marked as clean (by calling setTransactionSuccessful).
142      * Otherwise they will be committed.
143      *
144      * Here is the standard idiom for transactions:
145      * ```
146      *  db.beginTransactionWithListenerNonExclusive(listener)
147      *  try {
148      *      ...
149      *      db.setTransactionSuccessful()
150      *  } finally {
151      *      db.endTransaction()
152      *  }
153      * ```
154      *
155      * @param transactionListener listener that should be notified when the transaction begins,
156      *   commits, or is rolled back, either explicitly or by a call to [yieldIfContendedSafely].
157      */
158     public fun beginTransactionWithListenerNonExclusive(
159         transactionListener: SQLiteTransactionListener
160     )
161 
162     /**
163      * Begins a transaction in read-only mode with a {@link SQLiteTransactionListener} listener. The
164      * database may not be modified inside a read-only transaction otherwise a
165      * [android.database.sqlite.SQLiteDatabaseLockedException] might be thrown.
166      *
167      * Transactions can be nested. However, the behavior of the transaction is not altered by nested
168      * transactions. A nested transaction may be any of the three transaction types but if the
169      * outermost type is read-only then nested transactions remain read-only, regardless of how they
170      * are started.
171      *
172      * Here is the standard idiom for read-only transactions:
173      * ```
174      *   db.beginTransactionWightListenerReadOnly(listener);
175      *   try {
176      *     ...
177      *   } finally {
178      *     db.endTransaction();
179      *   }
180      * ```
181      *
182      * If the implementation does not support read-only transactions then the default implementation
183      * delegates to [beginTransactionWithListener].
184      */
185     @Suppress("ExecutorRegistration")
186     public fun beginTransactionWithListenerReadOnly(
187         transactionListener: SQLiteTransactionListener
188     ) {
189         beginTransactionWithListener(transactionListener)
190     }
191 
192     /**
193      * End a transaction. See beginTransaction for notes about how to use this and when transactions
194      * are committed and rolled back.
195      */
endTransactionnull196     public fun endTransaction()
197 
198     /**
199      * Marks the current transaction as successful. Do not do any more database work between calling
200      * this and calling endTransaction. Do as little non-database work as possible in that situation
201      * too. If any errors are encountered between this and endTransaction the transaction will still
202      * be committed.
203      *
204      * @throws IllegalStateException if the current thread is not in a transaction or the
205      *   transaction is already marked as successful.
206      */
207     public fun setTransactionSuccessful()
208 
209     /**
210      * Returns true if the current thread has a transaction pending.
211      *
212      * @return True if the current thread is in a transaction.
213      */
214     public fun inTransaction(): Boolean
215 
216     /**
217      * True if the current thread is holding an active connection to the database.
218      *
219      * The name of this method comes from a time when having an active connection to the database
220      * meant that the thread was holding an actual lock on the database. Nowadays, there is no
221      * longer a true "database lock" although threads may block if they cannot acquire a database
222      * connection to perform a particular operation.
223      */
224     public val isDbLockedByCurrentThread: Boolean
225 
226     /**
227      * Temporarily end the transaction to let other threads run. The transaction is assumed to be
228      * successful so far. Do not call setTransactionSuccessful before calling this. When this
229      * returns a new transaction will have been created but not marked as successful. This assumes
230      * that there are no nested transactions (beginTransaction has only been called once) and will
231      * throw an exception if that is not the case.
232      *
233      * @return true if the transaction was yielded
234      */
235     public fun yieldIfContendedSafely(): Boolean
236 
237     /**
238      * Temporarily end the transaction to let other threads run. The transaction is assumed to be
239      * successful so far. Do not call setTransactionSuccessful before calling this. When this
240      * returns a new transaction will have been created but not marked as successful. This assumes
241      * that there are no nested transactions (beginTransaction has only been called once) and will
242      * throw an exception if that is not the case.
243      *
244      * @param sleepAfterYieldDelayMillis if > 0, sleep this long before starting a new transaction
245      *   if the lock was actually yielded. This will allow other background threads to make some
246      *   more progress than they would if we started the transaction immediately.
247      * @return true if the transaction was yielded
248      */
249     public fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean
250 
251     /** Is true if [execPerConnectionSQL] is supported by the implementation. */
252     public val isExecPerConnectionSQLSupported: Boolean
253         get() = false
254 
255     /**
256      * Execute the given SQL statement on all connections to this database.
257      *
258      * This statement will be immediately executed on all existing connections, and will be
259      * automatically executed on all future connections.
260      *
261      * Some example usages are changes like `PRAGMA trusted_schema=OFF` or functions like `SELECT
262      * icu_load_collation()`. If you execute these statements using [execSQL] then they will only
263      * apply to a single database connection; using this method will ensure that they are uniformly
264      * applied to all current and future connections.
265      *
266      * An implementation of [SupportSQLiteDatabase] might not support this operation. Use
267      * [isExecPerConnectionSQLSupported] to check if this operation is supported before calling this
268      * method.
269      *
270      * @param sql The SQL statement to be executed. Multiple statements separated by semicolons are
271      *   not supported.
272      * @param bindArgs The arguments that should be bound to the SQL statement.
273      * @throws UnsupportedOperationException if this operation is not supported. To check if it
274      *   supported use [isExecPerConnectionSQLSupported]
275      */
276     public fun execPerConnectionSQL(
277         sql: String,
278         @SuppressLint("ArrayReturn") bindArgs: Array<out Any?>?
279     ) {
280         throw UnsupportedOperationException()
281     }
282 
283     /** The database version. */
284     public var version: Int
285 
286     /** The maximum size the database may grow to. */
287     public val maximumSize: Long
288 
289     /**
290      * Sets the maximum size the database will grow to. The maximum size cannot be set below the
291      * current size.
292      *
293      * @param numBytes the maximum database size, in bytes
294      * @return the new maximum database size
295      */
setMaximumSizenull296     public fun setMaximumSize(numBytes: Long): Long
297 
298     /**
299      * The current database page size, in bytes.
300      *
301      * The page size must be a power of two. This method does not work if any data has been written
302      * to the database file, and must be called right after the database has been created.
303      */
304     public var pageSize: Long
305 
306     /**
307      * Runs the given query on the database. If you would like to have typed bind arguments, use
308      * [query].
309      *
310      * @param query The SQL query that includes the query and can bind into a given compiled
311      *   program.
312      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
313      *   are not synchronized, see the documentation for more details.
314      */
315     public fun query(query: String): Cursor
316 
317     /**
318      * Runs the given query on the database. If you would like to have bind arguments, use [query].
319      *
320      * @param query The SQL query that includes the query and can bind into a given compiled
321      *   program.
322      * @param bindArgs The query arguments to bind.
323      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
324      *   are not synchronized, see the documentation for more details.
325      */
326     public fun query(query: String, bindArgs: Array<out Any?>): Cursor
327 
328     /**
329      * Runs the given query on the database.
330      *
331      * This class allows using type safe sql program bindings while running queries.
332      *
333      * @param query The [SimpleSQLiteQuery] query that includes the query and can bind into a given
334      *   compiled program.
335      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
336      *   are not synchronized, see the documentation for more details.
337      */
338     public fun query(query: SupportSQLiteQuery): Cursor
339 
340     /**
341      * Runs the given query on the database.
342      *
343      * This class allows using type safe sql program bindings while running queries.
344      *
345      * @param query The SQL query that includes the query and can bind into a given compiled
346      *   program.
347      * @param cancellationSignal A signal to cancel the operation in progress, or null if none. If
348      *   the operation is canceled, then [androidx.core.os.OperationCanceledException] will be
349      *   thrown when the query is executed.
350      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
351      *   are not synchronized, see the documentation for more details.
352      */
353     public fun query(query: SupportSQLiteQuery, cancellationSignal: CancellationSignal?): Cursor
354 
355     /**
356      * Convenience method for inserting a row into the database.
357      *
358      * @param table the table to insert the row into
359      * @param values this map contains the initial column values for the row. The keys should be the
360      *   column names and the values the column values
361      * @param conflictAlgorithm for insert conflict resolver. One of
362      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_NONE],
363      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_ROLLBACK],
364      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_ABORT],
365      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL],
366      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE],
367      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE].
368      * @return the row ID of the newly inserted row, or -1 if an error occurred
369      * @throws SQLException If the insert fails
370      */
371     @Throws(SQLException::class)
372     public fun insert(table: String, conflictAlgorithm: Int, values: ContentValues): Long
373 
374     /**
375      * Convenience method for deleting rows in the database.
376      *
377      * @param table the table to delete from
378      * @param whereClause the optional WHERE clause to apply when deleting. Passing null will delete
379      *   all rows.
380      * @param whereArgs You may include ?s in the where clause, which will be replaced by the values
381      *   from whereArgs. The values will be bound as Strings.
382      * @return the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all
383      *   rows and get a count pass "1" as the whereClause.
384      */
385     public fun delete(table: String, whereClause: String?, whereArgs: Array<out Any?>?): Int
386 
387     /**
388      * Convenience method for updating rows in the database.
389      *
390      * @param table the table to update in
391      * @param conflictAlgorithm for update conflict resolver. One of
392      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_NONE],
393      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_ROLLBACK],
394      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_ABORT],
395      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL],
396      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE],
397      *   [android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE].
398      * @param values a map from column names to new column values. null is a valid value that will
399      *   be translated to NULL.
400      * @param whereClause the optional WHERE clause to apply when updating. Passing null will update
401      *   all rows.
402      * @param whereArgs You may include ?s in the where clause, which will be replaced by the values
403      *   from whereArgs. The values will be bound as Strings.
404      * @return the number of rows affected
405      */
406     public fun update(
407         table: String,
408         conflictAlgorithm: Int,
409         values: ContentValues,
410         whereClause: String?,
411         whereArgs: Array<out Any?>?
412     ): Int
413 
414     /**
415      * Execute a single SQL statement that does not return any data.
416      *
417      * When using [enableWriteAheadLogging], journal_mode is automatically managed by this class.
418      * So, do not set journal_mode using "PRAGMA journal_mode" statement if your app is using
419      * [enableWriteAheadLogging]
420      *
421      * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
422      *   not supported.
423      * @throws SQLException if the SQL string is invalid
424      */
425     @Throws(SQLException::class) public fun execSQL(sql: String)
426 
427     /**
428      * Execute a single SQL statement that does not return any data.
429      *
430      * When using [enableWriteAheadLogging], journal_mode is automatically managed by this class.
431      * So, do not set journal_mode using "PRAGMA journal_mode" statement if your app is using
432      * [enableWriteAheadLogging]
433      *
434      * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
435      *   not supported.
436      * @param bindArgs only byte[], String, Long and Double are supported in selectionArgs.
437      * @throws SQLException if the SQL string is invalid
438      */
439     @Throws(SQLException::class) public fun execSQL(sql: String, bindArgs: Array<out Any?>)
440 
441     /** Is true if the database is opened as read only. */
442     public val isReadOnly: Boolean
443 
444     /** Is true if the database is currently open. */
445     public val isOpen: Boolean
446 
447     /**
448      * Returns true if the new version code is greater than the current database version.
449      *
450      * @param newVersion The new version code.
451      * @return True if the new version code is greater than the current database version.
452      */
453     public fun needUpgrade(newVersion: Int): Boolean
454 
455     /** The path to the database file. */
456     public val path: String?
457 
458     /**
459      * Sets the locale for this database. Does nothing if this database has the
460      * [android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS] flag set or was opened read
461      * only.
462      *
463      * @param locale The new locale.
464      * @throws SQLException if the locale could not be set. The most common reason for this is that
465      *   there is no collator available for the locale you requested. In this case the database
466      *   remains unchanged.
467      */
468     public fun setLocale(locale: Locale)
469 
470     /**
471      * Sets the maximum size of the prepared-statement cache for this database. (size of the cache =
472      * number of compiled-sql-statements stored in the cache).
473      *
474      * Maximum cache size can ONLY be increased from its current size (default = 10). If this method
475      * is called with smaller size than the current maximum value, then IllegalStateException is
476      * thrown.
477      *
478      * This method is thread-safe.
479      *
480      * @param cacheSize the size of the cache. can be (0 to
481      *   [android.database.sqlite.SQLiteDatabase.MAX_SQL_CACHE_SIZE])
482      * @throws IllegalStateException if input cacheSize is over the max.
483      *   [android.database.sqlite.SQLiteDatabase.MAX_SQL_CACHE_SIZE].
484      */
485     public fun setMaxSqlCacheSize(cacheSize: Int)
486 
487     /**
488      * Sets whether foreign key constraints are enabled for the database.
489      *
490      * By default, foreign key constraints are not enforced by the database. This method allows an
491      * application to enable foreign key constraints. It must be called each time the database is
492      * opened to ensure that foreign key constraints are enabled for the session.
493      *
494      * A good time to call this method is right after calling `#openOrCreateDatabase` or in the
495      * [SupportSQLiteOpenHelper.Callback.onConfigure] callback.
496      *
497      * When foreign key constraints are disabled, the database does not check whether changes to the
498      * database will violate foreign key constraints. Likewise, when foreign key constraints are
499      * disabled, the database will not execute cascade delete or update triggers. As a result, it is
500      * possible for the database state to become inconsistent. To perform a database integrity
501      * check, call [isDatabaseIntegrityOk].
502      *
503      * This method must not be called while a transaction is in progress.
504      *
505      * See also [SQLite Foreign Key Constraints](http://sqlite.org/foreignkeys.html) for more
506      * details about foreign key constraint support.
507      *
508      * @param enabled True to enable foreign key constraints, false to disable them.
509      * @throws IllegalStateException if the are transactions is in progress when this method is
510      *   called.
511      */
512     public fun setForeignKeyConstraintsEnabled(enabled: Boolean)
513 
514     /**
515      * This method enables parallel execution of queries from multiple threads on the same database.
516      * It does this by opening multiple connections to the database and using a different database
517      * connection for each query. The database journal mode is also changed to enable writes to
518      * proceed concurrently with reads.
519      *
520      * When write-ahead logging is not enabled (the default), it is not possible for reads and
521      * writes to occur on the database at the same time. Before modifying the database, the writer
522      * implicitly acquires an exclusive lock on the database which prevents readers from accessing
523      * the database until the write is completed.
524      *
525      * In contrast, when write-ahead logging is enabled (by calling this method), write operations
526      * occur in a separate log file which allows reads to proceed concurrently. While a write is in
527      * progress, readers on other threads will perceive the state of the database as it was before
528      * the write began. When the write completes, readers on other threads will then perceive the
529      * new state of the database.
530      *
531      * It is a good idea to enable write-ahead logging whenever a database will be concurrently
532      * accessed and modified by multiple threads at the same time. However, write-ahead logging uses
533      * significantly more memory than ordinary journaling because there are multiple connections to
534      * the same database. So if a database will only be used by a single thread, or if optimizing
535      * concurrency is not very important, then write-ahead logging should be disabled.
536      *
537      * After calling this method, execution of queries in parallel is enabled as long as the
538      * database remains open. To disable execution of queries in parallel, either call
539      * [disableWriteAheadLogging] or close the database and reopen it.
540      *
541      * The maximum number of connections used to execute queries in parallel is dependent upon the
542      * device memory and possibly other properties.
543      *
544      * If a query is part of a transaction, then it is executed on the same database handle the
545      * transaction was begun.
546      *
547      * Writers should use [beginTransactionNonExclusive] or
548      * [beginTransactionWithListenerNonExclusive] to start a transaction. Non-exclusive mode allows
549      * database file to be in readable by other threads executing queries.
550      *
551      * If the database has any attached databases, then execution of queries in parallel is NOT
552      * possible. Likewise, write-ahead logging is not supported for read-only databases or memory
553      * databases. In such cases, `enableWriteAheadLogging` returns false.
554      *
555      * The best way to enable write-ahead logging is to pass the
556      * [android.database.sqlite.SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING] flag to
557      * [android.database.sqlite.SQLiteDatabase.openDatabase]. This is more efficient than calling
558      *
559      * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
560      * SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
561      * myDatabaseErrorHandler) db.enableWriteAheadLogging()
562      *
563      * Another way to enable write-ahead logging is to call `enableWriteAheadLogging` after opening
564      * the database.
565      *
566      * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
567      * SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler) db.enableWriteAheadLogging()
568      *
569      * See also [SQLite Write-Ahead Logging](http://sqlite.org/wal.html) for more details about how
570      * write-ahead logging works.
571      *
572      * @return True if write-ahead logging is enabled.
573      * @throws IllegalStateException if there are transactions in progress at the time this method
574      *   is called. WAL mode can only be changed when there are no transactions in progress.
575      */
576     public fun enableWriteAheadLogging(): Boolean
577 
578     /**
579      * This method disables the features enabled by [enableWriteAheadLogging].
580      *
581      * @throws IllegalStateException if there are transactions in progress at the time this method
582      *   is called. WAL mode can only be changed when there are no transactions in progress.
583      */
584     public fun disableWriteAheadLogging()
585 
586     /** Is true if write-ahead logging has been enabled for this database. */
587     public val isWriteAheadLoggingEnabled: Boolean
588 
589     /**
590      * The list of full path names of all attached databases including the main database by
591      * executing 'pragma database_list' on the database.
592      */
593     @get:Suppress("NullableCollection") public val attachedDbs: List<Pair<String, String>>?
594 
595     /**
596      * Is true if the given database (and all its attached databases) pass integrity_check, false
597      * otherwise.
598      */
599     public val isDatabaseIntegrityOk: Boolean
600 }
601