Skip to content

Commit 2cc26d5

Browse files
committed
groupMap
1 parent 3bb4891 commit 2cc26d5

13 files changed

+543
-168
lines changed

src/main/Expression.scala

Lines changed: 133 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
package tyqu
22

3+
import scala.annotation.targetName
4+
5+
import utils.IsTupleOf
6+
import Tuple.Fold
7+
38

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

12+
type LogicalAnd[A, B] <: Boolean = (A, B) match
13+
case (true, true) => true
14+
case _ => false
15+
16+
type ForAll[T <: Tuple, Pred[X] <: Boolean] <: Boolean = T match
17+
case EmptyTuple => true
18+
case h *: t => LogicalAnd[Pred[h], ForAll[t, Pred]]
19+
20+
type CanSelectExpr[T] <: Boolean = T match
21+
case Expression[?, true] => true
22+
case Expression[?, false] => false
23+
case _ => Nothing
24+
25+
type ArgsCanSelect[T] <: Boolean = T match
26+
case Tuple => ForAll[T, CanSelectExpr]
27+
case _ => CanSelectExpr[T]
28+
729

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

2244
enum JoinType:
2345
case Inner, Left, Right, FullOuter
2446

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

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

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

3355

34-
abstract sealed class Expression[T]:
35-
36-
def as(n: String) = Alias[T, n.type](n, this)
56+
abstract sealed class Expression[T, CanSelect <: Boolean]:
3757

38-
def asc = Asc(this)
39-
def desc = Desc(this)
58+
def as(n: String) = Alias[T, CanSelect, n.type](n, this)
4059

41-
infix def ===[T2 <: T | Null](rhs: Expression[T2]) = Function[Boolean]("=", List(this, rhs))
42-
infix def =!=[T2 <: T | Null](rhs: Expression[T2]) = Function[Boolean]("!=", List(this, rhs))
60+
infix def ===[T2 <: T | Null, E <: Expression[T2, ?]](rhs: E) = Function[Boolean]("=", (this, rhs))
61+
infix def =!=[T2 <: T | Null, E <: Expression[T2, ?]](rhs: E) = Function[Boolean]("!=", (this, rhs))
4362

44-
def concat(rhs: Expression[?]) =
45-
Function[String]("CONCAT", List(this, rhs).flatMap{
46-
case Function("CONCAT", exprs) => exprs
47-
case expr => List(expr)
48-
})
63+
def concat[E <: Expression[?, ?]](rhs: E) =
64+
Concat(this, rhs)
4965

50-
def count = Function[Int]("COUNT", List(this))
66+
def count = Aggregation[Int]("COUNT", this)
5167

5268
end Expression
5369

70+
type AnyExpression = Expression[?, ?]
5471

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

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

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

6178
// E because the type in QueryBuilder is invariant
62-
case class SubqueryExpression[T, E <: Expression[T]](qb: QueryBuilder[E]) extends Expression[T]
79+
case class SubqueryExpression[T, E <: Expression[T, true]](qb: QueryBuilder[E]) extends Expression[T, true]
80+
81+
case class LiteralExpression[T](value: T, static: Boolean = false) extends Expression[T, true]
82+
83+
abstract class ProductExpression[T, Arguments <: Tuple | Expression[?, ?]] extends Expression[T, ArgsCanSelect[Arguments]]
84+
85+
case class Function[T, Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) extends ProductExpression[T, Arguments]
86+
87+
object Function:
88+
final class ApplyHelper[T]:
89+
def apply[Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) = new Function[T, Arguments](name, arguments)
90+
91+
def apply[E <: Expression[?, ?]](name: String, argument: E) = new Function[T, Tuple1[E]](name, Tuple1(argument))
92+
93+
def apply[T] = ApplyHelper[T]()
6394

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

66-
case class Function[T](name: String, arguments: List[Expression[?]]) extends Expression[T]
97+
object Aggregation:
98+
final class ApplyHelper[T]:
99+
def apply[Arguments <: Tuple](name: String, arguments: Arguments)(using IsTupleOf[Arguments, Expression[?, ?]] =:= true) = new Aggregation[T, Arguments](name, arguments)
100+
101+
def apply[E <: Expression[?, ?]](name: String, argument: E) = new Aggregation[T, Tuple1[E]](name, Tuple1(argument))
102+
103+
def apply[T] = ApplyHelper[T]()
104+
end Aggregation
105+
106+
abstract class UnaryNumericAggregation(name: String):
107+
def apply[T <: Numeric | Null](e: Expression[T, ?]) = Aggregation[T | Null](name, Tuple(e))
108+
def unapply(a: Aggregation[?, ?]): Option[AnyExpression] =
109+
if a.name == name then Some(a)
110+
else None
111+
end UnaryNumericAggregation
112+
113+
object Sum extends UnaryNumericAggregation("SUM")
114+
object Avg extends UnaryNumericAggregation("AVG")
115+
object Min extends UnaryNumericAggregation("MIN")
116+
object Max extends UnaryNumericAggregation("MAX")
67117

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

120+
abstract class BinaryFunction[T](name: String):
121+
def apply[E1 <: Expression[?, ?], E2 <: Expression[?, ?]](a: E1, b: E2) =
122+
Function[T](name, (a, b))
123+
124+
def unapply(f: Function[?, ?]): Option[(AnyExpression, AnyExpression)] =
125+
if f.name == name then Some(f.arguments.asInstanceOf[(AnyExpression, AnyExpression)])
126+
else None
127+
128+
object Concat extends BinaryFunction[String]("CONCAT")
70129

71-
case class And(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]
72-
case class Or(lhs: Expression[Boolean], rhs: Expression[Boolean]) extends Expression[Boolean]
73-
case class Not(expression: Expression[Boolean]) extends Expression[Boolean]
74-
case class IsNull[T](expression: Expression[T | Null]) extends Expression[Boolean]
75-
case class IsNotNull[T](expression: Expression[T | Null]) extends Expression[Boolean]
76-
case class Exists(subquery: SubqueryExpression[?, ?]) extends Expression[Boolean]
77-
case class StartsWith(needle: String, haystack: Expression[String]) extends Expression[Boolean]
78-
case class EndsWith(needle: String, haystack: Expression[String]) extends Expression[Boolean]
79-
case class Contains(needle: String, haystack: Expression[String]) extends Expression[Boolean]
130+
case class And[L <: Expression[Boolean, ?], R <: Expression[Boolean, ?]](lhs: L, rhs: R) extends ProductExpression[Boolean, (L, R)]
131+
case class Or[L <: Expression[Boolean, ?], R <: Expression[Boolean, ?]](lhs: L, rhs: R) extends ProductExpression[Boolean, (L, R)]
132+
case class Not[E <: Expression[Boolean, ?]](expression: E) extends ProductExpression[Boolean, Tuple1[E]]
133+
case class IsNull[T, S <: Boolean, E <: Expression[T | Null, S]](expression: E) extends ProductExpression[Boolean, Tuple1[E]]
134+
case class IsNotNull[T, E <: Expression[T | Null, ?]](expression: E) extends ProductExpression[Boolean, E]
135+
case class Exists(subquery: SubqueryExpression[?, ?]) extends Expression[Boolean, true]
136+
case class StartsWith[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]
137+
case class EndsWith[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]
138+
case class Contains[S <: Boolean](needle: String, haystack: Expression[String, S]) extends Expression[Boolean, S]
80139

81-
case class CountAll() extends Expression[Int]
140+
case class CountAll() extends Expression[Int, true]
82141

83-
case class Plus[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
84-
case class Minus[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
85-
case class Multiply[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
86-
case class Divide[T1 <: Numeric | Null, T2 <: Numeric | Null](lhs: Expression[T1], rhs: Expression[T2]) extends Expression[T1 | T2]
142+
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)]
143+
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)]
144+
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)]
145+
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)]
87146

88147

89-
extension (lhs: Expression[Boolean])
90-
infix def &&(rhs: Expression[Boolean]) =
148+
extension [S <: Boolean] (lhs: Expression[Boolean, S])
149+
infix def &&[S2 <: Boolean](rhs: Expression[Boolean, S2]) =
91150
if (lhs == NoFilterExpression) rhs
92151
else And(lhs, rhs)
93-
infix def ||(rhs: Expression[Boolean]) = Or(lhs, rhs)
152+
infix def ||[S2 <: Boolean](rhs: Expression[Boolean, S2]) = Or(lhs, rhs)
94153
infix def unary_! = Not(lhs)
95154

96155

97-
extension [T <: Numeric | Null] (lhs: Expression[T])
98-
infix def <[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean]("<", List(lhs, rhs))
99-
infix def <=[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean]("<=", List(lhs, rhs))
100-
infix def >[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean](">", List(lhs, rhs))
101-
infix def >=[T2 <: Numeric | Null](rhs: Expression[T2]) = Function[Boolean](">=", List(lhs, rhs))
102-
infix def +[T2 <: Numeric | Null](rhs: Expression[T2]) = Plus(lhs, rhs)
103-
infix def -[T2 <: Numeric | Null](rhs: Expression[T2]) = Minus(lhs, rhs)
104-
infix def *[T2 <: Numeric | Null](rhs: Expression[T2]) = Multiply(lhs, rhs)
105-
infix def /[T2 <: Numeric | Null](rhs: Expression[T2]) = Divide(lhs, rhs)
156+
extension [T <: Numeric | Null, S <: Boolean] (lhs: Expression[T, S])
157+
infix def <[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean]("<", (lhs, rhs))
158+
infix def <=[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean]("<=", (lhs, rhs))
159+
infix def >[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean, (Expression[T, S], Expression[T2, S2])](">", (lhs, rhs))
160+
infix def >=[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Function[Boolean](">=", (lhs, rhs))
161+
infix def +[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Plus(lhs, rhs)
162+
infix def -[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Minus(lhs, rhs)
163+
infix def *[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Multiply(lhs, rhs)
164+
infix def /[T2 <: Numeric | Null, S2 <: Boolean](rhs: Expression[T2, S2]) = Divide(lhs, rhs)
106165

107166

108-
extension [T] (lhs: Expression[T | Null])
167+
extension [T, S <: Boolean] (lhs: Expression[T | Null, S])
109168
def isNull = IsNull(lhs)
110169
def isNotNull = IsNotNull(lhs)
111-
def getOrElse[T2](fallback: Expression[T2]) = Function[T | T2]("COALESCE", List(lhs, fallback))
170+
def getOrElse[T2, S2 <: Boolean](fallback: Expression[T2, S2]) = Function[T | T2]("COALESCE", (lhs, fallback))
112171

113172

114-
extension (lhs: Expression[?])
115-
infix def +(rhs: Expression[?]) = lhs.concat(rhs)
173+
extension (lhs: Expression[?, ?])
174+
infix def +[T, S <: Boolean](rhs: Expression[T, S]) = lhs.concat(rhs)
116175

117176

118-
extension (lhs: Expression[String])
177+
extension (lhs: Expression[String, ?])
119178
def startsWith(rhs: String) = StartsWith(needle = rhs, haystack = lhs)
120179
def endsWith(rhs: String) = EndsWith(needle = rhs, haystack = lhs)
121180
def contains(rhs: String) = Contains(needle = rhs, haystack = lhs)
122181

123182

124-
given Conversion[String, Expression[String]] = LiteralExpression(_)
125-
given Conversion[Int, Expression[Int]] = LiteralExpression(_)
126-
given [T, E <: Expression[T]]: Conversion[QueryBuilder[E], Expression[T]] = SubqueryExpression(_)
183+
extension (e: Expression[?, true])
184+
def asc = Asc(e)
185+
def desc = Desc(e)
186+
187+
188+
extension [T <: Numeric | Null, E <: Expression[T, true]] (qb: QueryBuilder[E])
189+
def sum = qb.copy(scope = Sum(qb.scope))
190+
def min = qb.copy(scope = Min(qb.scope))
191+
def max = qb.copy(scope = Max(qb.scope))
192+
def avg = qb.copy(scope = Avg(qb.scope))
193+
194+
195+
extension [T <: Numeric | Null] (e: Expression[T, false])
196+
def sum = Sum(e)
197+
def min = Min(e)
198+
def max = Max(e)
199+
def avg = Avg(e)
200+
201+
202+
given Conversion[String, Expression[String, true]] = LiteralExpression(_)
203+
given Conversion[Int, Expression[Int, true]] = LiteralExpression(_)
204+
given [T, E <: Expression[T, true]]: Conversion[QueryBuilder[E], Expression[T, true]] = SubqueryExpression(_)
127205

128206

129207
object NoFilterExpression extends LiteralExpression(true)

src/main/OrderBy.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package tyqu
22

33

4-
type OrderBy = Expression[?] | ExplicitDirection
4+
type OrderBy = Expression[?, true] | ExplicitDirection
55

66

77
abstract sealed class ExplicitDirection
8-
case class Asc(by: Expression[?]) extends ExplicitDirection
9-
case class Desc(by: Expression[?]) extends ExplicitDirection
8+
case class Asc(by: Expression[?, true]) extends ExplicitDirection
9+
case class Desc(by: Expression[?, true]) extends ExplicitDirection
1010

0 commit comments

Comments
 (0)