Add statement
This commit is contained in:
parent
47cf808e8f
commit
cb0c6082d0
10 changed files with 97 additions and 72 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ target/
|
|||
.metals/
|
||||
.bloop/
|
||||
project/metals.sbt
|
||||
.aider*
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
name := "minisql"
|
||||
|
||||
scalaVersion := "3.5.2"
|
||||
scalaVersion := "3.7.0"
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalameta" %% "munit" % "1.0.3" % Test
|
||||
)
|
||||
|
||||
scalacOptions ++= Seq("-experimental", "-language:experimental.namedTuples")
|
||||
|
|
|
@ -1 +1 @@
|
|||
sbt.version=1.10.5
|
||||
sbt.version=1.10.10
|
||||
|
|
|
@ -3,10 +3,9 @@ package minisql
|
|||
import scala.util.Try
|
||||
|
||||
trait ParamEncoder[E] {
|
||||
|
||||
type Stmt
|
||||
|
||||
def setParam(s: Stmt, idx: Int, v: E): Unit
|
||||
def setParam(s: Stmt, idx: Int, v: E): Stmt
|
||||
}
|
||||
|
||||
trait ColumnDecoder[X] {
|
||||
|
|
|
@ -9,6 +9,40 @@ import minisql.{NamingStrategy, ParamEncoder}
|
|||
import minisql.ColumnDecoder
|
||||
import minisql.ast.{Ast, ScalarValueLift, CollectAst}
|
||||
|
||||
trait RowExtract[A, Row] {
|
||||
def extract(row: Row): Try[A]
|
||||
}
|
||||
|
||||
object RowExtract {
|
||||
|
||||
private def extractorImpl[A, Row](
|
||||
decoders: IArray[Any],
|
||||
m: Mirror.ProductOf[A]
|
||||
): RowExtract[A, Row] = new RowExtract[A, Row] {
|
||||
def extract(row: Row): Try[A] = {
|
||||
val decodedFields = decoders.zipWithIndex.traverse {
|
||||
case (d, i) =>
|
||||
d.asInstanceOf[ColumnDecoder.Aux[Row, ?]].decode(row, i)
|
||||
}
|
||||
decodedFields.map { vs =>
|
||||
m.fromProduct(Tuple.fromIArray(vs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline given [P <: Product, Row, Decoder[_]](using
|
||||
m: Mirror.ProductOf[P]
|
||||
): RowExtract[P, Row] = {
|
||||
val decoders =
|
||||
summonAll[
|
||||
Tuple.Map[m.MirroredElemTypes, [X] =>> ColumnDecoder[
|
||||
X
|
||||
] { type DBRow = Row }]
|
||||
]
|
||||
extractorImpl(decoders.toIArray.asInstanceOf, m)
|
||||
}
|
||||
}
|
||||
|
||||
trait Context[I <: Idiom, N <: NamingStrategy] { selft =>
|
||||
|
||||
val idiom: I
|
||||
|
@ -18,33 +52,6 @@ trait Context[I <: Idiom, N <: NamingStrategy] { selft =>
|
|||
type DBRow
|
||||
type DBResultSet
|
||||
|
||||
trait RowExtract[A] {
|
||||
def extract(row: DBRow): Try[A]
|
||||
}
|
||||
|
||||
object RowExtract {
|
||||
|
||||
private class ExtractorImpl[A](
|
||||
decoders: IArray[Any],
|
||||
m: Mirror.ProductOf[A]
|
||||
) extends RowExtract[A] {
|
||||
def extract(row: DBRow): Try[A] = {
|
||||
val decodedFields = decoders.zipWithIndex.traverse {
|
||||
case (d, i) =>
|
||||
d.asInstanceOf[Decoder[?]].decode(row, i)
|
||||
}
|
||||
decodedFields.map { vs =>
|
||||
m.fromProduct(Tuple.fromIArray(vs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline given [P <: Product](using m: Mirror.ProductOf[P]): RowExtract[P] = {
|
||||
val decoders = summonAll[Tuple.Map[m.MirroredElemTypes, Decoder]]
|
||||
ExtractorImpl(decoders.toIArray.asInstanceOf, m)
|
||||
}
|
||||
}
|
||||
|
||||
type Encoder[X] = ParamEncoder[X] {
|
||||
type Stmt = DBStatement
|
||||
}
|
||||
|
@ -76,7 +83,7 @@ trait Context[I <: Idiom, N <: NamingStrategy] { selft =>
|
|||
|
||||
inline def io[E](
|
||||
inline q: minisql.Query[E]
|
||||
)(using r: RowExtract[E]): DBIO[IArray[E]] = {
|
||||
)(using r: RowExtract[E, DBRow]): DBIO[IArray[E]] = {
|
||||
val lifts = q.liftMap
|
||||
val stmt = minisql.compile(q, idiom, naming)
|
||||
val (sql, params) = stmt.expand(lifts)
|
||||
|
|
|
@ -9,7 +9,6 @@ class MirrorContext[Idiom <: idiom.Idiom, Naming <: NamingStrategy](
|
|||
|
||||
type DBRow = Row
|
||||
|
||||
type DBResultSet = Iterable[DBRow]
|
||||
type DBResultSet = ResultSet
|
||||
|
||||
type DBStatement = IArray[Any]
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package minisql.context.mirror
|
||||
|
||||
import minisql.{MirrorContext, NamingStrategy}
|
||||
import minisql.{MirrorContext, NamingStrategy, ParamEncoder, ColumnDecoder}
|
||||
import minisql.idiom.Idiom
|
||||
import minisql.util.Messages.fail
|
||||
import scala.util.Try
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
/**
|
||||
* No extra class defined
|
||||
*/
|
||||
opaque type Row = IArray[Any] *: EmptyTuple
|
||||
opaque type ResultSet = Iterable[Row]
|
||||
opaque type Statement = Map[Int, Any]
|
||||
|
||||
extension (r: Row) {
|
||||
|
||||
|
@ -27,9 +30,42 @@ extension (r: Row) {
|
|||
}
|
||||
}
|
||||
|
||||
trait MirrorCodecs[I <: Idiom, N <: NamingStrategy] {
|
||||
this: MirrorContext[I, N] =>
|
||||
|
||||
given byteEncoder: Encoder[Byte]
|
||||
|
||||
type Encoder[E] = ParamEncoder[E] {
|
||||
type Stmt = Statement
|
||||
}
|
||||
|
||||
private def encoder[V]: Encoder[V] = new ParamEncoder[V] {
|
||||
|
||||
type Stmt = Map[Int, Any]
|
||||
|
||||
def setParam(s: Stmt, idx: Int, v: V): Stmt = {
|
||||
s + (idx -> v)
|
||||
}
|
||||
}
|
||||
|
||||
given Encoder[Long] = encoder[Long]
|
||||
|
||||
type Decoder[A] = ColumnDecoder[A] {
|
||||
type DBRow = Row
|
||||
}
|
||||
|
||||
private def apply[X](conv: Any => Option[X]): Decoder[X] =
|
||||
new ColumnDecoder[X] {
|
||||
type DBRow = Row
|
||||
def decode(row: Row, idx: Int): Try[X] = {
|
||||
row._1
|
||||
.lift(idx)
|
||||
.flatMap { x =>
|
||||
conv(x)
|
||||
}
|
||||
.toRight(new Exception(s"Cannot convert value at ${idx}"))
|
||||
.toTry
|
||||
}
|
||||
}
|
||||
|
||||
given Decoder[Long] = apply(x =>
|
||||
x match {
|
||||
case l: Long => Some(l)
|
||||
case _ => None
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package minisql.parsing
|
||||
|
||||
import minisql.ast
|
||||
import minisql.dsl.*
|
||||
import scala.quoted.*
|
||||
|
||||
private[parsing] def infixParsing(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package minisql.parsing
|
||||
|
||||
import minisql.ast
|
||||
import minisql.dsl.*
|
||||
import scala.quoted._
|
||||
|
||||
private[parsing] def propertyParsing(
|
||||
|
|
|
@ -1,35 +1,22 @@
|
|||
package minisql
|
||||
package minisql.parsing
|
||||
|
||||
import minisql.*
|
||||
import minisql.ast.*
|
||||
import minisql.idiom.*
|
||||
import minisql.NamingStrategy
|
||||
import minisql.MirrorContext
|
||||
import minisql.MirrorIdiom
|
||||
import minisql.context.mirror.{*, given}
|
||||
|
||||
class QuotedSuite extends munit.FunSuite {
|
||||
private inline def testQuoted(label: String)(
|
||||
inline x: Quoted,
|
||||
expect: Ast
|
||||
) = test(label) {
|
||||
assertEquals(compileTimeAst(x), Some(expect.toString()))
|
||||
}
|
||||
val ctx = new MirrorContext(MirrorIdiom, SnakeCase)
|
||||
|
||||
case class Foo(id: Long)
|
||||
|
||||
inline def Foos = query[Foo]("foo")
|
||||
val entityFoo = Entity("foo", Nil)
|
||||
val idx = Ident("x")
|
||||
|
||||
testQuoted("EntityQuery")(Foos, entityFoo)
|
||||
|
||||
testQuoted("Query/filter")(
|
||||
Foos.filter(x => x.id > 0),
|
||||
Filter(
|
||||
entityFoo,
|
||||
idx,
|
||||
BinaryOperation(Property(idx, "id"), NumericOperator.>, Constant(0))
|
||||
)
|
||||
)
|
||||
|
||||
testQuoted("Query/map")(
|
||||
Foos.map(x => x.id),
|
||||
Map(entityFoo, idx, Property(idx, "id"))
|
||||
)
|
||||
test("SimpleQuery") {
|
||||
val o = ctx.io(query[Foo]("foo").filter(_.id > 0))
|
||||
println("============" + o)
|
||||
o
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue