diff --git a/core/src/main/kotlin/de/mineking/database/Result.kt b/core/src/main/kotlin/de/mineking/database/Result.kt index bc2f952..857fc14 100644 --- a/core/src/main/kotlin/de/mineking/database/Result.kt +++ b/core/src/main/kotlin/de/mineking/database/Result.kt @@ -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?, val prefix: Array = 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 read(name: String, reader: (ResultSet, String) -> T): T = reader(set, formatName(name)) @@ -28,38 +22,21 @@ interface QueryResult { return try { first() } catch (_: NoSuchElementException) { null } } -} - -interface RowQueryResult : QueryResult { - val instance: () -> T - fun execute(handler: ((T) -> Boolean) -> O): O - - override fun list(): List = execute { read -> - val result = arrayListOf() - - 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 + fun withStream(handler: (Stream) -> R): R = stream().use(handler) } -interface ValueQueryResult: QueryResult { +interface SimpleQueryResult: QueryResult { fun execute(handler: (ResultIterable) -> O): O override fun list(): List = 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 = execute { it.stream() } } data class UpdateResult( diff --git a/core/src/main/kotlin/de/mineking/database/Table.kt b/core/src/main/kotlin/de/mineking/database/Table.kt index 76fed71..c0bc368 100644 --- a/core/src/main/kotlin/de/mineking/database/Table.kt +++ b/core/src/main/kotlin/de/mineking/database/Table.kt @@ -70,9 +70,7 @@ abstract class TableImplementation( ) : Table, InvocationHandler { override val implementation: TableImplementation = 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 @@ -80,7 +78,6 @@ abstract class TableImplementation( 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?): Any? { diff --git a/postgres/src/main/kotlin/de/mineking/database/vendors/PostgresTable.kt b/postgres/src/main/kotlin/de/mineking/database/vendors/PostgresTable.kt index 8379d56..2b609d7 100644 --- a/postgres/src/main/kotlin/de/mineking/database/vendors/PostgresTable.kt +++ b/postgres/src/main/kotlin/de/mineking/database/vendors/PostgresTable.kt @@ -108,17 +108,15 @@ class PostgresTable( ) val sql = createSelect(columnList.joinToString { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" }, where, order, limit, offset) - return object : RowQueryResult { - override val instance: () -> T = this@PostgresTable.instance - override fun execute(handler: ((T) -> Boolean) -> O): O = structure.manager.execute { it.createQuery(sql) + return object : SimpleQueryResult { + override fun execute(handler: (ResultIterable) -> 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 } - } + ) } } } @@ -139,7 +137,7 @@ class PostgresTable( 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 { + return object : SimpleQueryResult { override fun execute(handler: (ResultIterable) -> O): O = structure.manager.execute { handler(it.createQuery(sql) .bindMap(target.values(structure, column?.column)) .bindMap(where.values(structure)) @@ -156,11 +154,11 @@ class PostgresTable( 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 })) } } } diff --git a/sqlite/src/main/kotlin/de/mineking/database/vendors/SQLiteTable.kt b/sqlite/src/main/kotlin/de/mineking/database/vendors/SQLiteTable.kt index 2b00a47..c5fc440 100644 --- a/sqlite/src/main/kotlin/de/mineking/database/vendors/SQLiteTable.kt +++ b/sqlite/src/main/kotlin/de/mineking/database/vendors/SQLiteTable.kt @@ -108,17 +108,15 @@ class SQLiteTable( ) val sql = createSelect(columnList.joinToString { "\"${ it.first }\".\"${ it.second }\" as \"${ it.first }.${ it.second }\"" }, where, order, limit, offset) - return object : RowQueryResult { - override val instance: () -> T = this@SQLiteTable.instance - override fun execute(handler: ((T) -> Boolean) -> O): O = structure.manager.execute { it.createQuery(sql) + return object : SimpleQueryResult { + override fun execute(handler: (ResultIterable) -> 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 } - } + ) } } } @@ -139,7 +137,7 @@ class SQLiteTable( 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 { + return object : SimpleQueryResult { override fun execute(handler: (ResultIterable) -> O): O = structure.manager.execute { handler(it.createQuery(sql) .bindMap(target.values(structure, column?.column)) .bindMap(where.values(structure)) @@ -156,11 +154,11 @@ class SQLiteTable( 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 })) } } }