8

Let's say I have a table:

object Suppliers extends Table[(Int, String, String, String)]("SUPPLIERS") {
  def id = column[Int]("SUP_ID", O.PrimaryKey)
  def name = column[String]("SUP_NAME")
  def state = column[String]("STATE")
  def zip = column[String]("ZIP")
  def * = id ~ name ~ state ~ zip
}

Table's database name

The table's database name can be accessed by going: Suppliers.tableName
This is supported by the Scaladoc on AbstractTable.

For example, the above table's database name is "SUPPLIERS".

Columns' database names

Looking through AbstractTable, getLinearizedNodes and indexes looked promising. No column names in their string representations though.

I assume that * means "all the columns I'm usually interested in." * is a MappedProjection, which has this signature:

final case class MappedProjection[T, P <: Product](
  child: Node, 
  f: (P) ⇒ T, 
  g: (T) ⇒ Option[P])(proj: Projection[P]) 
extends ColumnBase[T] with UnaryNode with Product with Serializable

*.getLinearizedNodes contains a huge sequence of numbers, and I realized that at this point I'm just doing a brute force inspection of everything in the API for possibly finding the column names in the String.

Has anybody also encountered this problem before, or could anybody give me a better understanding of how MappedProjection works?

Community
  • 1
  • 1
Meredith
  • 3,928
  • 4
  • 33
  • 58

3 Answers3

3

It requires you to rely on Slick internals, which may change between versions, but it is possible. Here is how it works for Slick 1.0.1: You have to go via the FieldSymbol. Then you can extract the information you want like how columnInfo(driver: JdbcDriver, column: FieldSymbol): ColumnInfo does it.

To get a FieldSymbol from a Column you can use fieldSym(node: Node): Option[FieldSymbol] and fieldSym(column: Column[_]): FieldSymbol.

Meredith
  • 3,928
  • 4
  • 33
  • 58
cvogt
  • 11,260
  • 30
  • 46
  • How can I get a `Column` from a `ColumnBase` (thinking of `*` here)? Or from an `AbstractTable` or `Node`? – Meredith Nov 25 '13 at 22:43
  • Column is a subclass of ColumnBase. Not sure what you are trying to do. As it requires Slick internals this will require digging into Slick yourself (using the source code) and it will not be a stable API. – cvogt Nov 26 '13 at 02:42
  • Was trying to get the types of every Column in a table, given the original table and not the cloned table. Only succeeded in getting a Seq[Node] from which I was able to extract FieldSymbols. Wasn't sure of how to extract the type of out the TypeMapper included in a FieldSymbol, and didn't want to continue pursuing reflection as an option. The type would be used to infer the default filter operator for the column given an index that mapped to a column and a value to compare to. In other words, generalize calls to query.filter() – Meredith Nov 26 '13 at 11:04
2

To get the (qualified) column names you can simply do the following:

Suppliers.id.toString
Suppliers.name.toString
Suppliers.state.toString
Suppliers.zip.toString

It's not explicitly stated anywhere that the toString will yield the column name, so your question is a valid one.


Now, if you want to programmatically get all the column names, then that's a bit harder. You could try using reflection to get all the methods that return a Column[_] and call toString on them, but it wouldn't be elegant. Or you could hack a bit and get a select * SQL statement from a query like this:

val selectStatement = DB withSession {
  Query(Suppliers).selectStatement
}

And then parse our the column names.

This is the best I could do. If someone knows a better way then please share - I'm interested too ;)

Akos Krivachy
  • 4,915
  • 21
  • 28
  • 1
    Thought @krivachy.akos would like to know another answer popped up, from the developer of Slick no less – Meredith Nov 24 '13 at 14:11
2

Code is based on Lightbend Activator "slick-http-app".

slick version: 3.1.1

Added this method to the BaseDal:

def getColumns(): mutable.Map[String, Type] = {
  val columns = mutable.Map.empty[String, Type]

  def selectType(t: Any): Option[Any] = t match {
    case t: TableExpansion => Some(t.columns)
    case t: Select => Some(t.field)
    case _ => None
  }
  def selectArray(t:Any): Option[ConstArray[Node]] = t match {
    case t: TypeMapping => Some(t.child.children)
    case _ => None
  }
  def selectFieldSymbol(t:Any): Option[FieldSymbol] = t match {
    case t: FieldSymbol => Some(t)
    case _ => None
  }

  val t = selectType(tableQ.toNode)
  val c = selectArray(t.get)

  for (se <- c.get) {
    val col = selectType(se)
    val fs = selectFieldSymbol(col.get)
    columns += (fs.get.name -> fs.get.tpe)
  }
  columns
}

this method gets the column names (real names in DB) + types form the TableQ

used imports are:

     import slick.ast._
     import slick.util.ConstArray
Hans Schreuder
  • 745
  • 5
  • 10