Slick Query Enrichment Table Column - scala

I'm having an issue with Slick 3 getting a proper reference to the Table in an implicit query enhancement. This code works fine in Slick 2 but the new Table only has a Seq[Columns] on which I still can't call the column method on.
class SlickUtils {
implicit class QueryEnrichment[M,U<: Table[U] /*not valid */,C[_]](q: Query[U,M,C]) {
def sortDynamic(sortString: String): Query[U,M,C] = {
val sortKeys = sortString.split(',').toList.map(_.split('.').map(_.toUpperCase).toList)
sortDynamicImpl(sortKeys)
}
private def sortDynamicImpl(sortKeys: List[Seq[String]]): Query[U,M,C] = {
sortKeys match {
case key :: tail =>
sortDynamicImpl(tail).sortBy(table =>
key match {
case name :: Nil => table.column[String](name).desc // DOES NOT HAVE COLUMN METHOD
}
)
}
}
}
}

We use this trait with slick 3 and it works like a charm
trait Sorting {
implicit class QueryExtensions3[T <: Table[_], E, C[_]](val query: Query[T, E, C]) {
def sortDynamic(sortString: String): Query[T, E, C] = {
// split string into useful pieces
val sortKeys = sortString.split(',').toList.map(_.split('.').toList)
sortDynamicImpl(sortKeys)
}
private[this] def sortDynamicImpl(sortKeys: List[Seq[String]]): Query[T, E, C] = {
sortKeys match {
case key :: tail =>
sortDynamicImpl(tail).sortBy(table =>
key match {
case name :: Nil => table.column[String](name).asc
case name :: "asc" :: Nil => table.column[String](name).asc
case name :: "desc" :: Nil => table.column[String](name).desc
case o => throw new Exception("invalid sorting key: " + o)
})
case Nil => query
}
}
}
}

Related

How should I get B form A => B

I'm new to Scala, and I'm running into this strange situation.
def bar[A, B](implicit foo: A => B): B = {
// do something
foo
}
And then I got error like
require B but found A => B
How should I get B form A => B
Here's the reason why I did this, I have two functions:
def funcA: String = {
def getStrA: String = "A"
// then there's the same operation in both functions
Try{ } match {
case Success(_) => getStrA
case Failure(_) => // exactlly same error handler in both function
}
}
def funcB: Int = {
def doSomething(x: Int): Int = {
// do something
x / 1
}
val x = 1
Try{ } match {
case Success(_) => doSomething(1)
case Failure(_) => // exactlly same error handler in both function
}
}
Here's what I want to achieve
def funcA: String = {
implicit def getStrA: String = "A"
bar
}
def funcB: Int = {
val x = 1
implicit def doSomething(x: Int): Int = {
// do something
x / 1
}
bar
}
def bar[A, B](implicit foo: A => B): B = {
Try{ } match {
case Success(_) => foo
case Failure(_) => // exactlly same error handler in both function
}
}
You have a conversion from A to B. You need to return B. The only way to do this is to pass A into the function. This signature has an implied assumption that you have some valid A value (most likely hardcoded) that you will always use here.
def bar[A, B](implicit foo: A => B): B = {
val a: A = ... // hmm...
foo(a)
}
Considering, that A is parametric, then you are either missing some information, or this A is impossible to create (it cannot be null because not all types can take null as a value), so you might need to throw exception in such case. Probably you are either missing some A provider or you should always fail this operation.
UPDATE:
There is no need for using implicits at all in your code:
def bar[B](f: onSuccess: A => B) =
Try{ some operations } match {
case Success(value) => onSuccess(value)
case Failure(_) => // error handler
}
def funcA = bar(_ => "A")
def funcB = bar(_ => 1)

How to get underlying constant type from singleton type with Scala reflection API

import scala.reflect.runtime.universe._
val a: 42 = 42
val t: Type = typeOf[a.type]
assert(getConstantType(t).get =:= typeOf[42])
def getConstantType(t: Type): Option[ConstantType] = ???
How could I generally implement getConstantType so that the above assertion passes?
I assumed that something like this was possible since the assertion below passes:
assert(t <:< typeOf[42])
t.widen goes too far as it return Int. I'm looking for something that returns Int(42).
How about
assert(t.resultType =:= typeOf[42])
Updated -
def getConstantType[T](t: T): t.type = t
Update 2 -
def getConstantType(tp: Type): Option[ConstantType] = {
tp.erasure match {
case ConstantType(_) => Some(tp.erasure.asInstanceOf[ConstantType])
case _ => None
}
}
Try
def getConstantType(tp: Type): Option[ConstantType] = {
def unrefine(t: Type): Type = t.dealias match {
case RefinedType(List(t), scope) if scope.isEmpty => unrefine(t)
case t => t
}
unrefine(tp) match {
case SingleType(_, sym) => sym.typeSignature match {
case NullaryMethodType(t) => unrefine(t) match {
case c: ConstantType => Some(c)
case _ => None
}
case _ => None
}
case _ => None
}
}

How can I consume a Scala macro/quasiquote for code templates?

I want to generate a bunch of objects at compile time that follow a simple pattern, so I wrote the following macro:
object MyMacro {
def readWrite[T](taName: String, readParse: String => T, label: String, format: T => String): Any = macro readWriteImpl[T]
def readWriteImpl[T: c.WeakTypeTag](c: Context)(taName: c.Expr[String], readParse: c.Expr[String => T], label: c.Expr[String], format: c.Expr[T => String]): c.Expr[Any] = {
import c.universe._
def termName(s: c.Expr[String]): TermName = s.tree match {
case Literal(Constant(s: String)) => TermName(s)
case _ => c.abort(c.enclosingPosition, "Not a string literal")
}
c.Expr[Any](q"""
object ${termName(taName)} extends TypeAdapter.=:=[${implicitly[c.WeakTypeTag[T]].tpe}] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): ${implicitly[c.WeakTypeTag[T]].tpe} =
reader.readString(path) match {
case null => null.asInstanceOf[${implicitly[c.WeakTypeTag[T]].tpe}]
case s => Try( $readParse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse "+${termName(label)}+" from input '"+s+"'", List.empty[String], u)
}
}
def write[WIRE](t: ${implicitly[c.WeakTypeTag[T]].tpe}, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString($format(t), out)
}
}
""")
}
}
I'm not sure I have the return value for readWrite and readWriteImpl correct--the compiler complains mightily about some assertion failure!
I'm also not sure how to actually consume this macro. First I tried (in a separate compilation unit):
object TimeFactories {
MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString)
}
Didn't work. If I tried to reference TimeFactories.DurationTypeAdapterFactory I got an error saying it wasn't found. Next I thought I'd try assigning it to a val...didn't work either:
object Foo {
val duration = MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString).asInstanceOf[TypeAdapterFactory]
}
How can I wire this up so I get generated code compiled like this:
object TimeFactories{
object DurationTypeAdapterFactory extends TypeAdapter.=:=[Duration] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): Duration =
reader.readString(path) match {
case null => null.asInstanceOf[Duration]
case s => Try( Duration.parse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse Duration from input 'Duration'", List.empty[String], u)
}
}
def write[WIRE](t: Duration, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString(t.toString, out)
}
}
// ... More invocations of the readWrite macro with other types for T
}
I don't think, that you can generate new identifiers using macros and than use them publicly.
Instead, try to replace object ${termName(taName)} extends TypeAdapter simply with new TypeAdapter and assign invocation of the macro to a val (as in your second example). You will then reference an anonymous (and generated) class stored in a val. Parameter taName becomes redundant.

Dynamic sortBy with Slick 3

I'm attempting to dynamically include a sortBy to my query which sorts based on its string name from a query parameter. In Slick 3 this has proven to be quite tricky. Currently my setup is:
trait Model {
type ATable <: AbstractTable[_]
def tableQuery: TableQuery[ATable]
def sortMap: Map[String, Rep[_]]
private def sortKey[T](e: ATable, sort: (String, SortOrder)): ColumnOrdered[_] = sort match {
case (field, SortOrder.Asc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Asc))
case (field, SortOrder.Desc) => ColumnOrdered(sortMap.getOrElse(field, throw new ClientException(s"Can't sort by $field")), Ordering(Ordering.Desc))
}
def all(sort: (String, SortOrder)) = tableQuery.sortBy(sortKey(_, sort)).result
}
object User extends Model {
type ATable = Tables.User
val tableQuery = Tables.User
val sortMap = Map( "id" -> tableQuery.id )
}
But running db.run(User.all(("id", SortOrder.Asc)) throws the following error:
slick.SlickException: No type for symbol name found in Vector[t2<#t3<UnassignedType>>]
Does anyone know of a better solution or where I'm going wrong?
The way I solved this was to change my sortMap to contain the table type with column type implicit (from Map[String, Rep[_]] to Map[String, ATable => Rep[_]]):
trait Model {
type ATable <: AbstractTable[_]
def tableQuery: TableQuery[ATable]
def sortMap: Map[String, ATable => Rep[_]]
private def sortKey(baseQ: Query[ATable, ATable#TableElementType, Seq], sort: (String, SortOrder)) = {
val rep = sortMap.getOrElse(sort._1, throw new ClientException(s"Can't sort by ${sort._1}"))
val orderedRep = sort._2 match {
case SortOrder.Asc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Asc))
case SortOrder.Desc => (t: ATable) => ColumnOrdered(rep(t), slick.ast.Ordering(slick.ast.Ordering.Desc))
}
q.sortBy(orderedRep)
}
def all(sort: (String, SortOrder)) = sortKey(tableQuery, sort).result
}
object User extends Model {
type ATable = Tables.User
val tableQuery = Tables.User
val sortMap = Map( "id" -> { (t: ATable) => t.id } )
}
The only suboptimal aspect of this solution is that if a new column is added to the database it has to be manually added to the sortKey. I'm looking into including these maps in the table code generation scripts.
You just need to include in generation something like
val columns = Map( "id" -> { (t: User) => t.id } )
and use it like so:
class UserDAO{
//...
baseQuery = filterContext.sort.foldLeft[Query[User, UserRow, Seq]](User) ((_, s) => UserColumnFilter.getSort(s,User.baseTableRow.columns));
//...
}
case class Sort(columnName:String,value: String) {}
case class FilterContext(filters:List[Filter],page:Int,pageSize:Int,sort:List[Sort])
object UserColumnSort extends ColumnSort[User,UserRow](User)
class ColumnSort[T<:Table[C],C](query:TableQuery[T])
{
def getSort(s:Sort,columns:Map[String, T => Rep[_]]):Query[T, C, Seq] = {
val aux = columns.getOrElse(s.columnName, throw new Exception(s"Can't sort by ${s.columnName}"));
val orderedRep = s.value match {
case "asc" => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Asc))
case _ => (t: T) => ColumnOrdered(aux(t), slick.ast.Ordering(slick.ast.Ordering.Desc))
}
return query.sortBy(orderedRep);
}
}

Can't deal with UUID in Play (jdbc)

I'm using joda DateTime and UUID in my Play project. I'm struggling trying to put and get them from Postgresql:
import org.joda.time.DateTime
case class MyClass(id: Pk[UUID], name: String, addedAt: DateTime)
object MyClass {
val simple =
SqlParser.get[Pk[UUID]]("id") ~
SqlParser.get[String]("name") ~
SqlParser.get[DateTime]("added_at") map {
case id ~ name ~ addedAt => MyClass(id, name, addedAt)
}
implicit def rowToId = Column.nonNull[UUID] { (value, meta) =>
maybeValueToUUID(value) match {
case Some(uuid) => Right(uuid)
case _ => Left(TypeDoesNotMatch( s"Cannot convert $value: ${value.asInstanceOf[Any].getClass} to UUID"))
}
}
implicit def idToStatement = new ToStatement[UUID] {
def set(s: PreparedStatement, index: Int, aValue: UUID): Unit = s setObject(index, toByteArray(aValue))
}
def getSingle(id: UUID): Option[MyClass] = {
DB withConnection {
implicit con =>
SQL("SELECT my_table.id, my_table.name, my_table.added_at FROM my_table WHERE id = {id}")
.on('id -> id)
.as(MyClass.simple.*)
} match {
case List(x) => Some(x)
case _ => None
}
}
Implicit functions for joda DateTime are omited because they don't cause any error at this point. What causes an error is a getSingle(...) - conversion from and to UUID. The error is
org.postgresql.util.PSQLException: operator does not exist: uuid = bytea
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
4 helper functions:
private def maybeValueToUUID(value: Any): Option[UUID] = maybeValueToByteArray(value) match {
case Some(bytes) => Some(fromByteArray(bytes))
case _ => None
}
private def maybeValueToByteArray(value: Any): Option[Array[Byte]] =
try {
value match {
case bytes: Array[Byte] => Some(bytes)
case clob: Clob => None //todo
case blob: Blob => None //todo
case _ => None
}
} catch {
case e: Exception => None
}
def toByteArray(uuid: UUID) = {
val buffer = ByteBuffer.wrap(new Array[Byte](16))
buffer putLong uuid.getMostSignificantBits
buffer putLong uuid.getLeastSignificantBits
buffer.array
}
def fromByteArray(b: Array[Byte]) = {
val buffer = ByteBuffer.wrap(b)
val high = buffer.getLong
val low = buffer.getLong
new UUID(high, low)
}
Note that a record I'm trying to retrieve exists and has a correct format.