Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listing columns on a Slick table

Tags:

scala

slick

I have a Slick 3.0 table definition similar to the following:

case class Simple(a: String, b: Int, c: Option[String])

trait Tables { this: JdbcDriver =>
  import api._

  class Simples(tag: Tag) extends Table[Simple](tag, "simples") {
    def a = column[String]("a")
    def b = column[Int]("b")
    def c = column[Option[String]]("c")

    def * = (a, b, c) <> (Simple.tupled, Simple.unapply)
  }

  lazy val simples = TableQuery[Simples]

} 

object DB extends Tables with MyJdbcDriver

I would like to be able to do 2 things:

  1. Get a list of the column names as Seq[String]
  2. For an instance of Simple, generate a Seq[String] that would correspond to how the data would be inserted into the database using a raw query (e.g. Simple("hello", 1, None) becomes Seq("'hello'", "1", "NULL"))

What would be the best way to do this using the Slick table definition?

like image 551
Dan Gallagher Avatar asked Sep 19 '15 21:09

Dan Gallagher


Video Answer


2 Answers

  • First of all it is not possible to trick Slick and change the order on the left side of the <> operator in the * method without changing the order of values in Simple, the row type of Simples, i.e. what Ben assumed is not possible. The ProvenShape return type of the * projection method ensures that there is a Shape available for translating between the Column-based type in * and the client-side type and if you write def * = (c, b, a) <> Simple.tupled, Simple.unapply) having Simple defined as case class Simple(a: String, b: Int, c: Option[String]), Slick will complain with an error "No matching Shape found. Slick does not know how to map the given types...". Ergo, you can iterate over all the elements of an instance of Simple with its productIterator.
  • Secondly, you already have the definition of the Simples table in your code and querying metatables to get the same information you already have is not sensible. You can get all you column names with a one-liner simples.baseTableRow.create_*.map(_.name). Note that the * projection of the table also defines the columns generated when you create the table schema. So the columns not mentioned in the projection are not created and the statement above is guaranteed to return exactly what you need and not to drop anything.

To recap briefly:

  1. To get a list of the column names of the Simples table as Seq[String] use simples.baseTableRow.create_*.map(_.name).toSeq
  2. To generate a Seq[String] that would correspond to how the data would be inserted into the database using a raw query for aSimple, an instance of Simple use aSimple.productIterator.toSeq
like image 188
Max Plevako Avatar answered Sep 28 '22 04:09

Max Plevako


To get the column names, try this:

db.run(for {
       metaTables <- slick.jdbc.meta.MTable.getTables("simples")
       columns <- metaTables.head.getColumns
       } yield columns.map {_.name}) foreach println

This will print

Vector(a, b, c)

And for the case class values, you can use productIterator:

Simple("hello", 1, None).productIterator.toVector

is

Vector(hello, 1, None)

You still have to do the value mapping, and guarantee that the order of the columns in the table and the values in the case class are the same.

like image 40
poroszd Avatar answered Sep 28 '22 04:09

poroszd