Skip to content

Commit

Permalink
groupMap
Browse files Browse the repository at this point in the history
  • Loading branch information
KuceraMartin committed Apr 2, 2023
1 parent 3bb4891 commit 2cc26d5
Show file tree
Hide file tree
Showing 13 changed files with 543 additions and 168 deletions.
188 changes: 133 additions & 55 deletions src/main/Expression.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
package tyqu

import scala.annotation.targetName

import utils.IsTupleOf
import Tuple.Fold


type Numeric = Int | Long | Float | Double
type Primitive = Numeric | String | Char | Boolean

type LogicalAnd[A, B] <: Boolean = (A, B) match
case (true, true) => true
case _ => false

type ForAll[T <: Tuple, Pred[X] <: Boolean] <: Boolean = T match
case EmptyTuple => true
case h *: t => LogicalAnd[Pred[h], ForAll[t, Pred]]

type CanSelectExpr[T] <: Boolean = T match
case Expression[?, true] => true
case Expression[?, false] => false
case _ => Nothing

type ArgsCanSelect[T] <: Boolean = T match
case Tuple => ForAll[T, CanSelectExpr]
case _ => CanSelectExpr[T]


sealed abstract class Relation:
def underlyingName: String
Expand All @@ -17,113 +39,169 @@ abstract class TableRelation[T <: Table](val table: T) extends Relation:
def underlyingName: String = table.tableName
def getColumnName(property: String) = table.getColumnName(property)
def colToExpr(col: Column[?]) = table.colToExpr(col)(this.asInstanceOf[TableRelation[table.type]])
def pk: NamedExpression[?, ?] = colToExpr(table.pk)
def pk: NamedExpression[?, ?, ?] = colToExpr(table.pk)

enum JoinType:
case Inner, Left, Right, FullOuter

case class FromRelation[T <: Table](t: T) extends TableRelation(t)

case class JoinRelation[T <: Table](t: T, joinType: JoinType, on: JoinRelation[T] => Expression[Boolean]) extends TableRelation(t)
case class JoinRelation[T <: Table](t: T, joinType: JoinType, on: JoinRelation[T] => Expression[Boolean, ?]) extends TableRelation(t)

case class SubqueryRelation(qb: QueryBuilder[?]) extends Relation:
def underlyingName: String = qb.from.underlyingName
def getColumnName(property: String) = qb.from.getColumnName(property)


abstract sealed class Expression[T]:

def as(n: String) = Alias[T, n.type](n, this)
abstract sealed class Expression[T, CanSelect <: Boolean]:

def asc = Asc(this)
def desc = Desc(this)
def as(n: String) = Alias[T, CanSelect, n.type](n, this)

infix def ===[T2 <: T | Null](rhs: Expression[T2]) = Function[Boolean]("=", List(this, rhs))
infix def =!=[T2 <: T | Null](rhs: Expression[T2]) = Function[Boolean]("!=", List(this, rhs))
infix def ===[T2 <: T | Null, E <: Expression[T2, ?]](rhs: E) = Function[Boolean]("=", (this, rhs))
infix def =!=[T2 <: T | Null, E <: Expression[T2, ?]](rhs: E) = Function[Boolean]("!=", (this, rhs))

def concat(rhs: Expression[?]) =
Function[String]("CONCAT", List(this, rhs).flatMap{
case Function("CONCAT", exprs) => exprs
case expr => List(expr)
})
def concat[E <: Expression[?, ?]](rhs: E) =
Concat(this, rhs)

def count = Function[Int]("COUNT", List(this))
def count = Aggregation[Int]("COUNT", this)

end Expression

type AnyExpression = Expression[?, ?]

abstract sealed class NamedExpression[T, N <: String & Singleton](val alias: N) extends Expression[T]
abstract sealed class NamedExpression[T, CanSelect <: Boolean, N <: String & Singleton](val alias: N) extends Expression[T, CanSelect]

case class Alias[T, N <: String & Singleton](name: N, expression: Expression[T]) extends NamedExpression[T, N](name)
case class Alias[T, CanSelect <: Boolean, N <: String & Singleton](name: N, expression: Expression[T, CanSelect]) extends NamedExpression[T, CanSelect, N](name)

case class ColumnValue[T, N <: String & Singleton](name: N, relation: Relation) extends NamedExpression[T, N](name)
case class ColumnValue[T, CanSelect <: Boolean, N <: String & Singleton](name: N, relation: Relation) extends NamedExpression[T, CanSelect, N](name)

// E because the type in QueryBuilder is invariant
case class SubqueryExpression[T, E <: Expression[T]](qb: QueryBuilder[E]) extends Expression[T]
case class SubqueryExpression[T, E <: Expression[T, true]](qb: QueryBuilder[E]) extends Expression[T, true]

case class LiteralExpression[T](value: T, static: Boolean = false) extends Expression[T, true]

abstract class ProductExpression[T, Arguments <: Tuple | Expression[?, ?]] extends Expression[T, ArgsCanSelect[Arguments]]

case class Function[T, Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) extends ProductExpression[T, Arguments]

object Function:
final class ApplyHelper[T]:
def apply[Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) = new Function[T, Arguments](name, arguments)

def apply[E <: Expression[?, ?]](name: String, argument: E) = new Function[T, Tuple1[E]](name, Tuple1(argument))

def apply[T] = ApplyHelper[T]()

case class LiteralExpression[T](value: T, static: Boolean = false) extends Expression[T]
case class Aggregation[T, Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) extends Expression[T | Null, true]

case class Function[T](name: String, arguments: List[Expression[?]]) extends Expression[T]
object Aggregation:
final class ApplyHelper[T]:
def apply[Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) = new Aggregation[T, Arguments](name, arguments)

def apply[E <: Expression[?, ?]](name: String, argument: E) = new Aggregation[T, Tuple1[E]](name, Tuple1(argument))

def apply[T] = ApplyHelper[T]()
end Aggregation

abstract class UnaryNumericAggregation(name: String):
def apply[T <: Numeric | Null](e: Expression[T, ?]) = Aggregation[T | Null](name, Tuple(e))
def unapply(a: Aggregation[?, ?]): Option[AnyExpression] =
if a.name == name then Some(a)
else None
end UnaryNumericAggregation

object Sum extends UnaryNumericAggregation("SUM")
object Avg extends UnaryNumericAggregation("AVG")
object Min extends UnaryNumericAggregation("MIN")
object Max extends UnaryNumericAggregation("MAX")

def lit[T](value: T) = LiteralExpression(value)

abstract class BinaryFunction[T](name: String):
def apply[E1 <: Expression[?, ?], E2 <: Expression[?, ?]](a: E1, b: E2) =
Function[T](name, (a, b))

def unapply(f: Function[?, ?]): Option[(AnyExpression, AnyExpression)] =
if f.name == name then Some(f.arguments.asInstanceOf[(AnyExpression, AnyExpression)])
else None

object Concat extends BinaryFunction[String]("CONCAT")

case class And(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]
case class Or(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]
case class Not(expression: Expression[Boolean]) extends Expression[Boolean]
case class IsNull[T](expression: Expression[T | Null]) extends Expression[Boolean]
case class IsNotNull[T](expression: Expression[T | Null]) extends Expression[Boolean]
case class Exists(subquery: SubqueryExpression[?, ?]) extends Expression[Boolean]
case class StartsWith(needle: String, haystack: Expression[String]) extends Expression[Boolean]
case class EndsWith(needle: String, haystack: Expression[String]) extends Expression[Boolean]
case class Contains(needle: String, haystack: Expression[String]) extends Expression[Boolean]
case class And[L <: Expression[Boolean, ?], R <: Expression[Boolean, ?]](lhs: L, rhs: R) extends ProductExpression[Boolean, (L, R)]
case class Or[L <: Expression[Boolean, ?], R <: Expression[Boolean, ?]](lhs: L, rhs: R) extends ProductExpression[Boolean, (L, R)]
case class Not[E <: Expression[Boolean, ?]](expression: E) extends ProductExpression[Boolean, Tuple1[E]]
case class IsNull[T, S <: Boolean, E <: Expression[T | Null, S]](expression: E) extends ProductExpression[Boolean, Tuple1[E]]
case class IsNotNull[T, E <: Expression[T | Null, ?]](expression: E) extends ProductExpression[Boolean, E]
case class Exists(subquery: SubqueryExpression[?, ?]) extends Expression[Boolean, true]
case class StartsWith[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]
case class EndsWith[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]
case class Contains[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]

case class CountAll() extends Expression[Int]
case class CountAll() extends Expression[Int, true]

case class Plus[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
case class Minus[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
case class Multiply[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
case class Divide[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
case class Plus[T1 <: Numeric | Null, T2 <: Numeric | Null, E1 <: Expression[T1, ?], E2 <: Expression[T2, ?]](lhs: E1, rhs: E2) extends ProductExpression[T1 | T2, (E1, E2)]
case class Minus[T1 <: Numeric | Null, T2 <: Numeric | Null, E1 <: Expression[T1, ?], E2 <: Expression[T2, ?]](lhs: E1, rhs: E2) extends ProductExpression[T1 | T2, (E1, E2)]
case class Multiply[T1 <: Numeric | Null, T2 <: Numeric | Null, E1 <: Expression[T1, ?], E2 <: Expression[T2, ?]](lhs: E1, rhs: E2) extends ProductExpression[T1 | T2, (E1, E2)]
case class Divide[T1 <: Numeric | Null, T2 <: Numeric | Null, E1 <: Expression[T1, ?], E2 <: Expression[T2, ?]](lhs: E1, rhs: E2) extends ProductExpression[T1 | T2, (E1, E2)]


extension (lhs: Expression[Boolean])
infix def &&(rhs: Expression[Boolean]) =
extension [S <: Boolean] (lhs: Expression[Boolean, S])
infix def &&[S2 <: Boolean](rhs: Expression[Boolean, S2]) =
if (lhs == NoFilterExpression) rhs
else And(lhs, rhs)
infix def ||(rhs: Expression[Boolean]) = Or(lhs, rhs)
infix def ||[S2 <: Boolean](rhs: Expression[Boolean, S2]) = Or(lhs, rhs)
infix def unary_! = Not(lhs)


extension [T <: Numeric | Null] (lhs: Expression[T])
infix def <[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean]("<", List(lhs, rhs))
infix def <=[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean]("<=", List(lhs, rhs))
infix def >[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean](">", List(lhs, rhs))
infix def >=[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean](">=", List(lhs, rhs))
infix def +[T2 <: Numeric | Null](rhs: Expression[T2]) = Plus(lhs, rhs)
infix def -[T2 <: Numeric | Null](rhs: Expression[T2]) = Minus(lhs, rhs)
infix def *[T2 <: Numeric | Null](rhs: Expression[T2]) = Multiply(lhs, rhs)
infix def /[T2 <: Numeric | Null](rhs: Expression[T2]) = Divide(lhs, rhs)
extension [T <: Numeric | Null, S <: Boolean] (lhs: Expression[T, S])
infix def <[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean]("<", (lhs, rhs))
infix def <=[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean]("<=", (lhs, rhs))
infix def >[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean, (Expression[T, S], Expression[T2, S2])](">", (lhs, rhs))
infix def >=[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean](">=", (lhs, rhs))
infix def +[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Plus(lhs, rhs)
infix def -[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Minus(lhs, rhs)
infix def *[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Multiply(lhs, rhs)
infix def /[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Divide(lhs, rhs)


extension [T] (lhs: Expression[T | Null])
extension [T, S <: Boolean] (lhs: Expression[T | Null, S])
def isNull = IsNull(lhs)
def isNotNull = IsNotNull(lhs)
def getOrElse[T2](fallback: Expression[T2]) = Function[T | T2]("COALESCE", List(lhs, fallback))
def getOrElse[T2, S2 <: Boolean](fallback: Expression[T2, S2]) = Function[T | T2]("COALESCE", (lhs, fallback))


extension (lhs: Expression[?])
infix def +(rhs: Expression[?]) = lhs.concat(rhs)
extension (lhs: Expression[?, ?])
infix def +[T, S <: Boolean](rhs: Expression[T, S]) = lhs.concat(rhs)


extension (lhs: Expression[String])
extension (lhs: Expression[String, ?])
def startsWith(rhs: String) = StartsWith(needle = rhs, haystack = lhs)
def endsWith(rhs: String) = EndsWith(needle = rhs, haystack = lhs)
def contains(rhs: String) = Contains(needle = rhs, haystack = lhs)


given Conversion[String, Expression[String]] = LiteralExpression(_)
given Conversion[Int, Expression[Int]] = LiteralExpression(_)
given [T, E <: Expression[T]]: Conversion[QueryBuilder[E], Expression[T]] = SubqueryExpression(_)
extension (e: Expression[?, true])
def asc = Asc(e)
def desc = Desc(e)


extension [T <: Numeric | Null, E <: Expression[T, true]] (qb: QueryBuilder[E])
def sum = qb.copy(scope = Sum(qb.scope))
def min = qb.copy(scope = Min(qb.scope))
def max = qb.copy(scope = Max(qb.scope))
def avg = qb.copy(scope = Avg(qb.scope))


extension [T <: Numeric | Null] (e: Expression[T, false])
def sum = Sum(e)
def min = Min(e)
def max = Max(e)
def avg = Avg(e)


given Conversion[String, Expression[String, true]] = LiteralExpression(_)
given Conversion[Int, Expression[Int, true]] = LiteralExpression(_)
given [T, E <: Expression[T, true]]: Conversion[QueryBuilder[E], Expression[T, true]] = SubqueryExpression(_)


object NoFilterExpression extends LiteralExpression(true)
6 changes: 3 additions & 3 deletions src/main/OrderBy.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package tyqu


type OrderBy = Expression[?] | ExplicitDirection
type OrderBy = Expression[?, true] | ExplicitDirection


abstract sealed class ExplicitDirection
case class Asc(by: Expression[?]) extends ExplicitDirection
case class Desc(by: Expression[?]) extends ExplicitDirection
case class Asc(by: Expression[?, true]) extends ExplicitDirection
case class Desc(by: Expression[?, true]) extends ExplicitDirection

Loading

0 comments on commit 2cc26d5

Please sign in to comment.