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.driver
18 
19 import androidx.room.TransactionScope
20 import androidx.room.Transactor
21 import androidx.room.coroutines.ConnectionPool
22 import androidx.room.coroutines.RawConnectionAccessor
23 import androidx.sqlite.SQLiteConnection
24 import androidx.sqlite.SQLiteStatement
25 
26 /**
27  * An implementation of a connection pool used in compatibility mode. This impl doesn't do any
28  * connection management since the SupportSQLite* APIs already internally do.
29  */
30 internal class SupportSQLiteConnectionPool(internal val supportDriver: SupportSQLiteDriver) :
31     ConnectionPool {
32     private val supportConnection: SupportSQLitePooledConnection
33         get() {
34             val fileName = supportDriver.openHelper.databaseName ?: ":memory:"
35             return SupportSQLitePooledConnection(supportDriver.open(fileName))
36         }
37 
useConnectionnull38     override suspend fun <R> useConnection(
39         isReadOnly: Boolean,
40         block: suspend (Transactor) -> R
41     ): R {
42         return block.invoke(supportConnection)
43     }
44 
closenull45     override fun close() {
46         supportDriver.openHelper.close()
47     }
48 }
49 
50 private class SupportSQLitePooledConnection(val delegate: SupportSQLiteConnection) :
51     Transactor, RawConnectionAccessor {
52 
53     private var currentTransactionType: Transactor.SQLiteTransactionType? = null
54 
55     override val rawConnection: SQLiteConnection
56         get() = delegate
57 
usePreparednull58     override suspend fun <R> usePrepared(sql: String, block: (SQLiteStatement) -> R): R {
59         return delegate.prepare(sql).use { block.invoke(it) }
60     }
61 
withTransactionnull62     override suspend fun <R> withTransaction(
63         type: Transactor.SQLiteTransactionType,
64         block: suspend TransactionScope<R>.() -> R
65     ): R {
66         return transaction(type, block)
67     }
68 
transactionnull69     private suspend fun <R> transaction(
70         type: Transactor.SQLiteTransactionType,
71         block: suspend TransactionScope<R>.() -> R
72     ): R {
73         val db = delegate.db
74         if (!db.inTransaction()) {
75             currentTransactionType = type
76         }
77         when (type) {
78             Transactor.SQLiteTransactionType.DEFERRED -> db.beginTransactionReadOnly()
79             Transactor.SQLiteTransactionType.IMMEDIATE -> db.beginTransactionNonExclusive()
80             Transactor.SQLiteTransactionType.EXCLUSIVE -> db.beginTransaction()
81         }
82         try {
83             val result = SupportSQLiteTransactor<R>().block()
84             db.setTransactionSuccessful()
85             return result
86         } catch (rollback: ConnectionPool.RollbackException) {
87             @Suppress("UNCHECKED_CAST") return rollback.result as R
88         } finally {
89             db.endTransaction()
90             if (!db.inTransaction()) {
91                 currentTransactionType = null
92             }
93         }
94     }
95 
inTransactionnull96     override suspend fun inTransaction(): Boolean {
97         return delegate.db.inTransaction()
98     }
99 
100     private inner class SupportSQLiteTransactor<T> : TransactionScope<T>, RawConnectionAccessor {
101 
102         override val rawConnection: SQLiteConnection
103             get() = this@SupportSQLitePooledConnection.rawConnection
104 
usePreparednull105         override suspend fun <R> usePrepared(sql: String, block: (SQLiteStatement) -> R): R {
106             return this@SupportSQLitePooledConnection.usePrepared(sql, block)
107         }
108 
withNestedTransactionnull109         override suspend fun <R> withNestedTransaction(
110             block: suspend (TransactionScope<R>) -> R
111         ): R {
112             return transaction(checkNotNull(currentTransactionType), block)
113         }
114 
rollbacknull115         override suspend fun rollback(result: T): Nothing {
116             throw ConnectionPool.RollbackException(result)
117         }
118     }
119 }
120