Convert clear database queries to SQLDelight

This commit is contained in:
arkon 2022-06-10 21:33:56 -04:00
parent 349e6ca98f
commit e15a867106
13 changed files with 72 additions and 101 deletions

View file

@ -8,12 +8,12 @@ import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
class ChapterRepositoryImpl( class ChapterRepositoryImpl(
private val databaseHandler: DatabaseHandler, private val handler: DatabaseHandler,
) : ChapterRepository { ) : ChapterRepository {
override suspend fun update(chapterUpdate: ChapterUpdate) { override suspend fun update(chapterUpdate: ChapterUpdate) {
try { try {
databaseHandler.await { handler.await {
chaptersQueries.update( chaptersQueries.update(
chapterUpdate.mangaId, chapterUpdate.mangaId,
chapterUpdate.url, chapterUpdate.url,

View file

@ -8,16 +8,16 @@ import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
class MangaRepositoryImpl( class MangaRepositoryImpl(
private val databaseHandler: DatabaseHandler, private val handler: DatabaseHandler,
) : MangaRepository { ) : MangaRepository {
override fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>> { override fun getFavoritesBySourceId(sourceId: Long): Flow<List<Manga>> {
return databaseHandler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) } return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
} }
override suspend fun resetViewerFlags(): Boolean { override suspend fun resetViewerFlags(): Boolean {
return try { return try {
databaseHandler.await { mangasQueries.resetViewerFlags() } handler.await { mangasQueries.resetViewerFlags() }
true true
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)

View file

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import eu.kanade.tachiyomi.source.Source as LoadedSource
class SourceRepositoryImpl( class SourceRepositoryImpl(
private val sourceManager: SourceManager, private val sourceManager: SourceManager,
@ -29,13 +30,23 @@ class SourceRepositoryImpl(
val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() } val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
return sourceIdWithFavoriteCount.map { sourceIdsWithCount -> return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
sourceIdsWithCount sourceIdsWithCount
.filterNot { it.source == LocalSource.ID }
.map { (sourceId, count) -> .map { (sourceId, count) ->
val source = sourceManager.getOrStub(sourceId).run { val source = sourceManager.getOrStub(sourceId).run {
sourceMapper(this) sourceMapper(this)
} }
source to count source to count
} }
.filterNot { it.first.id == LocalSource.ID } }
}
override fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>> {
val sourceIdWithNonLibraryManga = handler.subscribeToList { mangasQueries.getSourceIdsWithNonLibraryManga() }
return sourceIdWithNonLibraryManga.map { sourceId ->
sourceId.map { (sourceId, count) ->
val source = sourceManager.getOrStub(sourceId)
source to count
}
} }
} }
} }

View file

@ -23,6 +23,7 @@ import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.domain.source.interactor.GetEnabledSources import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetLanguagesWithSources import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSource
@ -58,12 +59,13 @@ class DomainModule : InjektModule {
addFactory { GetExtensionLanguages(get(), get()) } addFactory { GetExtensionLanguages(get(), get()) }
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) } addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
addFactory { GetLanguagesWithSources(get(), get()) }
addFactory { GetEnabledSources(get(), get()) } addFactory { GetEnabledSources(get(), get()) }
addFactory { ToggleSource(get()) } addFactory { GetLanguagesWithSources(get(), get()) }
addFactory { ToggleSourcePin(get()) }
addFactory { GetSourcesWithFavoriteCount(get(), get()) } addFactory { GetSourcesWithFavoriteCount(get(), get()) }
addFactory { GetSourcesWithNonLibraryManga(get()) }
addFactory { SetMigrateSorting(get()) } addFactory { SetMigrateSorting(get()) }
addFactory { ToggleLanguage(get()) } addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) }
addFactory { ToggleSourcePin(get()) }
} }
} }

View file

@ -0,0 +1,14 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.tachiyomi.source.Source
import kotlinx.coroutines.flow.Flow
class GetSourcesWithNonLibraryManga(
private val repository: SourceRepository,
) {
fun subscribe(): Flow<List<Pair<Source, Long>>> {
return repository.getSourcesWithNonLibraryManga()
}
}

View file

@ -2,6 +2,7 @@ package eu.kanade.domain.source.repository
import eu.kanade.domain.source.model.Source import eu.kanade.domain.source.model.Source
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import eu.kanade.tachiyomi.source.Source as LoadedSource
interface SourceRepository { interface SourceRepository {
@ -10,4 +11,6 @@ interface SourceRepository {
fun getOnlineSources(): Flow<List<Source>> fun getOnlineSources(): Flow<List<Source>>
fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>> fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>>
} }

View file

@ -1,3 +0,0 @@
package eu.kanade.tachiyomi.data.database.models
data class SourceIdMangaCount(val source: Long, val count: Int)

View file

@ -1,20 +1,16 @@
package eu.kanade.tachiyomi.data.database.queries package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.Queries
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaCoverLastModifiedPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaCoverLastModifiedPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
import eu.kanade.tachiyomi.data.database.tables.CategoryTable import eu.kanade.tachiyomi.data.database.tables.CategoryTable
import eu.kanade.tachiyomi.data.database.tables.ChapterTable import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
@ -86,17 +82,6 @@ interface MangaQueries : DbProvider {
) )
.prepare() .prepare()
fun getSourceIdsWithNonLibraryManga() = db.get()
.listOfObjects(SourceIdMangaCount::class.java)
.withQuery(
RawQuery.builder()
.query(getSourceIdsWithNonLibraryMangaQuery())
.observesTables(MangaTable.TABLE)
.build(),
)
.withGetResolver(SourceIdMangaCountGetResolver.INSTANCE)
.prepare()
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare() fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare() fun insertMangas(mangas: List<Manga>) = db.put().objects(mangas).prepare()
@ -131,16 +116,6 @@ interface MangaQueries : DbProvider {
.withPutResolver(MangaCoverLastModifiedPutResolver()) .withPutResolver(MangaCoverLastModifiedPutResolver())
.prepare() .prepare()
fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ? AND ${MangaTable.COL_SOURCE} IN (${Queries.placeholders(sourceIds.size)})")
.whereArgs(0, *sourceIds.toTypedArray())
.build(),
)
.prepare()
fun getLastReadManga() = db.get() fun getLastReadManga() = db.get()
.listOfObjects(Manga::class.java) .listOfObjects(Manga::class.java)
.withQuery( .withQuery(

View file

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.database.queries package eu.kanade.tachiyomi.data.database.queries
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History
@ -113,14 +112,3 @@ fun getCategoriesForMangaQuery() =
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID} ${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
WHERE ${MangaCategory.COL_MANGA_ID} = ? WHERE ${MangaCategory.COL_MANGA_ID} = ?
""" """
/** Query to get the list of sources in the database that have
* non-library manga, and how many
*/
fun getSourceIdsWithNonLibraryMangaQuery() =
"""
SELECT ${Manga.COL_SOURCE}, COUNT(*) as ${SourceIdMangaCountGetResolver.COL_COUNT}
FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 0
GROUP BY ${Manga.COL_SOURCE}
"""

View file

@ -1,23 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.annotation.SuppressLint
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
import eu.kanade.tachiyomi.data.database.tables.MangaTable
class SourceIdMangaCountGetResolver : DefaultGetResolver<SourceIdMangaCount>() {
companion object {
val INSTANCE = SourceIdMangaCountGetResolver()
const val COL_COUNT = "manga_count"
}
@SuppressLint("Range")
override fun mapFromCursor(cursor: Cursor): SourceIdMangaCount {
val sourceID = cursor.getLong(cursor.getColumnIndexOrThrow(MangaTable.COL_SOURCE))
val count = cursor.getInt(cursor.getColumnIndexOrThrow(COL_COUNT))
return SourceIdMangaCount(sourceID, count)
}
}

View file

@ -1,43 +1,37 @@
package eu.kanade.tachiyomi.ui.setting.database package eu.kanade.tachiyomi.ui.setting.database
import android.os.Bundle import android.os.Bundle
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
import eu.kanade.tachiyomi.Database import eu.kanade.tachiyomi.Database
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import rx.Observable import eu.kanade.tachiyomi.util.lang.launchIO
import rx.android.schedulers.AndroidSchedulers import eu.kanade.tachiyomi.util.lang.withUIContext
import rx.schedulers.Schedulers import kotlinx.coroutines.flow.collectLatest
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class ClearDatabasePresenter : BasePresenter<ClearDatabaseController>() { class ClearDatabasePresenter(
private val database: Database = Injekt.get(),
private val db = Injekt.get<DatabaseHelper>() private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get(),
private val database = Injekt.get<Database>() ) : BasePresenter<ClearDatabaseController>() {
private val sourceManager = Injekt.get<SourceManager>()
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
getDatabaseSourcesObservable()
.subscribeOn(Schedulers.io()) presenterScope.launchIO {
.observeOn(AndroidSchedulers.mainThread()) getSourcesWithNonLibraryManga.subscribe()
.subscribeLatestCache(ClearDatabaseController::setItems) .collectLatest { list ->
val items = list
.map { (source, count) -> ClearDatabaseSourceItem(source, count) }
.sortedBy { it.source.name }
withUIContext { view?.setItems(items) }
}
}
} }
fun clearDatabaseForSourceIds(sources: List<Long>) { fun clearDatabaseForSourceIds(sources: List<Long>) {
db.deleteMangasNotInLibraryBySourceIds(sources).executeAsBlocking() database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sources)
database.historyQueries.removeResettedHistory() database.historyQueries.removeResettedHistory()
} }
private fun getDatabaseSourcesObservable(): Observable<List<ClearDatabaseSourceItem>> {
return db.getSourceIdsWithNonLibraryManga().asRxObservable()
.map { sourceCounts ->
sourceCounts.map {
val sourceObj = sourceManager.getOrStub(it.source)
ClearDatabaseSourceItem(sourceObj, it.count)
}.sortedBy { it.source.name }
}
}
} }

View file

@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.icon
data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: Int) : AbstractFlexibleItem<ClearDatabaseSourceItem.Holder>() { data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: Long) : AbstractFlexibleItem<ClearDatabaseSourceItem.Holder>() {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.clear_database_source_item return R.layout.clear_database_source_item
@ -31,7 +31,7 @@ data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: I
private val binding = ClearDatabaseSourceItemBinding.bind(view) private val binding = ClearDatabaseSourceItemBinding.bind(view)
fun bind(source: Source, count: Int) { fun bind(source: Source, count: Long) {
binding.title.text = source.toString() binding.title.text = source.toString()
binding.description.text = itemView.context.getString(R.string.clear_database_source_item_count, count) binding.description.text = itemView.context.getString(R.string.clear_database_source_item_count, count)

View file

@ -46,4 +46,14 @@ AND source = :sourceId;
resetViewerFlags: resetViewerFlags:
UPDATE mangas UPDATE mangas
SET viewer = 0; SET viewer = 0;
getSourceIdsWithNonLibraryManga:
SELECT source, COUNT(*) AS manga_count
FROM mangas
WHERE favorite = 0
GROUP BY source;
deleteMangasNotInLibraryBySourceIds:
DELETE FROM mangas
WHERE favorite = 0 AND source IN :sourceIds;