diff --git a/README.md b/README.md index 88ed1cdafe..97a3d4b976 100644 --- a/README.md +++ b/README.md @@ -256,12 +256,26 @@ db.run(q) **sortBy** ```scala -val q = quote { +val q1 = quote { query[Person].sortBy(p => p.age) } -db.run(q) -// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age +db.run(q1) +// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age ASC NULLS FIRST + +val q2 = quote { + query[Person].sortBy(p => p.age)(Ord.descNullsLast) +} + +db.run(q2) +// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.age DESC NULLS LAST + +val q3 = quote { + query[Person].sortBy(p => (p.name, p.age))(Ord(Ord.asc, Ord.desc)) +} + +db.run(q3) +// SELECT p.id, p.name, p.age FROM Person p ORDER BY p.name ASC, p.age DESC ``` **drop/take** diff --git a/quill-core/src/main/scala/io/getquill/Ord.scala b/quill-core/src/main/scala/io/getquill/Ord.scala new file mode 100644 index 0000000000..a41cfa48f8 --- /dev/null +++ b/quill-core/src/main/scala/io/getquill/Ord.scala @@ -0,0 +1,23 @@ +package io.getquill + +trait Ord[T] + +trait OrdOps { + + def asc[T](implicit ord: Ordering[T]): Ord[T] + def desc[T](implicit ord: Ordering[T]): Ord[T] + def ascNullsFirst[T](implicit ord: Ordering[T]): Ord[T] + def descNullsFirst[T](implicit ord: Ordering[T]): Ord[T] + def ascNullsLast[T](implicit ord: Ordering[T]): Ord[T] + def descNullsLast[T](implicit ord: Ordering[T]): Ord[T] + + def apply[T1, T2](o1: Ord[T1], o2: Ord[T2]): Ord[(T1, T2)] + def apply[T1, T2, T3](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3]): Ord[(T1, T2, T3)] + def apply[T1, T2, T3, T4](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4]): Ord[(T1, T2, T3, T4)] + def apply[T1, T2, T3, T4, T5](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5]): Ord[(T1, T2, T3, T4, T5)] + def apply[T1, T2, T3, T4, T5, T6](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6]): Ord[(T1, T2, T3, T4, T5, T6)] + def apply[T1, T2, T3, T4, T5, T6, T7](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7]): Ord[(T1, T2, T3, T4, T5, T6, T7)] + def apply[T1, T2, T3, T4, T5, T6, T7, T8](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8)] + def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8], o9: Ord[T9]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8, T9)] + def apply[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](o1: Ord[T1], o2: Ord[T2], o3: Ord[T3], o4: Ord[T4], o5: Ord[T5], o6: Ord[T6], o7: Ord[T7], o8: Ord[T8], o9: Ord[T9], o10: Ord[T10]): Ord[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)] +} diff --git a/quill-core/src/main/scala/io/getquill/Query.scala b/quill-core/src/main/scala/io/getquill/Query.scala index 4c392c0ed9..582e613b1a 100644 --- a/quill-core/src/main/scala/io/getquill/Query.scala +++ b/quill-core/src/main/scala/io/getquill/Query.scala @@ -6,7 +6,7 @@ sealed trait Query[+T] { def flatMap[R](f: T => Query[R]): Query[R] def withFilter(f: T => Boolean): Query[T] def filter(f: T => Boolean): Query[T] - def sortBy[R](f: T => R)(implicit ord: Ordering[R]): SortedQuery[T] + def sortBy[R](f: T => R)(implicit ord: Ord[R]): Query[T] def take(n: Int): Query[T] def drop(n: Int): Query[T] @@ -35,16 +35,6 @@ sealed trait OuterJoinQuery[A, B, R] extends Query[R] { def on(f: (A, B) => Boolean): Query[R] } -sealed trait SortedQuery[+T] extends Query[T] { - - def reverse: SortedQuery[T] - - def map[R](f: T => R): SortedQuery[R] - def flatMap[R](f: T => Query[R]): SortedQuery[R] - def withFilter(f: T => Boolean): SortedQuery[T] - def filter(f: T => Boolean): SortedQuery[T] -} - sealed trait EntityQuery[T] extends Query[T] with Insertable[T] diff --git a/quill-core/src/main/scala/io/getquill/ast/Ast.scala b/quill-core/src/main/scala/io/getquill/ast/Ast.scala index 7b40d33b25..5d965d7bf8 100644 --- a/quill-core/src/main/scala/io/getquill/ast/Ast.scala +++ b/quill-core/src/main/scala/io/getquill/ast/Ast.scala @@ -27,14 +27,23 @@ case class Map(query: Ast, alias: Ident, body: Ast) extends Query case class FlatMap(query: Ast, alias: Ident, body: Ast) extends Query -case class SortBy(query: Ast, alias: Ident, criterias: Ast) extends Query +case class SortBy(query: Ast, alias: Ident, criterias: Ast, ordering: Ordering) extends Query + +sealed trait Ordering +case class TupleOrdering(elems: List[Ordering]) extends Ordering + +sealed trait PropertyOrdering extends Ordering +case object Asc extends PropertyOrdering +case object Desc extends PropertyOrdering +case object AscNullsFirst extends PropertyOrdering +case object DescNullsFirst extends PropertyOrdering +case object AscNullsLast extends PropertyOrdering +case object DescNullsLast extends PropertyOrdering case class GroupBy(query: Ast, alias: Ident, body: Ast) extends Query case class Aggregation(operator: AggregationOperator, ast: Ast) extends Query -case class Reverse(query: Ast) extends Query - case class Take(query: Ast, n: Ast) extends Query case class Drop(query: Ast, n: Ast) extends Query diff --git a/quill-core/src/main/scala/io/getquill/ast/AstShow.scala b/quill-core/src/main/scala/io/getquill/ast/AstShow.scala index 3e23787885..69718b1195 100644 --- a/quill-core/src/main/scala/io/getquill/ast/AstShow.scala +++ b/quill-core/src/main/scala/io/getquill/ast/AstShow.scala @@ -57,8 +57,8 @@ object AstShow { case FlatMap(source, alias, body) => s"${source.show}.flatMap(${alias.show} => ${body.show})" - case SortBy(source, alias, body) => - s"${source.show}.sortBy(${alias.show} => ${body.show})" + case SortBy(source, alias, body, ordering) => + s"${source.show}.sortBy(${alias.show} => ${body.show})(${ordering.show})" case GroupBy(source, alias, body) => s"${source.show}.groupBy(${alias.show} => ${body.show})" @@ -66,9 +66,6 @@ object AstShow { case Aggregation(op, ast) => s"${scopedShow(ast)}.${op.show}" - case Reverse(source) => - s"${source.show}.reverse" - case Take(source, n) => s"${source.show}.take($n)" @@ -86,6 +83,19 @@ object AstShow { } } + implicit val orderingShow: Show[Ordering] = new Show[Ordering] { + def show(q: Ordering) = + q match { + case TupleOrdering(elems) => s"Ord(${elems.show})" + case Asc => s"Ord.asc" + case Desc => s"Ord.desc" + case AscNullsFirst => s"Ord.ascNullsFirst" + case DescNullsFirst => s"Ord.descNullsFirst" + case AscNullsLast => s"Ord.ascNullsLast" + case DescNullsLast => s"Ord.descNullsLast" + } + } + implicit val optionOperationShow: Show[OptionOperation] = new Show[OptionOperation] { def show(q: OptionOperation) = { val method = q.t match { diff --git a/quill-core/src/main/scala/io/getquill/ast/StatefulTransformer.scala b/quill-core/src/main/scala/io/getquill/ast/StatefulTransformer.scala index 5ddbcba09f..f4cbbfbb04 100644 --- a/quill-core/src/main/scala/io/getquill/ast/StatefulTransformer.scala +++ b/quill-core/src/main/scala/io/getquill/ast/StatefulTransformer.scala @@ -54,10 +54,10 @@ trait StatefulTransformer[T] { val (at, att) = apply(a) val (ct, ctt) = att.apply(c) (FlatMap(at, b, ct), ctt) - case SortBy(a, b, c) => + case SortBy(a, b, c, d) => val (at, att) = apply(a) val (ct, ctt) = att.apply(c) - (SortBy(at, b, ct), ctt) + (SortBy(at, b, ct, d), ctt) case GroupBy(a, b, c) => val (at, att) = apply(a) val (ct, ctt) = att.apply(c) @@ -65,9 +65,6 @@ trait StatefulTransformer[T] { case Aggregation(o, a) => val (at, att) = apply(a) (Aggregation(o, at), att) - case Reverse(a) => - val (at, att) = apply(a) - (Reverse(at), att) case Take(a, b) => val (at, att) = apply(a) val (bt, btt) = att.apply(b) diff --git a/quill-core/src/main/scala/io/getquill/ast/StatelessTransformer.scala b/quill-core/src/main/scala/io/getquill/ast/StatelessTransformer.scala index 76055a3c29..7c5124b458 100644 --- a/quill-core/src/main/scala/io/getquill/ast/StatelessTransformer.scala +++ b/quill-core/src/main/scala/io/getquill/ast/StatelessTransformer.scala @@ -25,10 +25,9 @@ trait StatelessTransformer { case Filter(a, b, c) => Filter(apply(a), b, apply(c)) case Map(a, b, c) => Map(apply(a), b, apply(c)) case FlatMap(a, b, c) => FlatMap(apply(a), b, apply(c)) - case SortBy(a, b, c) => SortBy(apply(a), b, apply(c)) + case SortBy(a, b, c, d) => SortBy(apply(a), b, apply(c), d) case GroupBy(a, b, c) => GroupBy(apply(a), b, apply(c)) case Aggregation(o, a) => Aggregation(o, apply(a)) - case Reverse(a) => Reverse(apply(a)) case Take(a, b) => Take(apply(a), apply(b)) case Drop(a, b) => Drop(apply(a), apply(b)) case Union(a, b) => Union(apply(a), apply(b)) diff --git a/quill-core/src/main/scala/io/getquill/norm/AdHocReduction.scala b/quill-core/src/main/scala/io/getquill/norm/AdHocReduction.scala index 5fe89595d8..c546bba31b 100644 --- a/quill-core/src/main/scala/io/getquill/norm/AdHocReduction.scala +++ b/quill-core/src/main/scala/io/getquill/norm/AdHocReduction.scala @@ -31,13 +31,8 @@ object AdHocReduction { // a.flatMap(b => c).sortBy(d => e) => // a.flatMap(b => c.sortBy(d => e)) - case SortBy(FlatMap(a, b, c), d, e) => - Some(FlatMap(a, b, SortBy(c, d, e))) - - // a.flatMap(b => c).reverse => - // a.flatMap(b => c.reverse) - case Reverse(FlatMap(a, b, c)) => - Some(FlatMap(a, b, Reverse(c))) + case SortBy(FlatMap(a, b, c), d, e, f) => + Some(FlatMap(a, b, SortBy(c, d, e, f))) // a.flatMap(b => c.union(d)) // a.flatMap(b => c).union(a.flatMap(b => d)) diff --git a/quill-core/src/main/scala/io/getquill/norm/ApplyIntermediateMap.scala b/quill-core/src/main/scala/io/getquill/norm/ApplyIntermediateMap.scala index 465ddcf290..c10d2460a4 100644 --- a/quill-core/src/main/scala/io/getquill/norm/ApplyIntermediateMap.scala +++ b/quill-core/src/main/scala/io/getquill/norm/ApplyIntermediateMap.scala @@ -10,7 +10,7 @@ object ApplyIntermediateMap { case Map(Map(a: GroupBy, b, c), d, e) => None case FlatMap(Map(a: GroupBy, b, c), d, e) => None case Filter(Map(a: GroupBy, b, c), d, e) => None - case SortBy(Map(a: GroupBy, b, c), d, e) => None + case SortBy(Map(a: GroupBy, b, c), d, e, f) => None case Map(a: GroupBy, b, c) if (b == c) => None // a.map(b => b) => @@ -38,9 +38,9 @@ object ApplyIntermediateMap { // a.map(b => c).sortBy(d => e) => // a.sortBy(b => e[d := c]).map(b => c) - case SortBy(Map(a, b, c), d, e) => + case SortBy(Map(a, b, c), d, e, f) => val er = BetaReduction(e, d -> c) - Some(Map(SortBy(a, b, er), b, c)) + Some(Map(SortBy(a, b, er, f), b, c)) case other => None } diff --git a/quill-core/src/main/scala/io/getquill/norm/AttachToEntity.scala b/quill-core/src/main/scala/io/getquill/norm/AttachToEntity.scala index 3c9bde9e37..bbca185564 100644 --- a/quill-core/src/main/scala/io/getquill/norm/AttachToEntity.scala +++ b/quill-core/src/main/scala/io/getquill/norm/AttachToEntity.scala @@ -11,13 +11,12 @@ object AttachToEntity { case Map(a: Entity, b, c) => Map(f(a, b), b, c) case FlatMap(a: Entity, b, c) => FlatMap(f(a, b), b, c) case Filter(a: Entity, b, c) => Filter(f(a, b), b, c) - case SortBy(a: Entity, b, c) => SortBy(f(a, b), b, c) + case SortBy(a: Entity, b, c, d) => SortBy(f(a, b), b, c, d) case Map(a: Query, b, c) => Map(apply(f, Some(b))(a), b, c) case FlatMap(a: Query, b, c) => FlatMap(apply(f, Some(b))(a), b, c) case Filter(a: Query, b, c) => Filter(apply(f, Some(b))(a), b, c) - case SortBy(a: Query, b, c) => SortBy(apply(f, Some(b))(a), b, c) - case Reverse(a: Query) => Reverse(apply(f, alias)(a)) + case SortBy(a: Query, b, c, d) => SortBy(apply(f, Some(b))(a), b, c, d) case Take(a: Query, b) => Take(apply(f, alias)(a), b) case Drop(a: Query, b) => Drop(apply(f, alias)(a), b) case GroupBy(a: Query, b, c) => GroupBy(apply(f, Some(b))(a), b, c) diff --git a/quill-core/src/main/scala/io/getquill/norm/BetaReduction.scala b/quill-core/src/main/scala/io/getquill/norm/BetaReduction.scala index e770438503..4b39df8429 100644 --- a/quill-core/src/main/scala/io/getquill/norm/BetaReduction.scala +++ b/quill-core/src/main/scala/io/getquill/norm/BetaReduction.scala @@ -29,13 +29,13 @@ case class BetaReduction(map: collection.Map[Ident, Ast]) Map(apply(a), b, BetaReduction(map - b)(c)) case FlatMap(a, b, c) => FlatMap(apply(a), b, BetaReduction(map - b)(c)) - case SortBy(a, b, c) => - SortBy(apply(a), b, BetaReduction(map - b)(c)) + case SortBy(a, b, c, d) => + SortBy(apply(a), b, BetaReduction(map - b)(c), d) case GroupBy(a, b, c) => GroupBy(apply(a), b, BetaReduction(map - b)(c)) case OuterJoin(t, a, b, iA, iB, on) => OuterJoin(t, apply(a), apply(b), iA, iB, BetaReduction(map - iA - iB)(on)) - case _: Reverse | _: Take | _: Entity | _: Drop | _: Union | _: UnionAll | _: Aggregation => + case _: Take | _: Entity | _: Drop | _: Union | _: UnionAll | _: Aggregation => super.apply(query) } } diff --git a/quill-core/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala b/quill-core/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala index 14d39d92ee..4f7bf7ec1c 100644 --- a/quill-core/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala +++ b/quill-core/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala @@ -10,10 +10,9 @@ object NormalizeNestedStructures { case Map(a, b, c) => apply(a, c)(Map(_, b, _)) case FlatMap(a, b, c) => apply(a, c)(FlatMap(_, b, _)) case Filter(a, b, c) => apply(a, c)(Filter(_, b, _)) - case SortBy(a, b, c) => apply(a, c)(SortBy(_, b, _)) + case SortBy(a, b, c, d) => apply(a, c)(SortBy(_, b, _, d)) case GroupBy(a, b, c) => apply(a, c)(GroupBy(_, b, _)) case Aggregation(a, b) => apply(b)(Aggregation(a, _)) - case Reverse(a) => apply(a)(Reverse) case Take(a, b) => apply(a, b)(Take) case Drop(a, b) => apply(a, b)(Drop) case Union(a, b) => apply(a, b)(Union) diff --git a/quill-core/src/main/scala/io/getquill/norm/OrderTerms.scala b/quill-core/src/main/scala/io/getquill/norm/OrderTerms.scala index 6859abfbe1..65da4b9fe3 100644 --- a/quill-core/src/main/scala/io/getquill/norm/OrderTerms.scala +++ b/quill-core/src/main/scala/io/getquill/norm/OrderTerms.scala @@ -7,23 +7,12 @@ object OrderTerms { def unapply(q: Query) = q match { - case Reverse(Map(a: GroupBy, b, c)) => None case Take(Map(a: GroupBy, b, c), d) => None - // a.reverse.filter(b => c) => - // a.filter(b => c).reverse - case Filter(Reverse(a), b, c) => - Some(Reverse(Filter(a, b, c))) - - // a.map(b => c).reverse => - // a.reverse.map(b => c) - case Reverse(Map(a, b, c)) => - Some(Map(Reverse(a), b, c)) - // a.sortBy(b => c).filter(d => e) => // a.filter(d => e).sortBy(b => c) - case Filter(SortBy(a, b, c), d, e) => - Some(SortBy(Filter(a, d, e), b, c)) + case Filter(SortBy(a, b, c, d), e, f) => + Some(SortBy(Filter(a, e, f), b, c, d)) // a.map(b => c).take(d) => // a.take(d).map(b => c) diff --git a/quill-core/src/main/scala/io/getquill/norm/SymbolicReduction.scala b/quill-core/src/main/scala/io/getquill/norm/SymbolicReduction.scala index c535e4da36..1a3525b5d2 100644 --- a/quill-core/src/main/scala/io/getquill/norm/SymbolicReduction.scala +++ b/quill-core/src/main/scala/io/getquill/norm/SymbolicReduction.scala @@ -7,11 +7,6 @@ object SymbolicReduction { def unapply(q: Query) = q match { - // a.reverse.reverse => - // a - case Reverse(Reverse(a: Query)) => - Some(a) - // a.filter(b => c).flatMap(d => e.$) => // a.flatMap(d => e.filter(_ => c[b := d]).$) case FlatMap(Filter(a, b, c), d, e: Query) => diff --git a/quill-core/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala b/quill-core/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala index 9b08c010a2..34fbe05d15 100644 --- a/quill-core/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala +++ b/quill-core/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala @@ -18,8 +18,8 @@ private case class AvoidAliasConflict(state: Set[Ident]) case Filter(q: Entity, x, p) => apply(x, p)(Filter(q, _, _)) - case SortBy(q: Entity, x, p) => - apply(x, p)(SortBy(q, _, _)) + case SortBy(q: Entity, x, p, o) => + apply(x, p)(SortBy(q, _, _, o)) case OuterJoin(t, a, b, iA, iB, o) => val freshA = freshIdent(iA) diff --git a/quill-core/src/main/scala/io/getquill/norm/capture/Dealias.scala b/quill-core/src/main/scala/io/getquill/norm/capture/Dealias.scala index 518e56ed30..d25160b1a0 100644 --- a/quill-core/src/main/scala/io/getquill/norm/capture/Dealias.scala +++ b/quill-core/src/main/scala/io/getquill/norm/capture/Dealias.scala @@ -17,15 +17,12 @@ case class Dealias(state: Option[Ident]) extends StatefulTransformer[Option[Iden dealias(a, b, c)(Map) case Filter(a, b, c) => dealias(a, b, c)(Filter) - case SortBy(a, b, c) => - dealias(a, b, c)(SortBy) + case SortBy(a, b, c, d) => + dealias(a, b, c)(SortBy(_, _, _, d)) case GroupBy(a, b, c) => dealias(a, b, c)(GroupBy) case q: Aggregation => (q, Dealias(None)) - case Reverse(a) => - val (an, ant) = apply(a) - (Reverse(an), ant) case Take(a, b) => val (an, ant) = apply(a) (Take(an, b), ant) diff --git a/quill-core/src/main/scala/io/getquill/package.scala b/quill-core/src/main/scala/io/getquill/package.scala index c5cd333fed..4c8622c31c 100644 --- a/quill-core/src/main/scala/io/getquill/package.scala +++ b/quill-core/src/main/scala/io/getquill/package.scala @@ -21,4 +21,8 @@ package object getquill { def mappedEncoding[I, O](f: I => O) = source.MappedEncoding(f) type Quoted[T] = quotation.Quoted[T] + + def Ord: OrdOps = NonQuotedException() + + implicit def orderingToOrd[T](implicit o: Ordering[T]): Ord[T] = NonQuotedException() } diff --git a/quill-core/src/main/scala/io/getquill/quotation/FreeVariables.scala b/quill-core/src/main/scala/io/getquill/quotation/FreeVariables.scala index bd61934992..ee9ccd2142 100644 --- a/quill-core/src/main/scala/io/getquill/quotation/FreeVariables.scala +++ b/quill-core/src/main/scala/io/getquill/quotation/FreeVariables.scala @@ -22,17 +22,17 @@ case class FreeVariables(state: State) override def apply(query: Query): (Query, StatefulTransformer[State]) = query match { - case q @ Filter(a, b, c) => (q, free(a, b, c)) - case q @ Map(a, b, c) => (q, free(a, b, c)) - case q @ FlatMap(a, b, c) => (q, free(a, b, c)) - case q @ SortBy(a, b, c) => (q, free(a, b, c)) - case q @ GroupBy(a, b, c) => (q, free(a, b, c)) + case q @ Filter(a, b, c) => (q, free(a, b, c)) + case q @ Map(a, b, c) => (q, free(a, b, c)) + case q @ FlatMap(a, b, c) => (q, free(a, b, c)) + case q @ SortBy(a, b, c, d) => (q, free(a, b, c)) + case q @ GroupBy(a, b, c) => (q, free(a, b, c)) case q @ OuterJoin(t, a, b, iA, iB, on) => val (_, freeA) = apply(a) val (_, freeB) = apply(a) val (_, freeOn) = FreeVariables(State(state.seen + iA + iB, Set.empty))(on) (q, FreeVariables(State(state.seen, state.free ++ freeA.state.free ++ freeB.state.free ++ freeOn.state.free))) - case _: Entity | _: Reverse | _: Take | _: Drop | _: Union | _: UnionAll | _: Aggregation => + case _: Entity | _: Take | _: Drop | _: Union | _: UnionAll | _: Aggregation => super.apply(query) } diff --git a/quill-core/src/main/scala/io/getquill/quotation/Liftables.scala b/quill-core/src/main/scala/io/getquill/quotation/Liftables.scala index 733fdc2054..7e95def61b 100644 --- a/quill-core/src/main/scala/io/getquill/quotation/Liftables.scala +++ b/quill-core/src/main/scala/io/getquill/quotation/Liftables.scala @@ -72,10 +72,9 @@ trait Liftables { case Filter(a, b, c) => q"$pack.Filter($a, $b, $c)" case Map(a, b, c) => q"$pack.Map($a, $b, $c)" case FlatMap(a, b, c) => q"$pack.FlatMap($a, $b, $c)" - case SortBy(a, b, c) => q"$pack.SortBy($a, $b, $c)" + case SortBy(a, b, c, d) => q"$pack.SortBy($a, $b, $c, $d)" case GroupBy(a, b, c) => q"$pack.GroupBy($a, $b, $c)" case Aggregation(a, b) => q"$pack.Aggregation($a, $b)" - case Reverse(a) => q"$pack.Reverse($a)" case Take(a, b) => q"$pack.Take($a, $b)" case Drop(a, b) => q"$pack.Drop($a, $b)" case Union(a, b) => q"$pack.Union($a, $b)" @@ -87,6 +86,16 @@ trait Liftables { case PropertyAlias(a, b) => q"$pack.PropertyAlias($a, $b)" } + implicit val orderingLiftable: Liftable[Ordering] = Liftable[Ordering] { + case TupleOrdering(elems) => q"$pack.TupleOrdering($elems)" + case Asc => q"$pack.Asc" + case Desc => q"$pack.Desc" + case AscNullsFirst => q"$pack.AscNullsFirst" + case DescNullsFirst => q"$pack.DescNullsFirst" + case AscNullsLast => q"$pack.AscNullsLast" + case DescNullsLast => q"$pack.DescNullsLast" + } + implicit val outerJoinTypeLiftable: Liftable[OuterJoinType] = Liftable[OuterJoinType] { case LeftJoin => q"$pack.LeftJoin" case RightJoin => q"$pack.RightJoin" diff --git a/quill-core/src/main/scala/io/getquill/quotation/Parsing.scala b/quill-core/src/main/scala/io/getquill/quotation/Parsing.scala index ba9c1fc6d8..097a32096d 100644 --- a/quill-core/src/main/scala/io/getquill/quotation/Parsing.scala +++ b/quill-core/src/main/scala/io/getquill/quotation/Parsing.scala @@ -132,7 +132,7 @@ trait Parsing { FlatMap(astParser(source), identParser(alias), astParser(body)) case q"$source.sortBy[$t](($alias) => $body)($ord)" if (is[QuillQuery[Any]](source)) => - SortBy(astParser(source), identParser(alias), astParser(body)) + SortBy(astParser(source), identParser(alias), astParser(body), orderingParser(ord)) case q"$source.groupBy[$t](($alias) => $body)" if (is[QuillQuery[Any]](source)) => GroupBy(astParser(source), identParser(alias), astParser(body)) @@ -143,9 +143,6 @@ trait Parsing { case q"$a.sum[$t]($n)" if (is[QuillQuery[Any]](a)) => Aggregation(AggregationOperator.`sum`, astParser(a)) case q"$a.size" if (is[QuillQuery[Any]](a)) => Aggregation(AggregationOperator.`size`, astParser(a)) - case q"$source.reverse" if (is[QuillQuery[Any]](source)) => - Reverse(astParser(source)) - case q"$source.take($n)" if (is[QuillQuery[Any]](source)) => Take(astParser(source), astParser(n)) @@ -168,6 +165,17 @@ trait Parsing { c.fail("An outer join clause must be followed by 'on'.") } + implicit val orderingParser: Parser[Ordering] = Parser[Ordering] { + case q"$pack.orderingToOrd[$t]($o)" => AscNullsFirst + case q"$pack.Ord.apply[..$t](..$elems)" => TupleOrdering(elems.map(orderingParser(_))) + case q"$pack.Ord.asc[$t]($o)" => Asc + case q"$pack.Ord.desc[$t]($o)" => Desc + case q"$pack.Ord.ascNullsFirst[$t]($o)" => AscNullsFirst + case q"$pack.Ord.descNullsFirst[$t]($o)" => DescNullsFirst + case q"$pack.Ord.ascNullsLast[$t]($o)" => AscNullsLast + case q"$pack.Ord.descNullsLast[$t]($o)" => DescNullsLast + } + implicit val propertyAliasParser: Parser[PropertyAlias] = Parser[PropertyAlias] { case q"(($x1) => scala.this.Predef.ArrowAssoc[$t]($x2.$prop).->[$v](${ alias: String }))" => PropertyAlias(prop.decodedName.toString, alias) diff --git a/quill-core/src/main/scala/io/getquill/quotation/Unliftables.scala b/quill-core/src/main/scala/io/getquill/quotation/Unliftables.scala index 4f79a1504a..72f0cabb30 100644 --- a/quill-core/src/main/scala/io/getquill/quotation/Unliftables.scala +++ b/quill-core/src/main/scala/io/getquill/quotation/Unliftables.scala @@ -77,9 +77,8 @@ trait Unliftables { case q"$pack.Filter.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => Filter(a, b, c) case q"$pack.Map.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => Map(a, b, c) case q"$pack.FlatMap.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => FlatMap(a, b, c) - case q"$pack.SortBy.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => SortBy(a, b, c) + case q"$pack.SortBy.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast }, ${ d: Ordering })" => SortBy(a, b, c, d) case q"$pack.GroupBy.apply(${ a: Ast }, ${ b: Ident }, ${ c: Ast })" => GroupBy(a, b, c) - case q"$pack.Reverse.apply(${ a: Ast })" => Reverse(a) case q"$pack.Take.apply(${ a: Ast }, ${ b: Ast })" => Take(a, b) case q"$pack.Drop.apply(${ a: Ast }, ${ b: Ast })" => Drop(a, b) case q"$pack.Union.apply(${ a: Ast }, ${ b: Ast })" => Union(a, b) @@ -88,6 +87,16 @@ trait Unliftables { OuterJoin(t, a, b, iA, iB, on) } + implicit val orderingUnliftable: Unliftable[Ordering] = Unliftable[Ordering] { + case q"$pack.TupleOrdering.apply(${ elems: List[Ordering] })" => TupleOrdering(elems) + case q"$pack.Asc" => Asc + case q"$pack.Desc" => Desc + case q"$pack.AscNullsFirst" => AscNullsFirst + case q"$pack.DescNullsFirst" => DescNullsFirst + case q"$pack.AscNullsLast" => AscNullsLast + case q"$pack.DescNullsLast" => DescNullsLast + } + implicit val propertyAliasUnliftable: Unliftable[PropertyAlias] = Unliftable[PropertyAlias] { case q"$pack.PropertyAlias.apply(${ a: String }, ${ b: String })" => PropertyAlias(a, b) } diff --git a/quill-core/src/test/scala/io/getquill/PackageSpec.scala b/quill-core/src/test/scala/io/getquill/PackageSpec.scala index 4899006f47..fcbc1cd233 100644 --- a/quill-core/src/test/scala/io/getquill/PackageSpec.scala +++ b/quill-core/src/test/scala/io/getquill/PackageSpec.scala @@ -67,4 +67,16 @@ class PackageSpec extends Spec { unquote(qr1) } } + + "fails if ord is unquoted ouside of a quotation" in { + val e = intercept[NonQuotedException] { + Ord.asc[Int] + } + } + + "fails if orderingToOrd is unquoted ouside of a quotation" in { + val e = intercept[NonQuotedException] { + orderingToOrd[Int] + } + } } diff --git a/quill-core/src/test/scala/io/getquill/ast/AstShowSpec.scala b/quill-core/src/test/scala/io/getquill/ast/AstShowSpec.scala index d274111db3..facd5497d4 100644 --- a/quill-core/src/test/scala/io/getquill/ast/AstShowSpec.scala +++ b/quill-core/src/test/scala/io/getquill/ast/AstShowSpec.scala @@ -70,12 +70,56 @@ class AstShowSpec extends Spec { } } - "shows sorted queries" in { - val q = quote { - qr1.sortBy(t => t.i).reverse + "shows sorted queries" - { + "asc" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.asc) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.asc)""" + } + "desc" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.desc) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.desc)""" + } + "ascNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.ascNullsFirst) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.ascNullsFirst)""" + } + "descNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.descNullsFirst) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.descNullsFirst)""" + } + "ascNullsLast" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.ascNullsLast) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.ascNullsLast)""" + } + "descNullsLast" in { + val q = quote { + qr1.sortBy(t => t.i)(Ord.descNullsLast) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => t.i)(Ord.descNullsLast)""" + } + "tuple" in { + val q = quote { + qr1.sortBy(t => (t.i, t.s))(Ord(Ord.descNullsLast, Ord.ascNullsLast)) + } + (q.ast: Ast).show mustEqual + """query[TestEntity].sortBy(t => (t.i, t.s))(Ord(Ord.descNullsLast, Ord.ascNullsLast))""" } - (q.ast: Ast).show mustEqual - """query[TestEntity].sortBy(t => t.i).reverse""" } "shows grouped queries" in { diff --git a/quill-core/src/test/scala/io/getquill/ast/StatefulTransformerSpec.scala b/quill-core/src/test/scala/io/getquill/ast/StatefulTransformerSpec.scala index 9906aafdae..db9c17e240 100644 --- a/quill-core/src/test/scala/io/getquill/ast/StatefulTransformerSpec.scala +++ b/quill-core/src/test/scala/io/getquill/ast/StatefulTransformerSpec.scala @@ -48,10 +48,10 @@ class StatefulTransformerSpec extends Spec { } } "sortBy" in { - val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("c")) + val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("c"), AscNullsFirst) Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) match { case (at, att) => - at mustEqual SortBy(Ident("a'"), Ident("b"), Ident("c'")) + at mustEqual SortBy(Ident("a'"), Ident("b"), Ident("c'"), AscNullsFirst) att.state mustEqual List(Ident("a"), Ident("c")) } } @@ -71,14 +71,6 @@ class StatefulTransformerSpec extends Spec { att.state mustEqual List(Ident("a")) } } - "reverse" in { - val ast: Ast = Reverse(SortBy(Ident("a"), Ident("b"), Ident("c"))) - Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) match { - case (at, att) => - at mustEqual Reverse(SortBy(Ident("a'"), Ident("b"), Ident("c'"))) - att.state mustEqual List(Ident("a"), Ident("c")) - } - } "take" in { val ast: Ast = Take(Ident("a"), Ident("b")) Subject(Nil, Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"))(ast) match { diff --git a/quill-core/src/test/scala/io/getquill/ast/StatelessTransformerSpec.scala b/quill-core/src/test/scala/io/getquill/ast/StatelessTransformerSpec.scala index c296ce66f5..6e77f8c0f9 100644 --- a/quill-core/src/test/scala/io/getquill/ast/StatelessTransformerSpec.scala +++ b/quill-core/src/test/scala/io/getquill/ast/StatelessTransformerSpec.scala @@ -33,9 +33,9 @@ class StatelessTransformerSpec extends Spec { FlatMap(Ident("a'"), Ident("b"), Ident("c'")) } "sortBy" in { - val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("c")) + val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("c"), AscNullsFirst) Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) mustEqual - SortBy(Ident("a'"), Ident("b"), Ident("c'")) + SortBy(Ident("a'"), Ident("b"), Ident("c'"), AscNullsFirst) } "groupBy" in { val ast: Ast = GroupBy(Ident("a"), Ident("b"), Ident("c")) @@ -47,11 +47,6 @@ class StatelessTransformerSpec extends Spec { Subject(Ident("a") -> Ident("a'"))(ast) mustEqual Aggregation(AggregationOperator.`max`, Ident("a'")) } - "reverse" in { - val ast: Ast = Reverse(SortBy(Ident("a"), Ident("b"), Ident("c"))) - Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"), Ident("c") -> Ident("c'"))(ast) mustEqual - Reverse(SortBy(Ident("a'"), Ident("b"), Ident("c'"))) - } "take" in { val ast: Ast = Take(Ident("a"), Ident("b")) Subject(Ident("a") -> Ident("a'"), Ident("b") -> Ident("b'"))(ast) mustEqual diff --git a/quill-core/src/test/scala/io/getquill/norm/AttachToEntitySpec.scala b/quill-core/src/test/scala/io/getquill/norm/AttachToEntitySpec.scala index 66eee7b33a..a56a917d93 100644 --- a/quill-core/src/test/scala/io/getquill/norm/AttachToEntitySpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/AttachToEntitySpec.scala @@ -5,7 +5,7 @@ import io.getquill.ast._ class AttachToEntitySpec extends Spec { - val attachToEntity = AttachToEntity(SortBy(_, _, Constant(1))) _ + val attachToEntity = AttachToEntity(SortBy(_, _, Constant(1), AscNullsFirst)) _ "attaches clause to the root of the query (entity)" - { "query is the entity" in { @@ -51,15 +51,6 @@ class AttachToEntitySpec extends Spec { } attachToEntity(q.ast) mustEqual n.ast } - "reverse" in { - val q = quote { - qr1.sortBy(b => b.s).reverse - } - val n = quote { - qr1.sortBy(b => 1).sortBy(b => b.s).reverse - } - attachToEntity(q.ast) mustEqual n.ast - } "take" in { val q = quote { qr1.sortBy(b => b.s).take(1) diff --git a/quill-core/src/test/scala/io/getquill/norm/BetaReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/BetaReductionSpec.scala index c75ca5454c..c98043d485 100644 --- a/quill-core/src/test/scala/io/getquill/norm/BetaReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/BetaReductionSpec.scala @@ -41,17 +41,13 @@ class BetaReductionSpec extends Spec { BetaReduction(ast, Ident("b") -> Ident("b'")) mustEqual ast } "sortBy" in { - val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("b")) + val ast: Ast = SortBy(Ident("a"), Ident("b"), Ident("b"), AscNullsFirst) BetaReduction(ast, Ident("b") -> Ident("b'")) mustEqual ast } "groupBy" in { val ast: Ast = GroupBy(Ident("a"), Ident("b"), Ident("b")) BetaReduction(ast, Ident("b") -> Ident("b'")) mustEqual ast } - "reverse" in { - val ast: Ast = Reverse(SortBy(Ident("a"), Ident("b"), Ident("b"))) - BetaReduction(ast, Ident("b") -> Ident("b'")) mustEqual ast - } "outer join" in { val ast: Ast = OuterJoin(LeftJoin, Ident("a"), Ident("b"), Ident("c"), Ident("d"), Tuple(List(Ident("c"), Ident("d")))) BetaReduction(ast, Ident("c") -> Ident("c'"), Ident("d") -> Ident("d'")) mustEqual ast diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala index bc94dad7f3..6b9f2dfe69 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala @@ -59,15 +59,6 @@ class NormalizeNestedStructuresSpec extends Spec { } NormalizeNestedStructures.unapply(q.ast) mustEqual Some(n.ast) } - "reverse" in { - val q = quote { - qr1.sortBy(t => (t.i, t.s)._1).reverse - } - val n = quote { - qr1.sortBy(t => t.i).reverse - } - NormalizeNestedStructures.unapply(q.ast) mustEqual Some(n.ast) - } "take" in { val q = quote { qr1.sortBy(t => (t.i, t.s)._1).take((1, 2)._2) @@ -160,12 +151,6 @@ class NormalizeNestedStructuresSpec extends Spec { } NormalizeNestedStructures.unapply(q.ast) mustEqual None } - "reverse" in { - val q = quote { - qr1.sortBy(t => t.i).reverse - } - NormalizeNestedStructures.unapply(q.ast) mustEqual None - } "groupBy" in { val q = quote { qr1.groupBy(t => t.i) diff --git a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala index 1a709c623b..5ba0648e11 100644 --- a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala @@ -6,32 +6,11 @@ class OrderTermsSpec extends Spec { "doesn't reorder groupBy.map" in { val q = quote { - qr1.map(b => b.s).sortBy(b => b).reverse + qr1.map(b => b.s).sortBy(b => b) } OrderTerms.unapply(q.ast) mustEqual None } - "reverse" - { - "a.reverse.filter(b => c)" in { - val q = quote { - qr1.sortBy(b => b.s).reverse.filter(d => d == "s2") - } - val n = quote { - qr1.sortBy(b => b.s).filter(d => d == "s2").reverse - } - OrderTerms.unapply(q.ast) mustEqual Some(n.ast) - } - "a.map(b => c).reverse" in { - val q = quote { - qr1.sortBy(t => t.s).map(t => t.s).reverse - } - val n = quote { - qr1.sortBy(t => t.s).reverse.map(t => t.s) - } - OrderTerms.unapply(q.ast) mustEqual Some(n.ast) - } - } - "sortBy" - { "a.sortBy(b => c).filter(d => e)" in { val q = quote { diff --git a/quill-core/src/test/scala/io/getquill/norm/QueryGenerator.scala b/quill-core/src/test/scala/io/getquill/norm/QueryGenerator.scala index 1d1a0e8ad7..14b85f41f3 100644 --- a/quill-core/src/test/scala/io/getquill/norm/QueryGenerator.scala +++ b/quill-core/src/test/scala/io/getquill/norm/QueryGenerator.scala @@ -12,16 +12,15 @@ class QueryGenerator(seed: Int) { if (i <= 2) { Entity(string(3)) } else { - random.nextInt(9) match { + random.nextInt(8) match { case 0 => map(i) case 1 => flatMap(i) case 2 => filter(i) case 3 => sortBy(i) - case 4 => reverse(i) - case 5 => take(i) - case 6 => drop(i) - case 7 => groupBy(i) - case 8 => aggregation(i) + case 4 => take(i) + case 5 => drop(i) + case 6 => groupBy(i) + case 7 => aggregation(i) } } @@ -48,12 +47,9 @@ class QueryGenerator(seed: Int) { private def sortBy(i: Int) = { val id = ident - SortBy(apply(i), id, Property(id, string)) + SortBy(apply(i), id, Property(id, string), AscNullsFirst) } - private def reverse(i: Int) = - Reverse(sortBy(i - 1)) - private def groupBy(i: Int) = { val id = ident val group = GroupBy(apply(i), id, Property(id, string)) diff --git a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala index 67f64d9668..1c2e9e83a5 100644 --- a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala @@ -4,16 +4,6 @@ import io.getquill._ class SymbolicReductionSpec extends Spec { - "a.reverse.reverse" in { - val q = quote { - qr1.sortBy(b => b.s).reverse.reverse - } - val n = quote { - qr1.sortBy(b => b.s) - } - SymbolicReduction.unapply(q.ast) mustEqual Some(n.ast) - } - "a.filter(b => c).flatMap(d => e.$)" - { "e is an entity" in { val q = quote { @@ -54,7 +44,7 @@ class SymbolicReductionSpec extends Spec { } SymbolicReduction.unapply(q.ast) mustEqual Some(n.ast) } - + "a.unionAll(b).flatMap(c => d)" in { val q = quote { qr1.unionAll(qr1.filter(t => t.i == 1)).flatMap(c => qr2) diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala index 39d4eb8a2d..10ad71e33b 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala @@ -58,15 +58,6 @@ class DealiasSpec extends Spec { } Dealias(q.ast) mustEqual q.ast } - "reverse" in { - val q = quote { - qr1.sortBy(a => a.s).reverse.map(b => b.s) - } - val n = quote { - qr1.sortBy(a => a.s).reverse.map(a => a.s) - } - Dealias(q.ast) mustEqual n.ast - } "take" in { val q = quote { qr1.filter(a => a.s == "s").take(10).map(b => b.s) diff --git a/quill-core/src/test/scala/io/getquill/norm/select/ExtractSelectSpec.scala b/quill-core/src/test/scala/io/getquill/norm/select/ExtractSelectSpec.scala index 3c90be5b9b..4e25ffd514 100644 --- a/quill-core/src/test/scala/io/getquill/norm/select/ExtractSelectSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/select/ExtractSelectSpec.scala @@ -48,19 +48,6 @@ class ExtractSelectSpec extends Spec { select mustEqual Ident("x") } } - "reversed query" in { - val q = quote { - qr1.sortBy(b => b.s).reverse - } - val m = quote { - qr1.sortBy(b => b.s).reverse.map(b => b) - } - ExtractSelect(q.ast) match { - case (query, select) => - query mustEqual m.ast - select mustEqual Ident("b") - } - } "limited query" in { val q = quote { qr1.take(10).map(t => t.s) diff --git a/quill-core/src/test/scala/io/getquill/quotation/FreeVariablesSpec.scala b/quill-core/src/test/scala/io/getquill/quotation/FreeVariablesSpec.scala index 27117b82f5..a2c3ccffaa 100644 --- a/quill-core/src/test/scala/io/getquill/quotation/FreeVariablesSpec.scala +++ b/quill-core/src/test/scala/io/getquill/quotation/FreeVariablesSpec.scala @@ -59,14 +59,6 @@ class FreeVariablesSpec extends Spec { } FreeVariables(q.ast) mustEqual Set(Ident("s")) } - "reverse" in { - val s = "s" - val q = - quote { - qr1.sortBy(_ => s).reverse - } - FreeVariables(q.ast) mustEqual Set(Ident("s")) - } "take" in { val s = 10 val q = @@ -110,12 +102,6 @@ class FreeVariablesSpec extends Spec { } FreeVariables(q.ast) mustBe empty } - "reverse" in { - val q = quote { - qr1.sortBy(b => b.s).reverse - } - FreeVariables(q.ast) mustBe empty - } "conditional outer join" in { val q = quote { qr1.leftJoin(qr2).on((a, b) => a.s == b.s) diff --git a/quill-core/src/test/scala/io/getquill/quotation/QuotationSpec.scala b/quill-core/src/test/scala/io/getquill/quotation/QuotationSpec.scala index 05b6118b53..fd9d28f4f4 100644 --- a/quill-core/src/test/scala/io/getquill/quotation/QuotationSpec.scala +++ b/quill-core/src/test/scala/io/getquill/quotation/QuotationSpec.scala @@ -50,11 +50,63 @@ class QuotationSpec extends Spec { } quote(unquote(q)).ast mustEqual FlatMap(Entity("TestEntity"), Ident("t"), Entity("TestEntity2")) } - "sortBy" in { - val q = quote { - qr1.sortBy(t => t.s) + "sortBy" - { + "default ordering" in { + val q = quote { + qr1.sortBy(t => t.s) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), AscNullsFirst) + } + "asc" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.asc) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), Asc) + } + "desc" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.desc) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), Desc) + } + "ascNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsFirst) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), AscNullsFirst) + } + "descNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsFirst) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), DescNullsFirst) + } + "ascNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsLast) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), AscNullsLast) + } + "descNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsLast) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"), DescNullsLast) + } + "tuple" - { + "simple" in { + val q = quote { + qr1.sortBy(t => (t.s, t.i))(Ord.desc) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Tuple(List(Property(Ident("t"), "s"), Property(Ident("t"), "i"))), Desc) + } + "by element" in { + val q = quote { + qr1.sortBy(t => (t.s, t.i))(Ord(Ord.desc, Ord.asc)) + } + quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Tuple(List(Property(Ident("t"), "s"), Property(Ident("t"), "i"))), TupleOrdering(List(Desc, Asc))) + } } - quote(unquote(q)).ast mustEqual SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s")) } "groupBy" in { val q = quote { @@ -111,12 +163,6 @@ class QuotationSpec extends Spec { } } - "reverse" in { - val q = quote { - qr1.sortBy(t => t.s).reverse - } - quote(unquote(q)).ast mustEqual Reverse(SortBy(Entity("TestEntity"), Ident("t"), Property(Ident("t"), "s"))) - } "take" in { val q = quote { qr1.take(10) diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/FlattenGroupByAggregation.scala b/quill-sql/src/main/scala/io/getquill/source/sql/FlattenGroupByAggregation.scala index be84373d54..bfe78120f0 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/FlattenGroupByAggregation.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/FlattenGroupByAggregation.scala @@ -28,8 +28,7 @@ case class FlattenGroupByAggregation(agg: Ident) extends StatelessTransformer { case Map(a, b, c) => isGroupByAggregation(a) case FlatMap(a, b, c) => isGroupByAggregation(a) case Filter(a, b, c) => isGroupByAggregation(a) - case SortBy(a, b, c) => isGroupByAggregation(a) - case Reverse(a) => isGroupByAggregation(a) + case SortBy(a, b, c, d) => isGroupByAggregation(a) case Take(a, b) => isGroupByAggregation(a) case Drop(a, b) => isGroupByAggregation(a) case Union(a, b) => isGroupByAggregation(a) || isGroupByAggregation(b) diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/RenameProperties.scala b/quill-sql/src/main/scala/io/getquill/source/sql/RenameProperties.scala index 1fac654452..0af86b0625 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/RenameProperties.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/RenameProperties.scala @@ -13,7 +13,7 @@ case class RenameProperties(state: collection.Map[Ident, collection.Map[String, case Filter(q: Entity, x, p) => apply(q, x, p)(Filter) - case SortBy(q: Entity, x, p) => apply(q, x, p)(SortBy) + case SortBy(q: Entity, x, p, o) => apply(q, x, p)(SortBy(_, _, _, o)) case q @ OuterJoin(t, a: Entity, b: Entity, iA, iB, o) => val ((_, _, or), ort) = apply(a, iA, o)((_, _, _)) diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/SqlQuery.scala b/quill-sql/src/main/scala/io/getquill/source/sql/SqlQuery.scala index 1ddef8b19c..29ffcbba42 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/SqlQuery.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/SqlQuery.scala @@ -3,8 +3,9 @@ package io.getquill.source.sql import io.getquill.ast._ import io.getquill.norm.BetaReduction import io.getquill.util.Messages.fail +import io.getquill.ast.PropertyOrdering -case class OrderByCriteria(property: Property, desc: Boolean) +case class OrderByCriteria(property: Property, ordering: PropertyOrdering) sealed trait Source case class TableSource(entity: Entity, alias: String) extends Source @@ -107,20 +108,9 @@ object SqlQuery { where = Some(p), select = aliasSelect) - case Reverse(SortBy(q, Ident(alias), p)) => + case SortBy(q, Ident(alias), p, o) => val b = base(q, alias) - val criterias = orderByCriterias(p, reverse = true) - if (b.orderBy.isEmpty) - b.copy(orderBy = criterias) - else - FlattenSqlQuery( - from = QuerySource(apply(q), alias) :: Nil, - orderBy = criterias, - select = aliasSelect) - - case SortBy(q, Ident(alias), p) => - val b = base(q, alias) - val criterias = orderByCriterias(p, reverse = false) + val criterias = orderByCriterias(p, o) if (b.orderBy.isEmpty) b.copy(orderBy = criterias) else @@ -186,10 +176,11 @@ object SqlQuery { case other => fail(s"Invalid group by criteria $ast") } - private def orderByCriterias(ast: Ast, reverse: Boolean): List[OrderByCriteria] = - ast match { - case a: Property => List(OrderByCriteria(a, reverse)) - case Tuple(properties) => properties.map(orderByCriterias(_, reverse)).flatten - case other => fail(s"Invalid order by criteria $ast") + private def orderByCriterias(ast: Ast, ordering: Ordering): List[OrderByCriteria] = + (ast, ordering) match { + case (a: Property, o: PropertyOrdering) => List(OrderByCriteria(a, o)) + case (Tuple(properties), ord: PropertyOrdering) => properties.map(orderByCriterias(_, ord)).flatten + case (Tuple(properties), TupleOrdering(ord)) => properties.zip(ord).map { case (a, o) => orderByCriterias(a, o) }.flatten + case other => fail(s"Invalid order by criteria $ast") } } diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/MySQLDialect.scala b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/MySQLDialect.scala index 2984c5c619..9ec3997aef 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/MySQLDialect.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/MySQLDialect.scala @@ -3,6 +3,7 @@ package io.getquill.source.sql.idiom import io.getquill.ast._ import io.getquill.naming.NamingStrategy import io.getquill.util.Show._ +import io.getquill.source.sql.OrderByCriteria trait MySQLDialect extends SqlIdiom @@ -16,5 +17,16 @@ trait MySQLDialect case BinaryOperation(a, StringOperator.`+`, b) => s"CONCAT(${a.show}, ${b.show})" case other => super.operationShow.show(other) } + + override implicit def orderByCriteriaShow(implicit strategy: NamingStrategy): Show[OrderByCriteria] = new Show[OrderByCriteria] { + def show(criteria: OrderByCriteria) = + criteria match { + case OrderByCriteria(prop, AscNullsFirst) => s"${prop.show} ASC" + case OrderByCriteria(prop, DescNullsFirst) => s"ISNULL(${prop.show}) DESC, ${prop.show} DESC" + case OrderByCriteria(prop, AscNullsLast) => s"ISNULL(${prop.show}) ASC, ${prop.show} ASC" + case OrderByCriteria(prop, DescNullsLast) => s"${prop.show} DESC" + } + } } + object MySQLDialect extends MySQLDialect diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/NullsOrderingClause.scala b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/NullsOrderingClause.scala deleted file mode 100644 index af01c0fc36..0000000000 --- a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/NullsOrderingClause.scala +++ /dev/null @@ -1,18 +0,0 @@ -package io.getquill.source.sql.idiom - -import io.getquill.naming.NamingStrategy -import io.getquill.source.sql.OrderByCriteria -import io.getquill.util.Show.Shower - -trait NullsOrderingClause { - this: SqlIdiom => - - override protected def showOrderBy(criterias: List[OrderByCriteria])(implicit strategy: NamingStrategy) = - s" ORDER BY ${criterias.map(showCriteria).mkString(", ")}" - - private def showCriteria(criteria: OrderByCriteria)(implicit strategy: NamingStrategy) = - criteria.desc match { - case false => s"${criteria.show} NULLS FIRST" - case true => s"${criteria.show} NULLS LAST" - } -} diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/PostgresDialect.scala b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/PostgresDialect.scala index a24bff2c71..86601f83c5 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/PostgresDialect.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/PostgresDialect.scala @@ -3,12 +3,11 @@ package io.getquill.source.sql.idiom import java.util.concurrent.atomic.AtomicInteger trait PostgresDialect - extends SqlIdiom - with NullsOrderingClause { + extends SqlIdiom { private[idiom] val preparedStatementId = new AtomicInteger - override def prepare(sql: String) = + override def prepare(sql: String) = s"PREPARE p${preparedStatementId.incrementAndGet} AS ${positionalVariables(sql)}" private def positionalVariables(sql: String) = @@ -20,4 +19,4 @@ trait PostgresDialect }._2 } -object PostgresDialect extends PostgresDialect \ No newline at end of file +object PostgresDialect extends PostgresDialect diff --git a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/SqlIdiom.scala b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/SqlIdiom.scala index 385ae0020d..5c054d6db9 100644 --- a/quill-sql/src/main/scala/io/getquill/source/sql/idiom/SqlIdiom.scala +++ b/quill-sql/src/main/scala/io/getquill/source/sql/idiom/SqlIdiom.scala @@ -162,8 +162,12 @@ trait SqlIdiom { implicit def orderByCriteriaShow(implicit strategy: NamingStrategy): Show[OrderByCriteria] = new Show[OrderByCriteria] { def show(criteria: OrderByCriteria) = criteria match { - case OrderByCriteria(prop, true) => s"${prop.show} DESC" - case OrderByCriteria(prop, false) => prop.show + case OrderByCriteria(prop, Asc) => s"${prop.show} ASC" + case OrderByCriteria(prop, Desc) => s"${prop.show} DESC" + case OrderByCriteria(prop, AscNullsFirst) => s"${prop.show} ASC NULLS FIRST" + case OrderByCriteria(prop, DescNullsFirst) => s"${prop.show} DESC NULLS FIRST" + case OrderByCriteria(prop, AscNullsLast) => s"${prop.show} ASC NULLS LAST" + case OrderByCriteria(prop, DescNullsLast) => s"${prop.show} DESC NULLS LAST" } } diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/FlattenGroupByAggregationSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/FlattenGroupByAggregationSpec.scala index 190cdf0d13..da0607d10a 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/FlattenGroupByAggregationSpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/FlattenGroupByAggregationSpec.scala @@ -70,15 +70,6 @@ class FlattenGroupByAggregationSpec extends Spec { FlattenGroupByAggregation(Ident("e"))(q.ast.body) } } - "reverse" in { - val q = quote { - (e: Query[TestEntity]) => - e.sortBy(_.i).reverse.size - } - val e = intercept[IllegalStateException] { - FlattenGroupByAggregation(Ident("e"))(q.ast.body) - } - } "take" in { val q = quote { (e: Query[TestEntity]) => diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/RenamePropertiesSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/RenamePropertiesSpec.scala index 522e90cdb4..5a079fbaf7 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/RenamePropertiesSpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/RenamePropertiesSpec.scala @@ -68,14 +68,14 @@ class RenamePropertiesSpec extends Spec { e.sortBy(t => t.i) } mirrorSource.run(q).sql mustEqual - "SELECT t.field_s, t.field_i, t.l, t.o FROM test_entity t ORDER BY t.field_i" + "SELECT t.field_s, t.field_i, t.l, t.o FROM test_entity t ORDER BY t.field_i ASC NULLS FIRST" } "transitive" in { val q = quote { e.sortBy(t => t.l).map(t => t.s) } mirrorSource.run(q).sql mustEqual - "SELECT t.field_s FROM test_entity t ORDER BY t.l" + "SELECT t.field_s FROM test_entity t ORDER BY t.l ASC NULLS FIRST" } } "outer join" - { diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/SqlQuerySpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/SqlQuerySpec.scala index b9d7146881..775f6ba11a 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/SqlQuerySpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/SqlQuerySpec.scala @@ -72,53 +72,56 @@ class SqlQuerySpec extends Spec { qr1.sortBy(t => t.s).map(t => t.s) } SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM TestEntity t ORDER BY t.s" + "SELECT t.s FROM TestEntity t ORDER BY t.s ASC NULLS FIRST" } "with filter" in { val q = quote { qr1.filter(t => t.s == "s").sortBy(t => t.s).map(t => (t.i)) } SqlQuery(q.ast).show mustEqual - "SELECT t.i FROM TestEntity t WHERE t.s = 's' ORDER BY t.s" - } - "with reverse" in { - val q = quote { - qr1.sortBy(t => t.s).reverse.map(t => t.s) - } - SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM TestEntity t ORDER BY t.s DESC" + "SELECT t.i FROM TestEntity t WHERE t.s = 's' ORDER BY t.s ASC NULLS FIRST" } "with outer filter" in { val q = quote { qr1.sortBy(t => t.s).filter(t => t.s == "s").map(t => t.s) } SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM TestEntity t WHERE t.s = 's' ORDER BY t.s" + "SELECT t.s FROM TestEntity t WHERE t.s = 's' ORDER BY t.s ASC NULLS FIRST" } "with flatMap" in { val q = quote { qr1.sortBy(t => t.s).flatMap(t => qr2.map(t => t.s)) } SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM (SELECT t.* FROM TestEntity t ORDER BY t.s) t, TestEntity2 t" + "SELECT t.s FROM (SELECT t.* FROM TestEntity t ORDER BY t.s ASC NULLS FIRST) t, TestEntity2 t" } - "tuple criteria" in { - val q = quote { - qr1.sortBy(t => (t.s, t.i)).map(t => t.s) + "tuple criteria" - { + "single ordering" in { + val q = quote { + qr1.sortBy(t => (t.s, t.i))(Ord.asc).map(t => t.s) + } + SqlQuery(q.ast).show mustEqual + "SELECT t.s FROM TestEntity t ORDER BY t.s ASC, t.i ASC" + } + "ordering per column" in { + val q = quote { + qr1.sortBy(t => (t.s, t.i))(Ord(Ord.asc, Ord.desc)).map(t => t.s) + } + SqlQuery(q.ast).show mustEqual + "SELECT t.s FROM TestEntity t ORDER BY t.s ASC, t.i DESC" } - SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM TestEntity t ORDER BY t.s, t.i" } "multiple sortBy" in { val q = quote { - qr1.sortBy(t => (t.s, t.i)).reverse.sortBy(t => t.l).map(t => t.s) + qr1.sortBy(t => (t.s, t.i)).sortBy(t => t.l).map(t => t.s) } SqlQuery(q.ast).show mustEqual - "SELECT t.s FROM (SELECT t.* FROM TestEntity t ORDER BY t.s DESC, t.i DESC) t ORDER BY t.l" + "SELECT t.s FROM (SELECT t.* FROM TestEntity t ORDER BY t.s ASC NULLS FIRST, t.i ASC NULLS FIRST) t ORDER BY t.l ASC NULLS FIRST" } "fails if the sortBy criteria is malformed" in { + implicit val o: Ordering[TestEntity] = null val q = quote { - qr1.sortBy(t => t)(null) + qr1.sortBy(t => t) } val e = intercept[IllegalStateException] { SqlQuery(q.ast) diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/MySQLDialectSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/MySQLDialectSpec.scala index 936af5061e..86e82b9923 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/MySQLDialectSpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/MySQLDialectSpec.scala @@ -28,4 +28,35 @@ class MySQLDialectSpec extends Spec { MySQLDialect.prepare(sql) mustEqual s"PREPARE p${sql.hashCode.abs} FROM '$sql'" } + + "workaround missing nulls ordering feature in mysql" - { + "ascNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsFirst) + } + (q.ast: Ast).show mustEqual + "SELECT t.* FROM TestEntity t ORDER BY t.s ASC" + } + "descNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsFirst) + } + (q.ast: Ast).show mustEqual + "SELECT t.* FROM TestEntity t ORDER BY ISNULL(t.s) DESC, t.s DESC" + } + "ascNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsLast) + } + (q.ast: Ast).show mustEqual + "SELECT t.* FROM TestEntity t ORDER BY ISNULL(t.s) ASC, t.s ASC" + } + "descNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsLast) + } + (q.ast: Ast).show mustEqual + "SELECT t.* FROM TestEntity t ORDER BY t.s DESC" + } + } } diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/NullsFirstClauseSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/NullsFirstClauseSpec.scala deleted file mode 100644 index 136e8f001d..0000000000 --- a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/NullsFirstClauseSpec.scala +++ /dev/null @@ -1,38 +0,0 @@ -package io.getquill.source.sql.idiom - -import io.getquill._ -import io.getquill.naming.Literal -import io.getquill.source.sql.SqlQuery -import io.getquill.util.Show._ - -class NullsFirstClauseSpec extends Spec { - - val subject = new SqlIdiom with NullsOrderingClause { - def prepare(sql: String) = sql - } - - implicit val naming = new Literal {} - - import subject._ - - "adds the nulls ordering clause" - { - "asc" in { - val q = quote { - qr1.sortBy(t => t.s).map(t => t.s) - } - SqlQuery(q.ast).show mustEqual "SELECT t.s FROM TestEntity t ORDER BY t.s NULLS FIRST" - } - "desc" in { - val q = quote { - qr1.sortBy(t => t.s).reverse.map(t => t.s) - } - SqlQuery(q.ast).show mustEqual "SELECT t.s FROM TestEntity t ORDER BY t.s DESC NULLS LAST" - } - "mixed" in { - val q = quote { - qr1.sortBy(t => t.i).sortBy(t => t.s).reverse.map(t => t.s) - } - SqlQuery(q.ast).show mustEqual "SELECT t.s FROM (SELECT t.* FROM TestEntity t ORDER BY t.i NULLS FIRST) t ORDER BY t.s DESC NULLS LAST" - } - } -} diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/PostgresDialectSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/PostgresDialectSpec.scala index 5eabd4219b..ac278814be 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/PostgresDialectSpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/PostgresDialectSpec.scala @@ -4,10 +4,6 @@ import io.getquill.Spec class PostgresDialectSpec extends Spec { - "mixes the nulls ordering clause" in { - PostgresDialect.isInstanceOf[NullsOrderingClause] mustEqual true - } - "supports the `prepare` statement" in { val sql = "test" PostgresDialect.prepare(sql) mustEqual diff --git a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/SqlIdiomSpec.scala b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/SqlIdiomSpec.scala index d57ea18513..fb9b98993f 100644 --- a/quill-sql/src/test/scala/io/getquill/source/sql/idiom/SqlIdiomSpec.scala +++ b/quill-sql/src/test/scala/io/getquill/source/sql/idiom/SqlIdiomSpec.scala @@ -41,19 +41,68 @@ class SqlIdiomSpec extends Spec { qr1.filter(t => t.s != null).sortBy(_.s) } mirrorSource.run(q).sql mustEqual - "SELECT t.s, t.i, t.l, t.o FROM TestEntity t WHERE t.s IS NOT NULL ORDER BY t.s" + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t WHERE t.s IS NOT NULL ORDER BY t.s ASC NULLS FIRST" } "nested" in { val q = quote { for { - a <- qr1.sortBy(t => t.s).reverse + a <- qr1.sortBy(t => t.s) b <- qr2.sortBy(t => t.i) } yield { (a.s, b.i) } } mirrorSource.run(q).sql mustEqual - "SELECT t.s, t1.i FROM (SELECT t.s FROM TestEntity t ORDER BY t.s DESC) t, (SELECT t1.i FROM TestEntity2 t1 ORDER BY t1.i) t1" + "SELECT t.s, t1.i FROM (SELECT t.s FROM TestEntity t ORDER BY t.s ASC NULLS FIRST) t, (SELECT t1.i FROM TestEntity2 t1 ORDER BY t1.i ASC NULLS FIRST) t1" + } + "asc" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.asc) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s ASC" + } + "desc" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.desc) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s DESC" + } + "ascNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsFirst) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s ASC NULLS FIRST" + } + "descNullsFirst" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsFirst) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s DESC NULLS FIRST" + } + "ascNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.ascNullsLast) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s ASC NULLS LAST" + } + "descNullsLast" in { + val q = quote { + qr1.sortBy(t => t.s)(Ord.descNullsLast) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.s DESC NULLS LAST" + } + "tuple" in { + val q = quote { + qr1.sortBy(t => (t.i, t.s))(Ord(Ord.desc, Ord.asc)) + } + mirrorSource.run(q).sql mustEqual + "SELECT t.s, t.i, t.l, t.o FROM TestEntity t ORDER BY t.i DESC, t.s ASC" } } "grouped" - {