Custom data type

This chapter describes how to use user-specific or unsupported types in table definitions built with ldbc.

Add a new column to the table definition created in setup.

ALTER TABLE user ADD COLUMN status BOOLEAN NOT NULL DEFAULT TRUE;

Encoder

In ldbc, the value to be passed to a statement is represented by an Encoder. The Encoder is a trait to represent the value to be bound to a statement.

By implementing Encoder, values passed to statements can be expressed as custom types.

Add Status to the user information to represent the user's status.

enum Status(val done: Boolean, val name: String):
  case Active   extends Status(false, "Active")
  case InActive extends Status(true, "InActive")

The following code example defines a custom type Encoder.

This allows binding custom types to statements.

given Encoder[Status] with
  override def encode(status: Status): Boolean = status.done

Custom types can be bound to statements just like any other parameter.

val program1: Executor[IO, Int] =
  sql"INSERT INTO user (name, email, status) VALUES (${ "user 1" }, ${ "user@example.com" }, ${ Status.Active })".update

Now you can bind custom types to statements.

Decoder

In addition to parameters, ldbc provides a Decoder to get a unique type from the execution result.

By implementing the Decoder, you can get your own type from the result of statement execution.

The following code example shows how to use Decoder.Elem to get a single data type.

given Decoder.Elem[Status] = Decoder.Elem.mapping[Boolean, Status] {
  case true  => Status.Active
  case false => Status.InActive
}
val program2: Executor[IO, (String, String, Status)] =
  sql"SELECT name, email, status FROM user WHERE id = 1".query[(String, String, Status)].unsafe

Now you can get a custom type from the execution result of a statement.