1 /* 2 * Copyright 2024 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 17 package androidx.room 18 19 import androidx.room.Transactor.SQLiteTransactionType 20 import androidx.sqlite.SQLiteConnection 21 import androidx.sqlite.SQLiteStatement 22 23 /** 24 * A wrapper of [SQLiteConnection] that belongs to a connection pool and is safe to use in a 25 * coroutine. 26 */ 27 interface PooledConnection { 28 /** 29 * Prepares a new SQL statement and use it within the code [block]. 30 * 31 * Using the given [SQLiteStatement] after [block] completes is prohibited. The statement will 32 * also be thread confined, attempting to use it from another thread is an error. 33 * 34 * Using a statement locks the connection it belongs to, therefore try not to do long-running 35 * computations within the [block]. 36 * 37 * @param sql The SQL statement to prepare 38 * @param block The code to use the statement 39 */ 40 // TODO(b/319653917): Revisit shareable / caching APIs usePreparednull41 suspend fun <R> usePrepared(sql: String, block: (SQLiteStatement) -> R): R 42 } 43 44 /** Executes a single SQL statement that returns no values. */ 45 suspend fun PooledConnection.execSQL(sql: String) { 46 usePrepared(sql) { it.step() } 47 } 48 49 /** A [PooledConnection] that can perform transactions. */ 50 interface Transactor : PooledConnection { 51 52 /** 53 * Begins a transaction and runs the [block] within the transaction. If [block] fails to 54 * complete normally i.e., an exception is thrown, or [TransactionScope.rollback] is invoked 55 * then the transaction will be rollback, otherwise it is committed. 56 * 57 * If [inTransaction] returns `true` and this function is invoked it is the equivalent of 58 * starting a nested transaction as if [TransactionScope.withNestedTransaction] was invoked and 59 * the [type] of the transaction will be ignored since its type will be inherited from the 60 * parent transaction. 61 * 62 * See also [Transaction](https://www.sqlite.org/lang_transaction.html) 63 * 64 * @param type The type of transaction to begin. 65 * @param block The code that will execute within the transaction. 66 */ withTransactionnull67 suspend fun <R> withTransaction( 68 type: SQLiteTransactionType, 69 block: suspend TransactionScope<R>.() -> R 70 ): R 71 72 /** Returns true if this connection has an active transaction, otherwise false. */ 73 suspend fun inTransaction(): Boolean 74 75 /** 76 * Transaction types. 77 * 78 * @see Transactor.withTransaction 79 */ 80 enum class SQLiteTransactionType { 81 /** 82 * The transaction mode that does not start the actual transaction until the database is 83 * accessed, may it be a read or a write. 84 */ 85 DEFERRED, 86 /** The transaction mode that immediately starts a write transaction. */ 87 IMMEDIATE, 88 /** 89 * The transaction mode that immediately starts a write transaction and locks the database 90 * preventing others from accessing it. 91 */ 92 EXCLUSIVE, 93 } 94 } 95 96 /** 97 * A [PooledConnection] with an active transaction capable of performing nested transactions. 98 * 99 * @see Transactor 100 */ 101 interface TransactionScope<T> : PooledConnection { 102 103 /** 104 * Begins a nested transaction and runs the [block] within the transaction. If [block] fails to 105 * complete normally i.e., an exception is thrown, or [rollback] is invoked then the transaction 106 * will be rollback, otherwise it is committed. 107 * 108 * Note that a nested transaction is still governed by its parent transaction and it too must 109 * complete successfully for all its children transactions to be committed. 110 * 111 * See also [Savepoint](https://www.sqlite.org/lang_savepoint.html) 112 * 113 * @param block The code that will execute within the transaction. 114 */ withNestedTransactionnull115 suspend fun <R> withNestedTransaction(block: suspend TransactionScope<R>.() -> R): R 116 117 /** 118 * Rollback the transaction, completing it and returning the [result]. 119 * 120 * @see Transactor.withTransaction 121 * @see TransactionScope.withNestedTransaction 122 */ 123 suspend fun rollback(result: T): Nothing 124 } 125 126 /** Performs a [SQLiteTransactionType.DEFERRED] within the [block]. */ 127 suspend fun <R> Transactor.deferredTransaction(block: suspend TransactionScope<R>.() -> R): R = 128 withTransaction(SQLiteTransactionType.DEFERRED, block) 129 130 /** Performs a [SQLiteTransactionType.IMMEDIATE] within the [block]. */ 131 suspend fun <R> Transactor.immediateTransaction(block: suspend TransactionScope<R>.() -> R): R = 132 withTransaction(SQLiteTransactionType.IMMEDIATE, block) 133 134 /** Performs a [SQLiteTransactionType.EXCLUSIVE] within the [block]. */ 135 suspend fun <R> Transactor.exclusiveTransaction(block: suspend TransactionScope<R>.() -> R): R = 136 withTransaction(SQLiteTransactionType.EXCLUSIVE, block) 137