Permalink
Browse files

Generate Fast Path Converters in `.mapTo`

  • Loading branch information...
szeiger committed Oct 2, 2015
1 parent 3e29760 commit de88a610f224a510fd07d05d462591fabed06741
@@ -406,10 +406,11 @@ class JdbcMapperTest extends AsyncTest[JdbcTestDB] {
class T(tag: Tag) extends Table[Data](tag, "T_fastpath") {
def a = column[Int]("A")
def b = column[Int]("B")
def * = (a, b).mapTo[Data].fastPath(new FastPath(_) {
def * = (a, b).<>(Data.tupled, Data.unapply _).fastPath(new FastPath(_) {
val (a, b) = (next[Int], next[Int])
override def read(r: Reader) = Data(a.read(r), b.read(r))
})
def auto = (a, b).mapTo[Data]
}
val ts = TableQuery[T]
@@ -418,7 +419,8 @@ class JdbcMapperTest extends AsyncTest[JdbcTestDB] {
ts ++= Seq(new Data(1, 2), new Data(3, 4), new Data(5, 6)),
ts.filter(_.a === 1).update(Data(7, 8)),
ts.filter(_.a === 3).map(identity).update(Data(9, 10)),
ts.to[Set].result.map(_ shouldBe Set(Data(7, 8), Data(9, 10), Data(5, 6)))
ts.to[Set].result.map(_ shouldBe Set(Data(7, 8), Data(9, 10), Data(5, 6))),
ts.map(_.auto).to[Set].result.map(_ shouldBe Set(Data(7, 8), Data(9, 10), Data(5, 6)))
)
}
}
@@ -217,7 +217,7 @@ final class MappedScalaType(val baseType: Type, val mapper: MappedScalaType.Mapp
}
object MappedScalaType {
case class Mapper(toBase: Any => Any, toMapped: Any => Any, fastPath: Option[PartialFunction[Any, Any]])
case class Mapper(toBase: Any => Any, toMapped: Any => Any, fastPath: Option[Any => Any])
}
/** The standard type for freshly constructed nodes without an explicit type. */
@@ -46,7 +46,7 @@ trait JdbcMappingCompilerComponent { driver: JdbcDriver =>
override def createTypeMappingResultConverter(rc: ResultConverter[JdbcResultConverterDomain, Any], mapper: MappedScalaType.Mapper) = {
val tm = new TypeMappingResultConverter(rc, mapper.toBase, mapper.toMapped)
mapper.fastPath match {
case Some(pf) => pf.orElse[Any, Any] { case x => x }.apply(tm).asInstanceOf[ResultConverter[JdbcResultConverterDomain, Any]]
case Some(f) => f(tm).asInstanceOf[ResultConverter[JdbcResultConverterDomain, Any]]
case None => tm
}
}
@@ -1,5 +1,7 @@
package slick.lifted
import slick.relational.{ProductResultConverter, SimpleFastPathResultConverter, ResultConverterDomain, TypeMappingResultConverter}
import scala.language.{existentials, implicitConversions, higherKinds}
import scala.language.experimental.macros
import scala.annotation.implicitNotFound
@@ -289,34 +291,48 @@ object ShapedValue {
case NoSymbol => q"${rSym.name.toTermName}" // This can happen for case classes defined inside of methods
case s => q"$s"
}
val caseFields = rTag.tpe.decls.collect {
case s: TermSymbol if s.isVal && s.isCaseAccessor => (TermName(s.name.toString.trim), s.typeSignature)
val fields = rTag.tpe.decls.collect {
case s: TermSymbol if s.isVal && s.isCaseAccessor => (TermName(s.name.toString.trim), s.typeSignature, TermName(c.freshName()))
}.toIndexedSeq
val (f, g) = if(uTag.tpe <:< c.typeOf[slick.collection.heterogeneous.HList]) { // Map from HList
val rTypeAsHList = caseFields.foldRight[Tree](tq"_root_.slick.collection.heterogeneous.HNil.type") {
case ((_, t), z) => tq"_root_.slick.collection.heterogeneous.HCons[$t, $z]"
val rTypeAsHList = fields.foldRight[Tree](tq"_root_.slick.collection.heterogeneous.HNil.type") {
case ((_, t, _), z) => tq"_root_.slick.collection.heterogeneous.HCons[$t, $z]"
}
val matchNames = caseFields.map(_ => TermName(c.freshName()))
val pat = matchNames.foldRight[Tree](pq"_root_.slick.collection.heterogeneous.HNil") {
case (n, z) => pq"_root_.slick.collection.heterogeneous.HCons($n, $z)"
val pat = fields.foldRight[Tree](pq"_root_.slick.collection.heterogeneous.HNil") {
case ((_, _, n), z) => pq"_root_.slick.collection.heterogeneous.HCons($n, $z)"
}
val cons = caseFields.foldRight[Tree](q"_root_.slick.collection.heterogeneous.HNil") {
case ((n, _), z) => q"v.$n :: $z"
val cons = fields.foldRight[Tree](q"_root_.slick.collection.heterogeneous.HNil") {
case ((n, _, _), z) => q"v.$n :: $z"
}
(q"({ case $pat => new $rTag(..$matchNames) } : ($rTypeAsHList => $rTag)): ($uTag => $rTag)",
(q"({ case $pat => new $rTag(..${fields.map(_._3)}) } : ($rTypeAsHList => $rTag)): ($uTag => $rTag)",
q"{ case v => $cons }: ($rTag => $uTag)")
} else if(caseFields.length == 1) { // Map from single value
} else if(fields.length == 1) { // Map from single value
(q"($rModule.apply _) : ($uTag => $rTag)",
q"(($rModule.unapply _) : $rTag => Option[$uTag]).andThen(_.get)")
} else { // Map from tuple
(q"($rModule.tupled) : ($uTag => $rTag)",
q"(($rModule.unapply _) : $rTag => Option[$uTag]).andThen(_.get)")
}
q"""val ff = $f // Resolving f first creates more useful type errors
new _root_.slick.lifted.MappedProjection[$rTag, $uTag](${c.prefix}.toNode,
_root_.slick.ast.MappedScalaType.Mapper($g.asInstanceOf[Any => Any], ff.asInstanceOf[Any => Any], _root_.scala.None),
$rCT
)"""
val fpName = Constant("Fast Path of ("+fields.map(_._2).mkString(", ")+").mapTo["+rTag.tpe+"]")
val fpChildren = fields.map { case (_, t, n) => q"val $n = next[$t]" }
val fpReadChildren = fields.map { case (_, _, n) => q"$n.read(r)" }
q"""
val ff = $f.asInstanceOf[_root_.scala.Any => _root_.scala.Any] // Resolving f first creates more useful type errors
val gg = $g.asInstanceOf[_root_.scala.Any => _root_.scala.Any]
val fpMatch: (_root_.scala.Any => _root_.scala.Any) = {
case tm @ _root_.slick.relational.TypeMappingResultConverter(_: _root_.slick.relational.ProductResultConverter[_, _], _, _) =>
new _root_.slick.relational.SimpleFastPathResultConverter[_root_.slick.relational.ResultConverterDomain, $rTag](tm.asInstanceOf[_root_.slick.relational.TypeMappingResultConverter[_root_.slick.relational.ResultConverterDomain, $rTag, _]]) {
..$fpChildren
override def read(r: Reader) = new $rTag(..$fpReadChildren)
override def getDumpInfo = super.getDumpInfo.copy(name = $fpName)
}
case tm => tm
}
new _root_.slick.lifted.MappedProjection[$rTag, $uTag](${c.prefix}.toNode,
_root_.slick.ast.MappedScalaType.Mapper(gg, ff, _root_.scala.Some(fpMatch)), $rCT)
"""
}
}
@@ -362,7 +378,7 @@ class MappedProjection[T, P](child: Node, mapper: MappedScalaType.Mapper, classT
def encodeRef(path: Node): MappedProjection[T, P] = new MappedProjection[T, P](child, mapper, classTag) {
override def toNode = path
}
def genericFastPath(pf: PartialFunction[Any, Any]) = new MappedProjection[T, P](child, mapper.copy(fastPath = Some(pf)), classTag)
def genericFastPath(f: Function[Any, Any]) = new MappedProjection[T, P](child, mapper.copy(fastPath = Some(f)), classTag)
}
object MappedProjection {
@@ -80,7 +80,7 @@ trait RelationalProfile extends BasicProfile with RelationalTableComponent
def fastPath(fpf: (TypeMappingResultConverter[M, T, _] => SimpleFastPathResultConverter[M, T])): MappedProjection[T, P] = mp.genericFastPath {
case tm @ TypeMappingResultConverter(_: ProductResultConverter[_, _], _, _) =>
fpf(tm.asInstanceOf[TypeMappingResultConverter[M, T, _]])
case tm => tm
}
}
}

0 comments on commit de88a61

Please sign in to comment.