Question

I have a database table in Scala Playframework defined as

CREATE TABLE account (
    id     SERIAL,
    email  TEXT  NOT NULL,
    buffer BYTEA NOT NULL,
    PRIMARY KEY (id)
);

I am using a protocol buffer to serialize an object to a byte array with the following code

DB.withConnection{ implicit c=>
  SQL("INSERT INTO device (buffer,secret) VALUES ({secret},{buffer})").on(
    "secret"->device.getSecret(),
    "buffer"->device.toByteArray()
  ).executeInsert()
}

The type returned by device.toByteArray() is Array[Byte] which should match the database type for that column. However upon executing the code I get

play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[PSQLException: ERROR: column "buffer" is of type bytea but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 44]]
    at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:134) [play_2.9.1.jar:2.0.3]
    at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:115) [play_2.9.1.jar:2.0.3]
    at akka.actor.Actor$class.apply(Actor.scala:318) [akka-actor.jar:2.0.2]
    at play.core.ActionInvoker.apply(Invoker.scala:113) [play_2.9.1.jar:2.0.3]
    at akka.actor.ActorCell.invoke(ActorCell.scala:626) [akka-actor.jar:2.0.2]
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:197) [akka-actor.jar:2.0.2]
Caused by: org.postgresql.util.PSQLException: ERROR: column "buffer" is of type bytea but expression is of type character varying
Was it helpful?

Solution

Looking at the anorm source and the Postgres docs, it looks like you'll need to add a handler for storing Array[Byte] properly using setBytes instead of setObject, which the current code calls for.

I'd extend the match in anyParameter in the framework to read

    value match {
      case Some(bd: java.math.BigDecimal) => stmt.setBigDecimal(index, bd)
      case Some(b: Array[Byte]) => stmt.setBytes(index, b)
      case Some(o) => stmt.setObject(index, o)
      // ...

and add

implicit val byteArrayToStatement = new ToStatement[Array[Byte]] {
  def set(s: java.sql.PreparedStatement, index: Int, aValue: Array[Byte]): Unit = setAny(index, aValue, s)
}

You should potentially be able to do this outside the framework as well through the type class magic at work here, but I don't have time to figure that out right now.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top