1

I'm having trouble figuring out how to update a column with type enumeration using play-slick.

Here's my enum and case class:

object TestStatus extends Enumeration {
  type TestStatus = Value
  val Status1 = Value("Status1")
}
case class Test (
  id: String,
  status: TestStatus
)

and the table mapping:

class Tests(tag: Tag) extends Table[Test](tag, "tests") {
  implicit val statusColumn = MappedColumnType.base[TestStatus, String](_.toString, TestStatus.withName)
  override def * = (id, status) <> ((Test.apply _).tupled, Test.unapply)
  val id = column[String]("id", 0.PrimaryKey)
  val status = column[TestStatus]("status")
}

when I try to go and update a Tests row, I get an error:

object TestQueries extends TableQuery[Tests](new Tests(_)) {
  def updateStatus(id: String, newStatus: TestStatus) = {
    TestQueries.filter(_.id === id).map(_.status).update(newStatus)
  }
}

[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection,
[error]  you use an unsupported type in a Query (e.g. scala List),
[error]  or you forgot to import a driver api into scope.
[error]   Required level: slick.lifted.FlatShapeLevel
[error]      Source type: slick.lifted.Rep[models.TestStatus.Value]
[error]    Unpacked type: T
[error]      Packed type: G
[error]     TestQueries.filter(_.id === id).map(_.status).update(newStatus)
[error]                                        ^

IntelliJ is showing that TestQueries.filter(_.id === id).map(_.status) has type Query[Nothing, Nothing, Seq], which makes me think the problem is with the specific column rather than with the update function.

Updating the id works fine using the same structure.

itadoatL
  • 63
  • 1
  • 5
  • It seems like only `Tests` class has implicit column mapping for `TestStatus`. Could you clarify whether `TestQueries` have proper column mapping in scope too? Just for sake of experiment try to duplicate `statusColumn` value inside of `TestQueries` and check if it compiles. – AdamBat Dec 23 '17 at 11:24
  • @AdamBat It is a bit unclear from my code, but I tried copying the column mapping to various scopes: `TestStatus` object level as well as `TestQueries` level, all with the same results. – itadoatL Dec 24 '17 at 17:43
  • Could you provide repository with replicated issue? I copied your code ad hoc and got same compiler error. Duplicating `TestStatus` column mapping in `TestQueries`, like I suggested, in the comment above, helped to resolve the problem. Here is my gist showing what I've done exactly: https://gist.github.com/Dasiu/ae4fa9bfa9077569e5e07a02aa1493f0 – AdamBat Jan 02 '18 at 21:11

1 Answers1

1

You need to define the custom column type of TestStatus.Value. This is how slick allows you to build a custom column type by mapping it to an already supported type:

implicit def testStatCT: BaseTypedType[TestStatus.Value] = 
  MappedColumnType.base[TestStatus.Value, String](
    enum => enum.toString, str => TestStatus.withName(str)
  )

this implicit needs to be imported wherever implicit resolutions such as the one in your example fail (or better yet defined in the TestStatus object so that it's always available) this way slick can have evidence that TestStatus.Value is a BaseTypedType which basically just means that something is a supported column type.

For further read on column mapping you can look at Slick Documentation

shayan
  • 1,211
  • 9
  • 12