Skip to content

Commit fe4429b

Browse files
committed
Simplify feed queries
1 parent cdaef8c commit fe4429b

4 files changed

Lines changed: 60 additions & 101 deletions

File tree

app/src/main/kotlin/org/vestifeed/db/Database.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import org.vestifeed.db.table.ConfQueries
66
import org.vestifeed.db.table.ConfSchema
77
import org.vestifeed.db.table.EntryQueries
88
import org.vestifeed.db.table.EntrySchema
9+
import org.vestifeed.db.table.FEED_SCHEMA
910
import org.vestifeed.db.table.FeedQueries
10-
import org.vestifeed.db.table.FeedSchema
1111

1212
class Database(driver: SQLiteDriver, val path: String) {
1313

@@ -26,7 +26,7 @@ class Database(driver: SQLiteDriver, val path: String) {
2626
val version = if (stmt.step()) stmt.getInt(0) else 0
2727

2828
if (version == 0) {
29-
conn.execSQL(FeedSchema.toString())
29+
conn.execSQL(FEED_SCHEMA)
3030
conn.execSQL(EntrySchema.toString())
3131
conn.execSQL(ConfSchema.toString())
3232
conn.execSQL("PRAGMA user_version=1;")

app/src/main/kotlin/org/vestifeed/db/SQLiteStatementExt.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ fun SQLiteStatement.bindTextOrNull(index: Int, value: String?) {
1010
if (value == null) bindNull(index) else bindText(index, value)
1111
}
1212

13+
fun SQLiteStatement.bindBooleanOrNull(index: Int, value: Boolean?) {
14+
if (value == null) bindNull(index) else bindInt(index, if (value) 1 else 0)
15+
}
16+
1317
fun SQLiteStatement.bindLongOrNull(index: Int, value: Long?) {
1418
if (value == null) bindNull(index) else bindLong(index, value)
1519
}
@@ -29,6 +33,9 @@ fun SQLiteStatement.bindJsonObjectOrNull(index: Int, value: JsonObject?) {
2933
fun SQLiteStatement.getTextOrNull(index: Int): String? =
3034
if (isNull(index)) null else getText(index)
3135

36+
fun SQLiteStatement.getBoolOrNull(index: Int): Boolean? =
37+
if (isNull(index)) null else getInt(index) != 0
38+
3239
fun SQLiteStatement.getLongOrNull(index: Int): Long? =
3340
if (isNull(index)) null else getLong(index)
3441

app/src/main/kotlin/org/vestifeed/db/table/Feed.kt

Lines changed: 50 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,23 @@
11
package org.vestifeed.db.table
22

33
import androidx.sqlite.SQLiteConnection
4-
import androidx.sqlite.SQLiteStatement
54
import androidx.sqlite.execSQL
65
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
6+
import org.vestifeed.db.bindBooleanOrNull
7+
import org.vestifeed.db.getBoolOrNull
78
import org.vestifeed.parser.AtomLinkRel
89
import kotlin.collections.forEach
910

10-
object FeedSchema {
11-
const val TABLE_NAME = "feed"
12-
13-
override fun toString(): String {
14-
return """
15-
CREATE TABLE $TABLE_NAME (
16-
${Columns.Id} TEXT PRIMARY KEY NOT NULL,
17-
${Columns.Links} TEXT,
18-
${Columns.Title} TEXT NOT NULL,
19-
${Columns.ExtOpenEntriesInBrowser} INTEGER,
20-
${Columns.ExtBlockedWords} TEXT,
21-
${Columns.ExtShowPreviewImages} INTEGER
22-
);
23-
""".trimIndent()
24-
}
25-
26-
enum class Columns(val sqlName: String) {
27-
Id("id"),
28-
Links("links"),
29-
Title("title"),
30-
ExtOpenEntriesInBrowser("ext_open_entries_in_browser"),
31-
ExtBlockedWords("ext_blocked_words"),
32-
ExtShowPreviewImages("ext_show_preview_images");
33-
34-
override fun toString() = sqlName
35-
}
36-
}
11+
const val FEED_SCHEMA = """
12+
CREATE TABLE feed (
13+
id TEXT PRIMARY KEY NOT NULL,
14+
links TEXT,
15+
title TEXT NOT NULL,
16+
ext_open_entries_in_browser INTEGER,
17+
ext_blocked_words TEXT,
18+
ext_show_preview_images INTEGER
19+
);
20+
"""
3721

3822
typealias Feed = FeedProjection
3923

@@ -44,25 +28,7 @@ data class FeedProjection(
4428
val extOpenEntriesInBrowser: Boolean?,
4529
val extBlockedWords: String,
4630
val extShowPreviewImages: Boolean?,
47-
) {
48-
companion object {
49-
val columns: String
50-
get() {
51-
return FeedSchema.Columns.entries.joinToString(",") { it.sqlName }
52-
}
53-
54-
fun fromStatement(stmt: SQLiteStatement): FeedProjection {
55-
return FeedProjection(
56-
id = stmt.getText(0),
57-
links = jsonToLinks(stmt.getText(1)),
58-
title = stmt.getText(2),
59-
extOpenEntriesInBrowser = if (stmt.isNull(3)) null else stmt.getInt(3) == 1,
60-
extBlockedWords = stmt.getText(4),
61-
extShowPreviewImages = if (stmt.isNull(5)) null else stmt.getInt(5) == 1
62-
)
63-
}
64-
}
65-
}
31+
)
6632

6733
class FeedQueries(private val conn: SQLiteConnection) {
6834
fun insertOrReplace(feeds: List<Feed>) {
@@ -73,25 +39,17 @@ class FeedQueries(private val conn: SQLiteConnection) {
7339
conn.prepare(
7440
"""
7541
INSERT OR REPLACE
76-
INTO ${FeedSchema.TABLE_NAME} (${FeedProjection.columns})
42+
INTO feed (id, links, title, ext_open_entries_in_browser, ext_blocked_words, ext_show_preview_images)
7743
VALUES (?, ?, ?, ?, ?, ?);
7844
"""
7945
).use { stmt ->
8046
feeds.forEach { feed ->
8147
stmt.bindText(1, feed.id)
8248
stmt.bindText(2, linksToJson(feed.links))
8349
stmt.bindText(3, feed.title)
84-
if (feed.extOpenEntriesInBrowser != null) {
85-
stmt.bindInt(4, if (feed.extOpenEntriesInBrowser) 1 else 0)
86-
} else {
87-
stmt.bindNull(4)
88-
}
50+
stmt.bindBooleanOrNull(4, feed.extOpenEntriesInBrowser)
8951
stmt.bindText(5, feed.extBlockedWords)
90-
if (feed.extShowPreviewImages != null) {
91-
stmt.bindInt(6, if (feed.extShowPreviewImages) 1 else 0)
92-
} else {
93-
stmt.bindNull(6)
94-
}
52+
stmt.bindBooleanOrNull(6, feed.extShowPreviewImages)
9553
stmt.step()
9654
stmt.reset()
9755
}
@@ -101,14 +59,23 @@ class FeedQueries(private val conn: SQLiteConnection) {
10159
fun selectAll(): List<FeedProjection> {
10260
conn.prepare(
10361
"""
104-
SELECT ${FeedProjection.columns}
105-
FROM ${FeedSchema.TABLE_NAME}
106-
ORDER BY ${FeedSchema.Columns.Title};
62+
SELECT id, links, title, ext_open_entries_in_browser, ext_blocked_words, ext_show_preview_images
63+
FROM feed
64+
ORDER BY title;
10765
"""
10866
).use { stmt ->
10967
return buildList {
11068
while (stmt.step()) {
111-
add(FeedProjection.fromStatement(stmt))
69+
add(
70+
FeedProjection(
71+
id = stmt.getText(0),
72+
links = jsonToLinks(stmt.getText(1)),
73+
title = stmt.getText(2),
74+
extOpenEntriesInBrowser = stmt.getBoolOrNull(3),
75+
extBlockedWords = stmt.getText(4),
76+
extShowPreviewImages = stmt.getBoolOrNull(5),
77+
)
78+
)
11279
}
11380
}
11481
}
@@ -128,10 +95,10 @@ class FeedQueries(private val conn: SQLiteConnection) {
12895
conn.prepare(
12996
"""
13097
SELECT f.*, COUNT(e.id) as unread_entries
131-
FROM ${FeedSchema.TABLE_NAME} f
132-
LEFT JOIN entry e ON e.feed_id = f.${FeedSchema.Columns.Id} AND e.ext_read = 0 AND e.ext_bookmarked = 0
133-
GROUP BY f.${FeedSchema.Columns.Id}
134-
ORDER BY f.${FeedSchema.Columns.Title};
98+
FROM feed f
99+
LEFT JOIN entry e ON e.feed_id = f.id AND e.ext_read = 0 AND e.ext_bookmarked = 0
100+
GROUP BY f.id
101+
ORDER BY f.title;
135102
"""
136103
).use { stmt ->
137104
return buildList {
@@ -155,8 +122,8 @@ class FeedQueries(private val conn: SQLiteConnection) {
155122
fun selectAllLinks(): List<List<Link>> {
156123
conn.prepare(
157124
"""
158-
SELECT ${FeedSchema.Columns.Links}
159-
FROM ${FeedSchema.TABLE_NAME};
125+
SELECT links
126+
FROM feed;
160127
"""
161128
).use { stmt ->
162129
return buildList {
@@ -170,21 +137,28 @@ class FeedQueries(private val conn: SQLiteConnection) {
170137
fun selectById(id: String): FeedProjection? {
171138
conn.prepare(
172139
"""
173-
SELECT ${FeedProjection.columns}
174-
FROM ${FeedSchema.TABLE_NAME}
140+
SELECT id, links, title, ext_open_entries_in_browser, ext_blocked_words, ext_show_preview_images
141+
FROM feed
175142
WHERE id = ?;
176143
"""
177144
).use { stmt ->
178145
stmt.bindText(1, id)
179-
return if (stmt.step()) FeedProjection.fromStatement(stmt) else null
146+
return if (stmt.step()) FeedProjection(
147+
id = stmt.getText(0),
148+
links = jsonToLinks(stmt.getText(1)),
149+
title = stmt.getText(2),
150+
extOpenEntriesInBrowser = stmt.getBoolOrNull(3),
151+
extBlockedWords = stmt.getText(4),
152+
extShowPreviewImages = stmt.getBoolOrNull(5),
153+
) else null
180154
}
181155
}
182156

183157
fun deleteById(id: String) {
184158
conn.prepare(
185159
"""
186-
DELETE FROM ${FeedSchema.TABLE_NAME}
187-
WHERE ${FeedSchema.Columns.Id} = ?;
160+
DELETE FROM feed
161+
WHERE id = ?;
188162
"""
189163
).use { stmt ->
190164
stmt.bindText(1, id)
@@ -193,7 +167,7 @@ class FeedQueries(private val conn: SQLiteConnection) {
193167
}
194168

195169
fun deleteAll() {
196-
conn.execSQL("DELETE FROM ${FeedSchema.TABLE_NAME};")
170+
conn.execSQL("DELETE FROM feed;")
197171
}
198172
}
199173

@@ -237,7 +211,8 @@ private fun jsonToLinks(json: String?): List<Link> {
237211
hreflang = obj.get("hreflang")?.asString?.ifEmpty { null },
238212
title = obj.get("title")?.asString?.ifEmpty { null },
239213
length = obj.get("length")?.let { if (it.isJsonNull) null else it.asLong },
240-
extEnclosureDownloadProgress = obj.get("extEnclosureDownloadProgress")?.let { if (it.isJsonNull) null else it.asDouble },
214+
extEnclosureDownloadProgress = obj.get("extEnclosureDownloadProgress")
215+
?.let { if (it.isJsonNull) null else it.asDouble },
241216
extCacheUri = obj.get("extCacheUri")?.asString?.ifEmpty { null }
242217
)
243218
}

app/src/test/kotlin/org/vestifeed/db/table/FeedTest.kt

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,9 @@ class FeedTest {
2121
db = Database(BundledSQLiteDriver(), ":memory:")
2222
}
2323

24-
@Test
25-
fun feedSchema_tableName() {
26-
assertEquals("feed", FeedSchema.TABLE_NAME)
27-
}
28-
29-
@Test
30-
fun feedSchema_columns() {
31-
val columns = FeedSchema.Columns.entries
32-
assertEquals(6, columns.size)
33-
assertEquals("id", columns[0].sqlName)
34-
assertEquals("links", columns[1].sqlName)
35-
assertEquals("title", columns[2].sqlName)
36-
assertEquals("ext_open_entries_in_browser", columns[3].sqlName)
37-
assertEquals("ext_blocked_words", columns[4].sqlName)
38-
assertEquals("ext_show_preview_images", columns[5].sqlName)
39-
}
40-
4124
@Test
4225
fun feedSchema_createTableStatement() {
43-
val statement = FeedSchema.toString()
26+
val statement = FEED_SCHEMA
4427
assertTrue(statement.contains("CREATE TABLE feed"))
4528
assertTrue(statement.contains("id TEXT PRIMARY KEY NOT NULL"))
4629
assertTrue(statement.contains("links TEXT"))
@@ -50,12 +33,6 @@ class FeedTest {
5033
assertTrue(statement.contains("ext_show_preview_images INTEGER"))
5134
}
5235

53-
@Test
54-
fun feedProjection_columns() {
55-
val columns = FeedProjection.columns
56-
assertEquals("id,links,title,ext_open_entries_in_browser,ext_blocked_words,ext_show_preview_images", columns)
57-
}
58-
5936
@Test
6037
fun feedQueries_insertOrReplace() {
6138
val feed = createFeed()

0 commit comments

Comments
 (0)