Skip to content

Instantly share code, notes, and snippets.

@lukaseder
Last active September 18, 2019 14:57
Show Gist options
  • Save lukaseder/ebc7c9d9a2eb178939b2149014e75581 to your computer and use it in GitHub Desktop.
Save lukaseder/ebc7c9d9a2eb178939b2149014e75581 to your computer and use it in GitHub Desktop.
A simple proof of concept for a new Query Object Model in Scala, using sealed traits and case classes
sealed trait Q {}
trait QField[T] extends Q {}
trait QTable extends Q {}
trait QSchema extends Q {}
trait QCondition extends Q {}
trait QName extends Q {}
trait QQuery extends Q {}
final case class CVal[T](value: T) extends QField[T]
final case class CIdent(name: String) extends QName
final case class CFieldRef[T](table: QTable, ident: CIdent) extends QField[T]
final case class CTableRef(schema: QSchema, ident: CIdent) extends QTable
final case class CJoin(lhs: QTable, rhs: QTable, on: QCondition) extends QTable
final case class CSchemaRef(ident: CIdent) extends QSchema
final case class CEq[T](lhs: QField[T], rhs: QField[T]) extends QCondition
final case class CIn[T](lhs: QField[T], rhs: List[QField[T]]) extends QCondition
final case class CAnd(lhs: QCondition, rhs: QCondition) extends QCondition
final case class CSelect(select: List[QField[_]], from: QTable, where: QCondition) extends QQuery
object QOM {
val st = CTableRef(CSchemaRef(CIdent("S")), CIdent("T"))
val stc = CFieldRef[Int](st, CIdent("C"))
val su = CTableRef(CSchemaRef(CIdent("S")), CIdent("U"))
val suc = CFieldRef[Int](su, CIdent("C"))
def main(args: Array[String]): Unit = {
val list = List(
CSelect(
select = List(stc, suc),
from = CJoin(st, su, CEq(stc, suc)),
where = CAnd(
CEq(stc, CVal(2)),
CEq(suc, CVal(2))
)
)
)
list.foreach(println)
println
list.map(render).foreach(println)
println
list.map(map).foreach(println)
println
list.map(map).map(render).foreach(println)
println
}
def map[T <: Q](q: T): T = {
q match {
case CVal(value) => CVal(value).asInstanceOf[T]
case CIdent(name) => CIdent(name).asInstanceOf[T]
case CFieldRef(table, ident) => CFieldRef(map(table), map(ident)).asInstanceOf[T]
case CTableRef(schema, ident) =>
CTableRef(
map(schema),
if (ident.name == "T") CIdent("X") else ident
).asInstanceOf[T]
case CJoin(lhs, rhs, on) => CJoin(map(lhs), map(rhs), map(on)).asInstanceOf[T]
case CSchemaRef(ident) => CSchemaRef(map(ident)).asInstanceOf[T]
case CEq(lhs, rhs) => CEq(map(lhs), map(rhs)).asInstanceOf[T]
case CAnd(lhs, rhs) => CAnd(map(lhs), map(rhs)).asInstanceOf[T]
case CSelect(select, from, where) =>
CSelect(
select.map(map),
map(from),
if (contains(from, su))
CAnd(map(where), CIn(suc, List(CVal(1), CVal(2), CVal(3))))
else
map(where)
).asInstanceOf[T]
}
}
def contains(q: QTable, t: CTableRef): Boolean = {
q match {
case CTableRef(schema, ident) => t.schema == schema && t.ident == ident
case CJoin(lhs, rhs, _) => contains(lhs, t) || contains(rhs, t)
case _ => false
}
}
def render(q: Q): String = render(q, StringBuilder.newBuilder).toString
def render(q: Q, sb: StringBuilder): StringBuilder = {
q match {
case CVal(value) => sb.append(value)
case CIdent(name) => sb.append(name)
case CFieldRef(table, ident) => {
render(table, sb)
sb.append(".")
render(ident, sb)
}
case CTableRef(schema, ident) => {
render(schema, sb)
sb.append(".")
render(ident, sb)
}
case CJoin(lhs, rhs, on) => {
render(lhs, sb)
sb.append(" JOIN ")
render(rhs, sb)
sb.append(" ON ")
render(on, sb);
}
case CSchemaRef(ident) => render(ident, sb)
case CEq(lhs, rhs) => {
render(lhs, sb)
sb.append(" = ")
render(rhs, sb)
}
case CIn(lhs, rhs) => {
render(lhs, sb)
sb.append(" IN (")
rhs.zipWithIndex.foreach(t => {
if (t._2 > 0)
sb.append(", ")
render(t._1, sb);
})
sb.append(")")
}
case CAnd(lhs, rhs) => {
render(lhs, sb)
sb.append(" AND ")
render(rhs, sb)
}
case CSelect(select, from, where) => {
sb.append("SELECT ")
select.zipWithIndex.foreach(t => {
if (t._2 > 0)
sb.append(", ")
render(t._1, sb);
})
sb.append(" FROM ")
render(from, sb);
sb.append(" WHERE ")
render(where, sb);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment