package eu.kanade.data import androidx.paging.PagingSource import com.squareup.sqldelight.Query import com.squareup.sqldelight.Transacter import com.squareup.sqldelight.android.paging3.QueryPagingSource import com.squareup.sqldelight.db.SqlDriver import com.squareup.sqldelight.runtime.coroutines.asFlow import com.squareup.sqldelight.runtime.coroutines.mapToList import com.squareup.sqldelight.runtime.coroutines.mapToOne import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull import eu.kanade.tachiyomi.Database import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext class AndroidDatabaseHandler( val db: Database, private val driver: SqlDriver, val queryDispatcher: CoroutineDispatcher = Dispatchers.IO, val transactionDispatcher: CoroutineDispatcher = queryDispatcher ) : DatabaseHandler { val suspendingTransactionId = ThreadLocal() override suspend fun await(inTransaction: Boolean, block: suspend Database.() -> T): T { return dispatch(inTransaction, block) } override suspend fun awaitList( inTransaction: Boolean, block: suspend Database.() -> Query ): List { return dispatch(inTransaction) { block(db).executeAsList() } } override suspend fun awaitOne( inTransaction: Boolean, block: suspend Database.() -> Query ): T { return dispatch(inTransaction) { block(db).executeAsOne() } } override suspend fun awaitOneOrNull( inTransaction: Boolean, block: suspend Database.() -> Query ): T? { return dispatch(inTransaction) { block(db).executeAsOneOrNull() } } override fun subscribeToList(block: Database.() -> Query): Flow> { return block(db).asFlow().mapToList(queryDispatcher) } override fun subscribeToOne(block: Database.() -> Query): Flow { return block(db).asFlow().mapToOne(queryDispatcher) } override fun subscribeToOneOrNull(block: Database.() -> Query): Flow { return block(db).asFlow().mapToOneOrNull(queryDispatcher) } override fun subscribeToPagingSource( countQuery: Database.() -> Query, transacter: Database.() -> Transacter, queryProvider: Database.(Long, Long) -> Query ): PagingSource { return QueryPagingSource( countQuery = countQuery(db), transacter = transacter(db), dispatcher = queryDispatcher, queryProvider = { limit, offset -> queryProvider.invoke(db, limit, offset) } ) } private suspend fun dispatch(inTransaction: Boolean, block: suspend Database.() -> T): T { // Create a transaction if needed and run the calling block inside it. if (inTransaction) { return withTransaction { block(db) } } // If we're currently in the transaction thread, there's no need to dispatch our query. if (driver.currentTransaction() != null) { return block(db) } // Get the current database context and run the calling block. val context = getCurrentDatabaseContext() return withContext(context) { block(db) } } }