Skip to content

Instantly share code, notes, and snippets.

@karthik20522
Created May 25, 2019 06:54
Show Gist options
  • Save karthik20522/5c03b99e95cddc616fa0d53d9f3a3f14 to your computer and use it in GitHub Desktop.
Save karthik20522/5c03b99e95cddc616fa0d53d9f3a3f14 to your computer and use it in GitHub Desktop.
Scala Query Parser
//case classes: https://github.com/karthik20522/ESQL/blob/master/src/main/scala/com/karthik/esql/sql/QueryBuilder.scala
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._
case class Query(
val operation: Operation,
val from: From,
val where: Option[Where],
val order: Option[Direction] = None,
val limit: Option[Limit] = None
)
class SQLParser extends JavaTokenParsers {
def query: Parser[Query] = (selectAll | count | defaultSelect) ~ from ~ opt(where) ~ opt(order) ~ opt(limit) ^^ {
case operation ~ from ~ where ~ order ~ limit => Query(operation, from, where, order, limit)
}
def selectAll: Parser[Operation] = "select" ~ "*" ^^^ (Select("*"))
def defaultSelect: Parser[Operation] = "select" ~ repsep(ident, ",") ^^ {
case "select" ~ f => Select(f: _*)
case _ => throw new IllegalArgumentException("Operation not implemented")
}
def limit: Parser[Limit] = "limit" ~> wholeNumber ^^ (f => Limit(Integer.parseInt(f)))
def count: Parser[Count] = "select" ~ "count" ~> "(" ~> ident <~ ")" ^^ { case exp => Count(exp) }
def from: Parser[From] = "from" ~> ident ^^ (From(_))
def where: Parser[Where] = "where" ~> rep(clause) ^^ (Where(_: _*))
def clause: Parser[Clause] = (predicate | parens) * (
"and" ^^^ { (a: Clause, b: Clause) => And(a, b) } |
"or" ^^^ { (a: Clause, b: Clause) => Or(a, b) })
def parens: Parser[Clause] = "(" ~> clause <~ ")"
def predicate = (
ident ~ "=" ~ boolean ^^ { case f ~ "=" ~ b => BooleanEquals(f, b) }
| ident ~ "=" ~ stringLiteral ^^ { case f ~ "=" ~ v => StringEquals(f, stripQuotes(v)) }
| ident ~ ("like" | "LIKE") ~ stringLiteral ^^ { case f ~ ("like" | "LIKE") ~ v => Like(f, stripQuotes(v)) }
| ident ~ "=" ~ wholeNumber ^^ { case f ~ "=" ~ i => NumberEquals(f, i.toInt) }
| ident ~ "<" ~ wholeNumber ^^ { case f ~ "<" ~ i => LessThan(f, i.toInt) }
| ident ~ "<=" ~ wholeNumber ^^ { case f ~ "<=" ~ i => LessThanEquals(f, i.toInt) }
| ident ~ ">" ~ wholeNumber ^^ { case f ~ ">" ~ i => GreaterThan(f, i.toInt) }
| ident ~ ">=" ~ wholeNumber ^^ { case f ~ ">=" ~ i => GreaterThanEquals(f, i.toInt) })
def boolean = ("true" ^^^ (true) | "false" ^^^ (false))
def order: Parser[Direction] = {
("order" ~> "by" ~> ident ~ ("asc" | "desc") ^^ {
case f ~ "asc" => Asc(f)
case f ~ "desc" => Desc(f)
}) | ("order" ~> "by" ~> repsep(ident, ",") ~ ("asc" | "desc") ^^ {
case f ~ "asc" => Asc(f: _*)
case f ~ "desc" => Desc(f: _*)
})
}
def stripQuotes(s: String) = s.substring(1, s.length - 1)
def parse(sql: String): Option[Query] = {
parseAll(query, sql.replace(".", "__")) match {
case Success(r, q) => Option(r)
case x => println(x); None
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment