Skip to content

Instantly share code, notes, and snippets.

@tkroman
Last active November 3, 2018 01:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkroman/911671a50c39ee0bcc39b888bdcad616 to your computer and use it in GitHub Desktop.
Save tkroman/911671a50c39ee0bcc39b888bdcad616 to your computer and use it in GitHub Desktop.
import AstElem._
class AsString extends QueryAstVisitor[String] {
override def visitFromTable(from: From.Table): String = {
s"FROM ${from.name}"
}
override def visitFromNested(from: From.NestedQueryTree): String = {
s"FROM (${visitTopLevelQuery(from.tree)})"
}
override def visitFromUnion(from: From.NestedUnion): String = {
s"FROM (${visitUnion(from.union)})"
}
override def visitFromJoin(from: From.NestedJoin): String = {
s"FROM (${visitJoin(from.join)})"
}
override def visitHaving(having: Having): String = {
val Having(cond) = having
s"HAVING ${visitCond(cond)}"
}
override def visitGroupBy(groupBy: GroupBy): String = {
val GroupBy(fs, h) = groupBy
val fields: String = fs.mkString(",")
val having: String = h.map(visitHaving).getOrElse("")
s"GROUP BY $fields $having"
}
override def visitWhere(where: Where): String = {
val Where(cond) = where
s"WHERE ${visitCond(cond)}"
}
override def visitSelect(select: Select): String = {
val Select(fs) = select
s"SELECT ${fs.mkString(",")}"
}
override def visitCondAnd(cond: Cond.And): String = {
val Cond.And(a, b) = cond
s"(${visitCond(a)} AND ${visitCond(b)}))"
}
override def visitCondOr(cond: Cond.Or): String = {
val Cond.Or(a, b) = cond
s"(${visitCond(a)} OR ${visitCond(b)}))"
}
override def visitCondIn(cond: Cond.In): String = {
val Cond.In(fields, constExpr) = cond
s"(${fields.mkString(",")} IN ${constExpr.mkString("(",",",")")})"
}
override def visitCondInNested(cond: Cond.InNested): String = {
val Cond.InNested(fields, in) = cond
s"(${fields.mkString(",")} IN ${visitTopLevelQuery(in)})"
}
override def visitCondFlat(cond: Cond.Flat): String = {
s"(${cond.expr})"
}
override def visitJoin(join: Join): String = {
val Join(l, r, using, mod) = join
val left: String = visitTopLevelQuery(l)
val right: String = visitTopLevelQuery(r)
val fields: String = using.mkString(",")
s"$left $mod JOIN ($right) USING $fields)"
}
override def visitUnion(union: Union): String = {
val Union(qs) = union
qs.map(visitTopLevelQuery).mkString(" UNION ALL ")
}
override def visitTopLevelQuery(topLevelQuery: TopLevelQuery): String = {
val TopLevelQuery(select, from, where, groupBy) = topLevelQuery
val sel: String = visitSelect(select)
val froms: String = visitFrom(from)
val wheres: String = where.map(visitWhere).getOrElse("")
val groupBys: String = groupBy.map(visitGroupBy).getOrElse("")
s"$sel $froms $wheres $groupBys"
}
}
sealed trait AstElem
object AstElem {
sealed trait From extends AstElem
object From {
case class Table(name: String) extends From
case class NestedQueryTree(tree: TopLevelQuery) extends From
case class NestedJoin(join: Join) extends From
case class NestedUnion(union: Union) extends From
}
case class Having(conditions: Cond) extends AstElem
case class GroupBy(fields: List[String],
having: Option[Having]) extends AstElem
case class Where(conditions: Cond) extends AstElem
case class Select(fields: List[String]) extends AstElem
sealed trait Cond extends AstElem {
def &(b: Cond): Cond = Cond.And(this, b)
def |(b: Cond): Cond = Cond.Or(this, b)
def &(b: Option[Cond]): Cond = {
b.map(c => Cond.And(this, c)).getOrElse(this)
}
def |(b: Option[Cond]): Cond = {
b.map(c => Cond.And(this, c)).getOrElse(this)
}
}
object Cond {
case class And(a: Cond, b: Cond) extends Cond
case class Or(a: Cond, b: Cond) extends Cond
case class Flat(expr: String) extends Cond
case class In(fields: List[String], constExpr: List[String]) extends Cond
case class InNested(fields: List[String],
in: TopLevelQuery) extends Cond
}
case class Join(left: TopLevelQuery,
right: TopLevelQuery,
using: List[String],
how: String = "ALL INNER") extends AstElem
case class Union(queries: List[TopLevelQuery]) extends AstElem
case class TopLevelQuery(select: Select,
from: From,
where: Option[Where],
groupBy: Option[GroupBy]) extends AstElem
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment