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

quasar.sql.ast.scala Maven / Gradle / Ivy

There is a newer version: 28.1.6
Show newest version
/*
 * Copyright 2014–2016 SlamData Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package quasar.sql

import quasar.Predef._
import quasar.fp._

import scala.Any

import matryoshka._
import monocle.macros.Lenses
import scalaz._, Scalaz._
import pathy.Path._

trait IsDistinct
final case object SelectDistinct extends IsDistinct
final case object SelectAll extends IsDistinct

@Lenses final case class Proj[A](expr: A, alias: Option[String])

sealed trait Sql[A]
object Sql {
  implicit val traverse: Traverse[Sql] = new Traverse[Sql] {
    def traverseImpl[G[_], A, B](
      fa: Sql[A])(
      f: A => G[B])(
      implicit G: Applicative[G]):
        G[Sql[B]] = {
      def traverseCase(c: Case[A]): G[Case[B]] =
        (f(c.cond) ⊛ f(c.expr))(Case(_, _))

      fa match {
        case Select(dist, proj, rel, filter, group, order) =>
          (proj.traverse(p => f(p.expr).map(Proj(_, p.alias))) ⊛
            rel.traverse(traverseRelation(_, f)) ⊛
            filter.traverse(f) ⊛
            group.traverse(g =>
              (g.keys.traverse(f) ⊛ g.having.traverse(f))(GroupBy(_, _))) ⊛
            order.traverse(_.keys.traverse(_.traverse(f)).map(OrderBy(_))))(
            Select(dist, _, _, _, _, _))
        case Vari(symbol) => vari(symbol).point[G]
        case SetLiteral(exprs) => exprs.traverse(f).map(setLiteral(_))
        case ArrayLiteral(exprs) => exprs.traverse(f).map(arrayLiteral(_))
        case MapLiteral(exprs) =>
          exprs.traverse(_.bitraverse(f, f)).map(mapLiteral(_))
        case Splice(expr) => expr.traverse(f).map(splice(_))
        case Binop(lhs, rhs, op) => (f(lhs) ⊛ f(rhs))(binop(_, _, op))
        case Unop(expr, op) => f(expr).map(unop(_, op))
        case Ident(name) => G.point(ident(name))
        case InvokeFunction(name, args) =>
          args.traverse(f).map(invokeFunction(name, _))
        case Match(expr, cases, default) =>
          (f(expr) ⊛ cases.traverse(traverseCase) ⊛ default.traverse(f))(
            matc(_, _, _))
        case Switch(cases, default) =>
          (cases.traverse(traverseCase) ⊛ default.traverse(f))(
            switch(_, _))
        case Let(name, form, body) => (f(form) ⊛ f(body))(Let(name, _, _))
        case IntLiteral(v) => intLiteral(v).point[G]
        case FloatLiteral(v) => floatLiteral(v).point[G]
        case StringLiteral(v) => stringLiteral(v).point[G]
        case NullLiteral() => nullLiteral().point[G]
        case BoolLiteral(v) => boolLiteral(v).point[G]
      }
    }
  }

  implicit def toExprAlgebraOps[A](a: Algebra[Sql, A]): AlgebraOps[Sql, A] =
    toAlgebraOps[Sql, A](a)
}

@Lenses final case class Select[A] private[sql] (
  isDistinct:  IsDistinct,
  projections: List[Proj[A]],
  relations:   Option[SqlRelation[A]],
  filter:      Option[A],
  groupBy:     Option[GroupBy[A]],
  orderBy:     Option[OrderBy[A]])
    extends Sql[A]
@Lenses final case class Vari[A] private[sql] (symbol: String) extends Sql[A]
@Lenses final case class SetLiteral[A] private[sql] (exprs: List[A]) extends Sql[A]
@Lenses final case class ArrayLiteral[A] private[sql] (exprs: List[A]) extends Sql[A]
/** Can’t be a Map, because we need to arbitrarily transform the key */
@Lenses final case class MapLiteral[A] private[sql] (exprs: List[(A, A)]) extends Sql[A]
/** Represents the wildcard in a select projection
  * For instance:
  *  "select foo.* from example" => ...(Splice(Some(Ident("foo"))))...
  *  "select * from example"     => ...(Splice(None))...
  */
@Lenses final case class Splice[A] private[sql] (expr: Option[A]) extends Sql[A]
@Lenses final case class Binop[A] private[sql] (lhs: A, rhs: A, op: BinaryOperator)
    extends Sql[A]
@Lenses final case class Unop[A] private[sql] (expr: A, op: UnaryOperator) extends Sql[A]
@Lenses final case class Ident[A] private[sql] (name: String) extends Sql[A]
@Lenses final case class InvokeFunction[A] private[sql] (name: String, args: List[A])
    extends Sql[A]
@Lenses final case class Match[A] private[sql] (expr: A, cases: List[Case[A]], default: Option[A])
    extends Sql[A]
@Lenses final case class Switch[A] private[sql] (cases: List[Case[A]], default: Option[A])
    extends Sql[A]
@Lenses final case class Let[A](name: String, form: A, body: A) extends Sql[A]
@Lenses final case class IntLiteral[A] private[sql] (v: Long) extends Sql[A]
@Lenses final case class FloatLiteral[A] private[sql] (v: Double) extends Sql[A]
@Lenses final case class StringLiteral[A] private[sql] (v: String) extends Sql[A]
@Lenses final case class NullLiteral[A] private[sql] () extends Sql[A]
@Lenses final case class BoolLiteral[A] private[sql] (value: Boolean) extends Sql[A]

sealed abstract class BinaryOperator(val sql: String) {
  def apply[A](lhs: A, rhs: A): Sql[A] = binop(lhs, rhs, this)

  val name = "(" + sql + ")"

  override def equals(that: Any) = that match {
    case x: BinaryOperator => sql == x.sql
    case _                 => false
  }

  override def hashCode = sql.hashCode

  override def toString = sql
}

final case object IfUndefined  extends BinaryOperator("??")
final case object Range        extends BinaryOperator("..")
final case object Or           extends BinaryOperator("or")
final case object And          extends BinaryOperator("and")
final case object Eq           extends BinaryOperator("=")
final case object Neq          extends BinaryOperator("<>")
final case object Ge           extends BinaryOperator(">=")
final case object Gt           extends BinaryOperator(">")
final case object Le           extends BinaryOperator("<=")
final case object Lt           extends BinaryOperator("<")
final case object Concat       extends BinaryOperator("||")
final case object Plus         extends BinaryOperator("+")
final case object Minus        extends BinaryOperator("-")
final case object Mult         extends BinaryOperator("*")
final case object Div          extends BinaryOperator("/")
final case object Mod          extends BinaryOperator("%")
final case object Pow          extends BinaryOperator("^")
final case object In           extends BinaryOperator("in")
final case object FieldDeref   extends BinaryOperator("{}")
final case object IndexDeref   extends BinaryOperator("[]")
final case object Limit        extends BinaryOperator("limit")
final case object Offset       extends BinaryOperator("offset")
final case object Union        extends BinaryOperator("union")
final case object UnionAll     extends BinaryOperator("union all")
final case object Intersect    extends BinaryOperator("intersect")
final case object IntersectAll extends BinaryOperator("intersect all")
final case object Except       extends BinaryOperator("except")

sealed abstract class UnaryOperator(val sql: String) {
  def apply[A](expr: A): Sql[A] = unop(expr, this)

  val name = sql

  override def equals(that: Any) = that match {
    case x: UnaryOperator => sql == x.sql
    case _                => false
  }

  override def hashCode = sql.hashCode

  override def toString = sql
}

final case object Not                 extends UnaryOperator("not")
final case object Exists              extends UnaryOperator("exists")
final case object Positive            extends UnaryOperator("+")
final case object Negative            extends UnaryOperator("-")
final case object Distinct            extends UnaryOperator("distinct")
final case object FlattenMapKeys      extends UnaryOperator("{*:}")
final case object FlattenMapValues    extends UnaryOperator("flatten_map")
final case object ShiftMapKeys        extends UnaryOperator("{_:}")
final case object ShiftMapValues      extends UnaryOperator("shift_map")
final case object UnshiftMap          extends UnaryOperator("{...}")
final case object FlattenArrayIndices extends UnaryOperator("[*:]")
final case object FlattenArrayValues  extends UnaryOperator("flatten_array")
final case object ShiftArrayIndices   extends UnaryOperator("[_:]")
final case object ShiftArrayValues    extends UnaryOperator("shift_array")
final case object UnshiftArray        extends UnaryOperator("[...]")

@Lenses final case class Case[A](cond: A, expr: A)

sealed trait SqlRelation[A] {
  def namedRelations: Map[String, List[NamedRelation[A]]] = {
    def collect(n: SqlRelation[A]): List[(String, NamedRelation[A])] =
      n match {
        case JoinRelation(left, right, _, _) => collect(left) ++ collect(right)
        case t: NamedRelation[A] => (t.aliasName -> t) :: Nil
    }

    collect(this).groupBy(_._1).mapValues(_.map(_._2))
  }

  def mapPathsM[F[_]: Monad](f: FUPath => F[FUPath]): F[SqlRelation[A]] = this match {
    case IdentRelationAST(_, _) => this.point[F]
    case TableRelationAST(path, alias) => f(path).map(TableRelationAST(_, alias))
    case rel @ JoinRelation(left, right, _, _) =>
      (left.mapPathsM(f) |@| right.mapPathsM(f))((l,r) => rel.copy(left = l, right = r))
    case ExprRelationAST(_,_) => this.point[F]
  }
}

sealed trait NamedRelation[A] extends SqlRelation[A] {
  def aliasName: String
}

/**
 * IdentRelationAST allows us to reference a let binding in relation (i.e. table)
 * context. ExprF.IdentF allows us to reference a let binding in expression context.
 * Ideally we can unify these two contexts, providing a single way to reference a
 * let binding.
 */
@Lenses final case class IdentRelationAST[A](name: String, alias: Option[String])
    extends NamedRelation[A] {
  def aliasName = alias.getOrElse(name)
}

@Lenses final case class TableRelationAST[A](tablePath: FUPath, alias: Option[String])
    extends NamedRelation[A] {
  def aliasName = alias.getOrElse(fileName(tablePath).value)
}
@Lenses final case class ExprRelationAST[A](expr: A, aliasName: String)
    extends NamedRelation[A]

@Lenses final case class JoinRelation[A](left: SqlRelation[A], right: SqlRelation[A], tpe: JoinType, clause: A)
    extends SqlRelation[A]

sealed abstract class JoinType(val sql: String)
final case object LeftJoin extends JoinType("left join")
final case object RightJoin extends JoinType("right join")
final case object InnerJoin extends JoinType("inner join")
final case object FullJoin extends JoinType("full join")

sealed trait OrderType
final case object ASC extends OrderType
final case object DESC extends OrderType

@Lenses final case class GroupBy[A](keys: List[A], having: Option[A])

@Lenses final case class OrderBy[A](keys: List[(OrderType, A)])




© 2015 - 2025 Weber Informatics LLC | Privacy Policy