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.coroutines
18 
19 import androidx.room.Transactor
20 import androidx.sqlite.SQLiteConnection
21 import androidx.sqlite.SQLiteDriver
22 import androidx.sqlite.SQLiteException
23 
24 /**
25  * A container that manages [SQLiteConnection]s.
26  *
27  * Connection pools provides the means of bounding and managing the resources that are database
28  * connections. They provide safe usage of connections in a multi-threaded application and usually
29  * provide improved performance when performing asynchronous and concurrent database interactions.
30  *
31  * An implementation instance can be created via the factory function [newConnectionPool] and
32  * [newSingleConnectionPool].
33  */
34 internal interface ConnectionPool : AutoCloseable {
35 
36     /**
37      * Acquires a connection, suspending while waiting if none is available and then calling the
38      * [block] to use the connection once it is acquired. The connection to use in the [block] is an
39      * instance of [Transactor] that provides the capabilities for performing nested transactions.
40      *
41      * Using the connection after [block] completes is prohibited.
42      *
43      * The connection will be confined to the coroutine on which [block] executes, attempting to use
44      * the connection from a different coroutine will result in an error.
45      *
46      * If the current coroutine calling this function already has a confined connection, then that
47      * connection is used as long as it isn't required to be upgraded to a writer. If an upgrade is
48      * required then a [SQLiteException] is thrown.
49      *
50      * If a caller has to wait too long to acquire a connection a [SQLiteException] will be thrown
51      * due to a timeout.
52      *
53      * @param isReadOnly Whether to use a reader or a writer connection.
54      * @param block The code to use the connection.
55      * @throws SQLiteException when the pool is closed or a thread confined connection needs to be
56      *   upgraded or there is a timeout acquiring a connection.
57      */
useConnectionnull58     suspend fun <R> useConnection(isReadOnly: Boolean, block: suspend (Transactor) -> R): R
59 
60     /**
61      * Closes the pool and any opened connections, attempting to use connections is an error once
62      * the pool is closed.
63      */
64     override fun close()
65 
66     /** Internal exception thrown to rollback a transaction. */
67     class RollbackException(val result: Any?) : Throwable()
68 }
69 
70 /**
71  * Creates a new [ConnectionPool] with a single connection.
72  *
73  * A pool containing a single connection that is used for both reading and writing is useful for
74  * in-memory databases whose schema and data are isolated to a database connection.
75  *
76  * @param driver The driver from which to request the connection to be opened.
77  * @param fileName The database file name.
78  * @return The newly created connection pool
79  * @see newConnectionPool
80  */
81 internal fun newSingleConnectionPool(driver: SQLiteDriver, fileName: String): ConnectionPool =
82     ConnectionPoolImpl(driver, fileName)
83 
84 /**
85  * Creates a new [ConnectionPool] with multiple connections separated by readers and writers.
86  *
87  * If the database journal mode is Write-Ahead Logging (WAL) then it is recommended to create a pool
88  * of one writer and multiple readers. If the database journal mode is not WAL (e.g. TRUNCATE,
89  * DELETE or PERSIST) then a single connection pool is recommended.
90  *
91  * @param driver The driver from which to request new connections to be opened.
92  * @param fileName The database file name.
93  * @param maxNumOfReaders The maximum number of connections to be opened and used as readers.
94  * @param maxNumOfWriters The maximum number of connections to be opened and used as writers.
95  * @return The newly created connection pool
96  * @see newSingleConnectionPool
97  */
98 internal fun newConnectionPool(
99     driver: SQLiteDriver,
100     fileName: String,
101     maxNumOfReaders: Int,
102     maxNumOfWriters: Int
103 ): ConnectionPool = ConnectionPoolImpl(driver, fileName, maxNumOfReaders, maxNumOfWriters)
104 
105 /** Defines an object that provides 'raw' access to a connection. */
106 internal interface RawConnectionAccessor {
107     val rawConnection: SQLiteConnection
108 }
109