zio-protoquill
zio-protoquill copied to clipboard
fail to build a query when lifting a scala3 enum with a Decoder
Version: 4.6.0
Module: quill-sql
Database: sqlite
Actual behavior
Scala 3 enum
with a Decoder fails to build query
Models :
enum PostState {
case DRAFT
case LIVE
}
case class Post(
title: String,
state: PostState
)
Quill context :
case class Context(override val ds: DataSource) extends Quill.Sqlite(Literal, ds) {
given Encoder[PostState] = encoder(Types.VARCHAR, (index, value, row) => row.setString(index, value.toString))
given Decoder[PostState] = decoder((index, row, _) => PostState.valueOf(row.getString(index)))
inline given SchemaMeta[Post] = schemaMeta("post")
inline def posts = query[Post]
}
Repository :
final case class Live(ctx: Context) extends PostRepository {
import ctx.*
import ctx.given
override def byString(title: Option[String]): Task[List[Post]] = {
run(posts.filter(p => lift(title).forall(s => s == p.title)))
// Quill Query: SELECT p.title, p.state FROM post p WHERE ? IS NULL OR ? = p.title
}
override def byEnum(state: Option[PostState]): Task[List[Post]] = {
run(posts.filter(p => lift(state).forall(s => s == p.state)))
// ERROR:
// The Co-Product element PostState.DRAFT was not a Case Class or Value Type. Value-level Co-Products are not supported. Please write a decoder for it's parent-type PostState.
}
}
Steps to reproduce the behavior
Minimal code to reproduce is available at https://github.com/bicouy0/zio-protoquill-enum-query-fail
Workaround
Unknown
@getquill/maintainers
I just ran into the same thing, turned out it I didn't import ctx.*
in my repository. In my case the encoding / decoding worked by just having a given MappedEncoding
in both directions and an imported context.
Same issue here 🤔
Explained in Discord: https://discord.com/channels/629491597070827530/821565909139062845/1246119588408725614
I manage to make my query compile by changing it as following:
// before - doesn't compile
inline def insertOrganizationMembers(members: Iterable[(UserUuid, OrgUuid, OrgRoleDB)]) = {
quote {
liftQuery(members).foreach { case (userUuid, orgUuid, role) =>
organizations_members
.insert(
_.user_uuid_fk -> userUuid,
_.org_uuid_fk -> orgUuid,
_.role -> role,
)
.onConflictIgnore
}
}
}
// after - does compile
inline def insertOrganizationMembers(members: Iterable[(UserUuid, OrgUuid, OrgRoleDB)]) = {
quote {
liftQuery(members).foreach { t =>
organizations_members
.insert(
_.user_uuid_fk -> t._1,
_.org_uuid_fk -> t._2,
_.role -> t._3,
)
.onConflictIgnore
}
}
}
Perhaps unrelated, but I had a problem today where my custom encoder was not used unless it was named (ie given myEncoder: Encoder[PostState]... vs given Encoder[PostState]), and it was imported by name as well at the call site.
Edit: Tried this in the example code, and it didn't help