Writing a simple CRUD application using ZIO & Quill.
Domain model consists of User and Roles (One to Many)
When trying to invoke method on one of the Entities lifted in the quill context (or obtained in a process of building a query) I'm facing compile time exception:
Can't find case class property: typedCode
roleSchema.filter(r => r.typedCode == lift(roleCode)).take(1)
val dc: db.Ctx.type = db.Ctx
import dc._
def findRoleByCode(roleCode: RoleCode): Result[Option[Role]] =
dc.run(
roleSchema.filter(r => r.typedCode == lift(roleCode)).take(1)
).map(_.headOption)
Schema as follows:
User Entity
case class User(
id: String,
firstName: String,
lastName: String,
age: Int
){
def typedId: UserId = UserId(id)
}
Role Entity
case class Role(
code: String,
name: String
){
def typedCode: RoleCode = RoleCode(code)
}
User To Role Association and Value Classes for identifiers
case class UserToRole(roleId: RoleCode, userId: UserId)
case class RoleCode(code: String) extends AnyVal
case class UserId(id: String) extends AnyVal
The conclusion that I currently have that it's not possible to invoke methods on entities present in the Quill context. I suppose it's due to macro evaluation and some kind of type replacement
The thing is - getting properties is not a problem. I can obtain role.code
as a String
, but can not invoke .typedCode
. Same applies to User
Edit
Trying converting .typedCode
and .typedId
to methods that accept Unit
as a parameter typedCode(): RoleCode
typedId(): UserId
results in another exception:
Tree 'r.typedCode()' can't be parsed to 'Ast'
roleSchema.filter(r => r.typedCode() == lift(roleCode)).take(1)
Edit 2
Building instances of value classes in a query: RoleCode(r.code)
results in:
exception during macro expansion:
scala.reflect.macros.TypecheckException: package module4.homework.dao.entity is not a value
at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:44)
at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$2(Typers.scala:38)
at scala.reflect.macros.contexts.Typers.doTypecheck$1(Typers.scala:37)
at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$7(Typers.scala:50)
at scala.reflect.internal.Trees.wrappingIntoTerm(Trees.scala:1891)
at scala.reflect.internal.Trees.wrappingIntoTerm$(Trees.scala:1888)
at scala.reflect.internal.SymbolTable.wrappingIntoTerm(SymbolTable.scala:28)
at scala.reflect.macros.contexts.Typers.typecheck(Typers.scala:50)
at scala.reflect.macros.contexts.Typers.typecheck$(Typers.scala:32)
at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
at io.getquill.quotation.Parsing$$anonfun$propertyParser$1.applyOrElse(Parsing.scala:540)
at io.getquill.quotation.Parsing$$anonfun$propertyParser$1.applyOrElse(Parsing.scala:526)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:64)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$Parser.apply(Parsing.scala:35)
at io.getquill.quotation.Parsing$$anonfun$propertyParser$1.applyOrElse(Parsing.scala:546)
at io.getquill.quotation.Parsing$$anonfun$propertyParser$1.applyOrElse(Parsing.scala:526)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:64)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$functionApplyParser$1.applyOrElse(Parsing.scala:579)
at io.getquill.quotation.Parsing$$anonfun$functionApplyParser$1.applyOrElse(Parsing.scala:578)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$operationParser$1.applyOrElse(Parsing.scala:558)
at io.getquill.quotation.Parsing$$anonfun$operationParser$1.applyOrElse(Parsing.scala:552)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:61)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$Parser.apply(Parsing.scala:35)
at io.getquill.quotation.Parsing.io$getquill$quotation$Parsing$$equalityWithInnerTypechecksIdiomatic(Parsing.scala:603)
at io.getquill.quotation.Parsing$$anonfun$equalityOperationParser$1.applyOrElse(Parsing.scala:624)
at io.getquill.quotation.Parsing$$anonfun$equalityOperationParser$1.applyOrElse(Parsing.scala:622)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$operationParser$1.applyOrElse(Parsing.scala:553)
at io.getquill.quotation.Parsing$$anonfun$operationParser$1.applyOrElse(Parsing.scala:552)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:61)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$Parser.apply(Parsing.scala:35)
at io.getquill.quotation.Parsing$$anonfun$queryParser$1.applyOrElse(Parsing.scala:202)
at io.getquill.quotation.Parsing$$anonfun$queryParser$1.applyOrElse(Parsing.scala:187)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:50)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$Parser.apply(Parsing.scala:35)
at io.getquill.quotation.Parsing$$anonfun$queryParser$1.applyOrElse(Parsing.scala:230)
at io.getquill.quotation.Parsing$$anonfun$queryParser$1.applyOrElse(Parsing.scala:187)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:50)
at io.getquill.quotation.Parsing$$anonfun$astParser$1.applyOrElse(Parsing.scala:48)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:338)
at scala.PartialFunction$Lifted.apply(PartialFunction.scala:334)
at io.getquill.quotation.Parsing$Parser.unapply(Parsing.scala:44)
at io.getquill.quotation.Parsing$Parser.apply(Parsing.scala:35)
at io.getquill.quotation.Quotation.$anonfun$quote$1(Quotation.scala:28)
at io.getquill.util.Interpolator$Traceable.andReturn(Interpolator.scala:145)
at io.getquill.quotation.Quotation.quote(Quotation.scala:28)
at io.getquill.quotation.Quotation.quote$(Quotation.scala:24)
at io.getquill.dsl.QuotationMacro.quote(QuotationDsl.scala:34)
at jdk.internal.reflect.GeneratedMethodAccessor61.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at scala.reflect.macros.runtime.JavaReflectionRuntimes$JavaReflectionResolvers.$anonfun$resolveJavaReflectionRuntime$5(JavaReflectionRuntimes.scala:45)
at scala.tools.nsc.typechecker.Macros.macroExpandWithRuntime(Macros.scala:770)
roleSchema.filter(r => RoleCode(r.code) == lift(roleCode)).take(1)
Edit 3 I suppose it's stated by design that we can't invoke methods from the query context.
The only solution that resolves most of the problems is changing domain model itself and using Value Classes as case class parameters for the User
and Role
As already answered in the comment, Quill will translate fields and case classes to the selected SQL dialect. There isn't a way to translate RoleCode(code)
to SQL. You should be able to make typedRole
and typedCode
a field of the case class and handle that through overriding apply
method for this particular case class.