Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iterator streaming for Query and Returning #22

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions magnum/src/main/scala/com/augustnagro/magnum/Query.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.augustnagro.magnum

import scala.util.Using.Manager
import scala.util.{Failure, Success, Using}

case class Query[E](frag: Frag, reader: DbCodec[E]):
Expand All @@ -14,3 +15,20 @@ case class Query[E](frag: Frag, reader: DbCodec[E]):
) match
case Success(res) => res
case Failure(t) => throw SqlException(frag, t)

/** Streaming [[Iterator]]. Set [[fetchSize]] to give the JDBC driver a hint
* as to how many rows to fetch per request
*/
def iterator(
fetchSize: Int = 0
)(using con: DbCon, use: Manager): Iterator[E] =
logSql(frag)
try
val ps = use(con.connection.prepareStatement(frag.sqlString))
ps.setFetchSize(fetchSize)
frag.writer.write(ps, 1)
val rs = use(ps.executeQuery())
ResultSetIterator(rs, frag, reader)
catch case t => throw SqlException(frag, t)

end Query
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.augustnagro.magnum

import java.sql.ResultSet

private class ResultSetIterator[E](
rs: ResultSet,
frag: Frag,
reader: DbCodec[E]
) extends Iterator[E] {

private var rsHasNext: Boolean =
try rs.next()
catch case t => throw SqlException(frag, t)

override def hasNext: Boolean = rsHasNext

override def next(): E =
if !rsHasNext then throw IllegalStateException("ResultSet is empty")
try
val e = reader.readSingle(rs)
rsHasNext = rs.next()
e
catch case t => throw SqlException(frag, t)

}
21 changes: 21 additions & 0 deletions magnum/src/main/scala/com/augustnagro/magnum/Returning.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,24 @@ case class Returning[E](frag: Frag, reader: DbCodec[E]):
) match
case Success(res) => res
case Failure(t) => throw SqlException(frag, t)

/** Streaming [[Iterator]]. Set [[fetchSize]] to give the JDBC driver a hint
* as to how many rows to fetch per request
*/
def iterator(
fetchSize: Int = 0
)(using con: DbCon, use: Manager): Iterator[E] =
logSql(frag)
try
val ps = use(con.connection.prepareStatement(frag.sqlString))
ps.setFetchSize(fetchSize)
frag.writer.write(ps, 1)
val hasResults = ps.execute()
if hasResults then
val rs = use(ps.getResultSet())
ResultSetIterator(rs, frag, reader)
else
throw UnsupportedOperationException("No results for RETURNING clause")
catch case t => throw SqlException(frag, t)

end Returning
16 changes: 16 additions & 0 deletions magnum/src/test/scala/PgTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,22 @@ class PgTests extends FunSuite, TestContainersFixtures:
customPersonRepo.count
assertEquals(count, 8L)

test(".query iterator"):
connect(ds()):
Using.Manager(implicit use =>
val it = sql"SELECT * FROM person".query[Person].iterator()
assertEquals(it.map(_.id).size, 8)
)

test(".returning iterator"):
connect(ds()):
Using.Manager(implicit use =>
val it = sql"UPDATE person set is_admin = false RETURNING first_name"
.returning[String]
.iterator()
assertEquals(it.size, 8)
)

@Table(PostgresDbType, SqlNameMapper.CamelToSnakeCase)
case class BigDec(id: Int, myBigDec: Option[BigDecimal]) derives DbCodec

Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.2
sbt.version=1.9.9