Skip to content

Commit

Permalink
Improve result handling and add QueryResult#stream
Browse files Browse the repository at this point in the history
  • Loading branch information
MineKing9534 committed Oct 20, 2024
1 parent ebfd06c commit fc6a2de
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 60 deletions.
41 changes: 9 additions & 32 deletions core/src/main/kotlin/de/mineking/database/Result.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@ package de.mineking.database
import org.jdbi.v3.core.result.ResultIterable
import java.sql.ResultSet
import java.sql.SQLException
import java.util.stream.Stream

data class ReadContext(val instance: Any?, val table: TableStructure<*>, val set: ResultSet, val selected: List<String>?, val prefix: Array<String> = emptyArray(), val autofillPrefix: (String) -> Boolean = { true }, var shouldRead: Boolean = true) {
fun proceed(): Boolean {
if (!shouldRead) return true

shouldRead = false
return set.next()
}

fun formatName(name: String) = ((prefix.takeIf { it.isNotEmpty() || !autofillPrefix(name) } ?: arrayOf(table.name)) + name).joinToString(".")

fun <T> read(name: String, reader: (ResultSet, String) -> T): T = reader(set, formatName(name))
Expand All @@ -28,38 +22,21 @@ interface QueryResult<T> {
return try { first() }
catch (_: NoSuchElementException) { null }
}
}

interface RowQueryResult<T: Any> : QueryResult<T> {
val instance: () -> T
fun <O> execute(handler: ((T) -> Boolean) -> O): O

override fun list(): List<T> = execute { read ->
val result = arrayListOf<T>()

while (true) {
val obj = instance()

if (!read(obj)) break
result.add(obj)
}

result
}

override fun first(): T = execute { read ->
val obj = instance()

if (!read(obj)) throw NoSuchElementException()
else obj
}
fun stream(): Stream<T>
fun <R> withStream(handler: (Stream<T>) -> R): R = stream().use(handler)
}

interface ValueQueryResult<T>: QueryResult<T> {
interface SimpleQueryResult<T>: QueryResult<T> {
fun <O> execute(handler: (ResultIterable<T>) -> O): O

override fun list(): List<T> = execute { it.list() }
override fun first(): T = execute { it.first() }

/**
* Note: The returned stream has to be closed by the user!
*/
override fun stream(): Stream<T> = execute { it.stream() }
}

data class UpdateResult<T>(
Expand Down
5 changes: 1 addition & 4 deletions core/src/main/kotlin/de/mineking/database/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,14 @@ abstract class TableImplementation<T: Any>(
) : Table<T>, InvocationHandler {
override val implementation: TableImplementation<T> = this

open fun parseResult(context: ReadContext): Boolean {
if (!context.proceed()) return false

open fun parseResult(context: ReadContext) {
@Suppress("UNCHECKED_CAST")
val instance = context.instance as T

fun <C> setField(column: DirectColumnData<T, C>) = column.set(instance, column.mapper.read(column, column.type, context, column.name))
structure.columns.forEach { if (context.shouldRead(it.name)) setField(it) }

if (context.instance is DataObject<*>) context.instance.afterRead()
return true
}

override fun invoke(proxy: Any?, method: Method?, args: Array<out Any?>?): Any? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,15 @@ class PostgresTable<T: Any>(
)

val sql = createSelect(columnList.joinToString { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" }, where, order, limit, offset)
return object : RowQueryResult<T> {
override val instance: () -> T = this@PostgresTable.instance
override fun <O> execute(handler: ((T) -> Boolean) -> O): O = structure.manager.execute { it.createQuery(sql)
return object : SimpleQueryResult<T> {
override fun <O> execute(handler: (ResultIterable<T>) -> O): O = structure.manager.execute { handler(it.createQuery(sql)
.bindMap(where.values(structure))
.execute { stmt, _ ->
val statement = stmt.get()
val set = statement.resultSet

handler { parseResult(ReadContext(it, structure, set, columnList.map { "${ it.first }.${ it.second }" })) }
.map { set, _ ->
val instance = instance()
parseResult(ReadContext(instance, structure, set, columnList.map { "${ it.first }.${ it.second }" }))
instance
}
}
) }
}
}

Expand All @@ -139,7 +137,7 @@ class PostgresTable<T: Any>(
val columnList = createColumnList(column?.column?.let { listOf(it) } ?: emptyList())

val sql = createSelect((columnList.map { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" } + "(${ target.format(structure) }) as \"value\"").joinToString(), where, order, limit, offset)
return object : ValueQueryResult<C> {
return object : SimpleQueryResult<C> {
override fun <O> execute(handler: (ResultIterable<C>) -> O): O = structure.manager.execute { handler(it.createQuery(sql)
.bindMap(target.values(structure, column?.column))
.bindMap(where.values(structure))
Expand All @@ -156,11 +154,11 @@ class PostgresTable<T: Any>(
update.bind(it.name, createArgument(it))
}

return update.execute { stmt, ctx -> ctx.use {
update.execute { stmt, ctx -> ctx.use {
val statement = stmt.get()
val set = statement.resultSet

parseResult(ReadContext(obj, structure, set, columns.filter { it.getRootColumn().reference == null }.map { it.name }, autofillPrefix = { false }))
if (set.next()) parseResult(ReadContext(obj, structure, set, columns.filter { it.getRootColumn().reference == null }.map { it.name }, autofillPrefix = { false }))
} }
}

Expand Down
22 changes: 10 additions & 12 deletions sqlite/src/main/kotlin/de/mineking/database/vendors/SQLiteTable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,15 @@ class SQLiteTable<T: Any>(
)

val sql = createSelect(columnList.joinToString { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" }, where, order, limit, offset)
return object : RowQueryResult<T> {
override val instance: () -> T = this@SQLiteTable.instance
override fun <O> execute(handler: ((T) -> Boolean) -> O): O = structure.manager.execute { it.createQuery(sql)
return object : SimpleQueryResult<T> {
override fun <O> execute(handler: (ResultIterable<T>) -> O): O = structure.manager.execute { handler(it.createQuery(sql)
.bindMap(where.values(structure))
.execute { stmt, _ ->
val statement = stmt.get()
val set = statement.resultSet

handler { parseResult(ReadContext(it, structure, set, columnList.map { "${ it.first }.${ it.second }" })) }
.map { set, _ ->
val instance = instance()
parseResult(ReadContext(instance, structure, set, columnList.map { "${ it.first }.${ it.second }" }))
instance
}
}
) }
}
}

Expand All @@ -139,7 +137,7 @@ class SQLiteTable<T: Any>(
val columnList = createColumnList(column?.column?.let { listOf(it) } ?: emptyList())

val sql = createSelect((columnList.map { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" } + "(${ target.format(structure) }) as \"value\"").joinToString(), where, order, limit, offset)
return object : ValueQueryResult<C> {
return object : SimpleQueryResult<C> {
override fun <O> execute(handler: (ResultIterable<C>) -> O): O = structure.manager.execute { handler(it.createQuery(sql)
.bindMap(target.values(structure, column?.column))
.bindMap(where.values(structure))
Expand All @@ -156,11 +154,11 @@ class SQLiteTable<T: Any>(
update.bind(it.name, createArgument(it))
}

return update.execute { stmt, ctx -> ctx.use {
update.execute { stmt, ctx -> ctx.use {
val statement = stmt.get()
val set = statement.resultSet

parseResult(ReadContext(obj, structure, set, columns.filter { it.getRootColumn().reference == null }.map { it.name }, autofillPrefix = { false }))
if (set.next()) parseResult(ReadContext(obj, structure, set, columns.filter { it.getRootColumn().reference == null }.map { it.name }, autofillPrefix = { false }))
} }
}

Expand Down

0 comments on commit fc6a2de

Please sign in to comment.