Skip to content

Commit

Permalink
Embed frags in sql interpolator (#51)
Browse files Browse the repository at this point in the history
* embed frags in sql interpolator

* better testing
  • Loading branch information
AugustNagro authored Nov 13, 2024
1 parent f978d91 commit d2c977f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 0 deletions.
11 changes: 11 additions & 0 deletions magnum/src/main/scala/com/augustnagro/magnum/util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private def sqlImpl(sc: Expr[StringContext], args: Expr[Seq[Any]])(using

val interpolatedVarargs = Varargs(argsExprs.map {
case '{ $arg: SqlLiteral } => '{ $arg.queryRepr }
case '{ $arg: Frag } => '{ $arg.sqlString }
case '{ $arg: tp } =>
val codecExpr = summonWriter[tp]
'{ $codecExpr.queryRepr }
Expand Down Expand Up @@ -101,6 +102,15 @@ private def sqlWriter(
argsExprs match
case head +: tail =>
head match
case '{ $arg: Frag } =>
'{
val i = $iExpr
val frag = $args(i).asInstanceOf[Frag]
val pos = $posExpr
val newPos = frag.writer.write($psExpr, pos)
val newI = i + 1
${ sqlWriter(psExpr, '{ newPos }, args, tail, '{ newI }) }
}
case '{ $arg: tp } =>
val codecExpr = summonWriter[tp]
'{
Expand All @@ -116,6 +126,7 @@ private def sqlWriter(
case _ =>
report.errorAndAbort("Args must be explicit", head)
case Seq() => posExpr
end match
end sqlWriter

private def summonWriter[T: Type](using Quotes): Expr[DbCodec[T]] =
Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/ClickHouseTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,19 @@ class ClickHouseTests extends FunSuite, TestContainersFixtures:
.createContainer()
)

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0"
val limitFrag = sql"LIMIT $limit"
sql"SELECT count(*) FROM person WHERE $filter $limitFrag $offsetFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = true"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

override def munitFixtures: Seq[Fixture[_]] =
super.munitFixtures :+ clickHouseContainer

Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/H2Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,19 @@ class H2Tests extends FunSuite:
assertEquals(rowsUpdated, 1)
assertEquals(personRepo.findById(p.id).get.isAdmin, true)

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0"
val limitFrag = sql"LIMIT $limit"
sql"SELECT count(*) FROM person WHERE $filter $limitFrag $offsetFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = true"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

lazy val h2DbPath = Files.createTempDirectory(null).toAbsolutePath

def ds(): DataSource =
Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/MySqlTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,19 @@ class MySqlTests extends FunSuite, TestContainersFixtures:
assertEquals(rowsUpdated, 1)
assertEquals(personRepo.findById(p.id).get.isAdmin, true)

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0"
val limitFrag = sql"LIMIT $limit"
sql"SELECT count(*) FROM person WHERE $filter $limitFrag $offsetFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = true"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

val mySqlContainer = ForAllContainerFixture(
MySQLContainer
.Def(dockerImageName = DockerImageName.parse("mysql:8.0.32"))
Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/OracleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,19 @@ class OracleTests extends FunSuite, TestContainersFixtures:
assertEquals(rowsUpdated, 1)
assertEquals(personRepo.findById(p.id).get.isAdmin, "Y")

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0 ROWS"
val limitFrag = sql"FETCH NEXT $limit ROWS ONLY"
sql"SELECT count(*) FROM person WHERE $filter $offsetFrag $limitFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = 'Y'"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

val oracleContainer = ForAllContainerFixture(
OracleContainer
.Def(dockerImageName =
Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/PgTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,19 @@ class PgTests extends FunSuite, TestContainersFixtures:
assertEquals(it.size, 8)
)

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0"
val limitFrag = sql"LIMIT $limit"
sql"SELECT count(*) FROM person WHERE $filter $limitFrag $offsetFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = true"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

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

Expand Down
13 changes: 13 additions & 0 deletions magnum/src/test/scala/SqliteTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,19 @@ class SqliteTests extends FunSuite:
assertEquals(rowsUpdated, 1)
assertEquals(personRepo.findById(p.id).get.isAdmin, true)

test("embed Frag into Frag"):
def findPersonCnt(filter: Frag, limit: Long = 1)(using DbCon): Int =
val offsetFrag = sql"OFFSET 0"
val limitFrag = sql"LIMIT $limit"
sql"SELECT count(*) FROM person WHERE $filter $limitFrag $offsetFrag"
.query[Int]
.run()
.head
val isAdminFrag = sql"is_admin = true"
connect(ds()):
val johnCnt = findPersonCnt(sql"$isAdminFrag AND first_name = 'John'", 2)
assertEquals(johnCnt, 2)

lazy val sqliteDbPath = Files.createTempFile(null, ".db").toAbsolutePath

def ds(): DataSource =
Expand Down

0 comments on commit d2c977f

Please sign in to comment.