All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.spark.connector.util.CqlWhereParser.scala Maven / Gradle / Ivy

The newest version!
package com.datastax.spark.connector.util

import java.util.UUID
import scala.util.parsing.combinator.RegexParsers

object CqlWhereParser extends RegexParsers with Logging {

  sealed trait RelationalOperator
  case object EqualTo extends RelationalOperator
  case object LowerThan extends RelationalOperator
  case object LowerEqual extends RelationalOperator
  case object GreaterThan extends RelationalOperator
  case object GreaterEqual extends RelationalOperator
  case object In extends RelationalOperator

  sealed trait Value
  case object Placeholder extends Value

  sealed trait Literal extends Value { def value: Any }
  case class StringLiteral(value: String) extends Literal
  case class NumberLiteral(value: String) extends Literal
  case class BooleanLiteral(value: Boolean) extends Literal
  case class UUIDLiteral(value: UUID) extends Literal

  case class ValueList(values: Value*)

  case class Identifier(name: String)

  sealed trait Predicate
  sealed trait SingleColumnPredicate extends Predicate {
    def columnName: String
  }
  case class InPredicate(columnName: String) extends SingleColumnPredicate
  case class InListPredicate(columnName: String, values: ValueList) extends SingleColumnPredicate
  case class EqPredicate(columnName: String, value: Value) extends SingleColumnPredicate
  case class RangePredicate(columnName: String, operator: RelationalOperator, value: Value) extends SingleColumnPredicate
  case class UnknownPredicate(text: String) extends Predicate

  private def unquotedIdentifier = """[_\p{L}][_\p{L}\p{Nd}]*""".r ^^ {
    id => Identifier(id.toLowerCase)
  }

  private def quotedIdentifier = "\"" ~> "(\"\"|[^\"])*".r <~ "\"" ^^ {
    def unEscapeQuotes(s: String) = s.replace("\"\"", "\"")
    id => Identifier(unEscapeQuotes(id.toString))
  }

  private def identifier = unquotedIdentifier | quotedIdentifier

  private def num = """-?\d+(\.\d*)?([eE][-\+]?\d+)?""".r ^^ NumberLiteral.apply

  private def bool = ("true" | "false") ^^ {
    s => BooleanLiteral(s.toBoolean)
  }

  private def str = "'" ~> """(''|[^'])*+""".r <~ "'" ^^ {
    def unEscapeQuotes(s: String) = s.replace("''", "'")
    s => StringLiteral(unEscapeQuotes(s))
  }

  private def uuid = """[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}""".r ^^ {
    s => UUIDLiteral(UUID.fromString(s))
  }

  private def placeholder = "?" ^^ (_ => Placeholder)

  private def value: Parser[Value] = placeholder | uuid | num | bool | str

  private def relationalOperator: Parser[RelationalOperator] =
    "<="       ^^ (_ => LowerEqual) |
    ">="       ^^ (_ => GreaterEqual) |
    "<"        ^^ (_ => LowerThan) |
    ">"        ^^ (_ => GreaterThan) |
    "="        ^^ (_ => EqualTo) |
    "(?i)in".r ^^ (_ => In)

  private def valueList: Parser[ValueList] = "(" ~> value ~ rep("," ~> value) <~ ")" ^^ {
    case literal ~ list => ValueList(literal :: list :_*)
  }

  private def and = "(?i)and".r ^^ {_ => "and" }

  private def predicate: Parser[Predicate] = ((identifier ~ relationalOperator ~ (value | valueList)) | ".*".r) ^^ {
    case Identifier(name) ~ In ~ Placeholder                          => InPredicate(name)
    case Identifier(name) ~ In ~ (list : ValueList)                   => InListPredicate(name, list)
    case Identifier(name) ~ EqualTo ~ (v: Value)                      => EqPredicate(name, v)
    case Identifier(name) ~ (op: RelationalOperator) ~ (param: Value) => RangePredicate(name, op, param)
    case other                                                        => UnknownPredicate(other.toString)
  }

  private def whereClause = predicate ~ rep(and ~> predicate) ^^ {
    case expr ~ list => expr :: list
  }

  def parse(cqlWhere: String): Seq[Predicate] = {
    parseAll(whereClause, cqlWhere) match {
      case Success(columns, _) => columns
      case x => logError("Parse error when parsing CQL WHERE clause:" + x.toString); List()
    }
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy