Skip to content

Commit 1a30b60

Browse files
committed
Replace SheathLeafClauses with simpler SqlQuery-oriented transforms
1 parent 7755f31 commit 1a30b60

File tree

5 files changed

+129
-14
lines changed

5 files changed

+129
-14
lines changed

quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,27 @@ import io.getquill.sql.Common.ContainsImpurities
1111

1212
final case class OrderByCriteria(ast: Ast, ordering: PropertyOrdering)
1313

14-
sealed trait FromContext { def quat: Quat }
14+
sealed trait FromContext {
15+
def quat: Quat
16+
def mapAst(f: Ast => Ast): FromContext = this match {
17+
case c: TableContext => c
18+
case QueryContext(query, alias) => QueryContext(query.mapAst(f), alias)
19+
case c: InfixContext => c.mapAsts(f)
20+
case JoinContext(t, a, b, on) => JoinContext(t, a.mapAst(f), b.mapAst(f), f(on))
21+
case FlatJoinContext(t, a, on) => FlatJoinContext(t, a.mapAst(f), f(on))
22+
}
23+
}
1524
final case class TableContext(entity: Entity, alias: Ident) extends FromContext {
1625
override def quat: Quat = entity.quat
1726
}
1827
final case class QueryContext(query: SqlQuery, alias: Ident) extends FromContext {
1928
override def quat: Quat = query.quat
2029
}
21-
final case class InfixContext(infix: Infix, alias: Ident) extends FromContext { override def quat: Quat = infix.quat }
30+
final case class InfixContext(infix: Infix, alias: Ident) extends FromContext {
31+
override def quat: Quat = infix.quat
32+
def mapAsts(f: Ast => Ast): InfixContext =
33+
copy(infix = infix.copy(params = infix.params.map(f)))
34+
}
2235
final case class JoinContext(t: JoinType, a: FromContext, b: FromContext, on: Ast) extends FromContext {
2336
override def quat: Quat = Quat.Tuple(a.quat, b.quat)
2437
}
@@ -29,6 +42,16 @@ final case class FlatJoinContext(t: JoinType, a: FromContext, on: Ast) extends F
2942
sealed trait SqlQuery {
3043
def quat: Quat
3144

45+
def mapAst(f: Ast => Ast): SqlQuery =
46+
this match {
47+
case flatten: FlattenSqlQuery =>
48+
flatten.mapAsts(f)
49+
case SetOperationSqlQuery(a, op, b) =>
50+
SetOperationSqlQuery(a.mapAst(f), op, b.mapAst(f))(quat)
51+
case UnaryOperationSqlQuery(op, q) =>
52+
UnaryOperationSqlQuery(op, q.mapAst(f))(quat)
53+
}
54+
3255
override def toString: String = {
3356
import io.getquill.MirrorSqlDialect._
3457
import io.getquill.idiom.StatementInterpolator._
@@ -83,6 +106,17 @@ final case class FlattenSqlQuery(
83106
)(quatType: Quat)
84107
extends SqlQuery {
85108
override def quat: Quat = quatType
109+
110+
def mapAsts(f: Ast => Ast): FlattenSqlQuery =
111+
copy(
112+
from = from.map(_.mapAst(f)),
113+
where = where.map(f),
114+
groupBy = groupBy.map(f),
115+
orderBy = orderBy.map(o => o.copy(ast = f(o.ast))),
116+
limit = limit.map(f),
117+
offset = offset.map(f),
118+
select = select.map(s => s.copy(ast = f(s.ast)))
119+
)(quatType)
86120
}
87121

88122
object TakeDropFlatten {

quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import io.getquill.norm.ConcatBehavior.AnsiConcat
1717
import io.getquill.norm.EqualityBehavior.AnsiEquality
1818
import io.getquill.norm.{ConcatBehavior, EqualityBehavior, ExpandReturning, NormalizeCaching, ProductAggregationToken}
1919
import io.getquill.quat.Quat
20-
import io.getquill.sql.norm.{HideTopLevelFilterAlias, NormalizeFilteredActionAliases, RemoveExtraAlias, RemoveUnusedSelects}
20+
import io.getquill.sql.norm.{HideTopLevelFilterAlias, NormalizeFilteredActionAliases, RemoveExtraAlias, RemoveUnusedSelects, ValueizeSingleLeafSelects}
2121
import io.getquill.util.{Interleave, Interpolator, Messages, TraceConfig}
2222
import io.getquill.util.Messages.{TraceType, fail, trace}
2323

@@ -82,7 +82,9 @@ trait SqlIdiom extends Idiom {
8282
val sql = querifyAst(q, idiomContext.traceConfig)
8383
trace"SQL: ${sql}".andLog()
8484
VerifySqlQuery(sql).map(fail)
85-
val expanded = ExpandNestedQueries(sql, topLevelQuat)
85+
val valueized = ValueizeSingleLeafSelects(naming)(sql, topLevelQuat)
86+
trace"Valueized SQL: ${valueized}".andLog()
87+
val expanded = ExpandNestedQueries(valueized, topLevelQuat)
8688
trace"Expanded SQL: ${expanded}".andLog()
8789
val refined = if (Messages.pruneColumns) RemoveUnusedSelects(expanded) else expanded
8890
trace"Filtered SQL (only used selects): ${refined}".andLog()

quill-engine/src/main/scala/io/getquill/sql/norm/ExpandDistinct.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,15 @@ class ExpandDistinct(traceConfig: TraceConfig) {
7777
// Problems with distinct were first discovered in #1032. Basically, unless
7878
// the distinct is "expanded" adding an outer map, Ident's representing a Table will end up in invalid places
7979
// such as "ORDER BY tableIdent" etc...
80-
case Distinct(Map(q, x, p)) =>
81-
val newMap = Map(q, x, Tuple(List(p)))
82-
val newQuat = Quat.Tuple(valueQuat(p.quat)) // force quat recomputation for perf purposes
83-
val newIdent = Ident(x.name, newQuat)
84-
trace"ExpandDistinct Distinct(Map(other))" andReturn
85-
Map(Distinct(newMap), newIdent, Property(newIdent, "_1"))
80+
81+
// TODO EXPERIMENTING WITH THIS CLAUSE, TRY TO DISABLE``
82+
83+
// case Distinct(Map(q, x, p)) =>
84+
// val newMap = Map(q, x, Tuple(List(p)))
85+
// val newQuat = Quat.Tuple(valueQuat(p.quat)) // force quat recomputation for perf purposes
86+
// val newIdent = Ident(x.name, newQuat)
87+
// trace"ExpandDistinct Distinct(Map(other))" andReturn
88+
// Map(Distinct(newMap), newIdent, Property(newIdent, "_1"))
8689
}
8790
}
8891
}

quill-engine/src/main/scala/io/getquill/sql/norm/RemoveExtraAlias.scala

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,84 @@
11
package io.getquill.sql.norm
22

33
import io.getquill.NamingStrategy
4-
import io.getquill.ast.{Property, Renameable}
5-
import io.getquill.context.sql.{FlattenSqlQuery, SelectValue}
4+
import io.getquill.ast.Ast.LeafQuat
5+
import io.getquill.ast.{Ast, CollectAst, Ident, Property, Renameable}
6+
import io.getquill.context.sql.{FlatJoinContext, FlattenSqlQuery, FromContext, InfixContext, JoinContext, QueryContext, SelectValue, TableContext}
7+
import io.getquill.norm.{BetaReduction, TypeBehavior}
8+
import io.getquill.quat.Quat
9+
10+
// If we run this right after SqlQuery we know that in every place with a single select-value it is a leaf clause e.g. `SELECT x FROM (SELECT p.name from Person p)) AS x`
11+
// in that case we know that SelectValue(x) is a leaf clause that we should expand into a `x.value`.
12+
// MAKE SURE THIS RUNS BEFORE ExpandNestedQueries otherwise it will be incorrect, it should only run for single-selects from atomic values,
13+
// if the ExpandNestedQueries ran it could be a single field that is coming from a case class e.g. case class MySingleValue(stuff: Int) that is being selected from
14+
case class ValueizeSingleLeafSelects(strategy: NamingStrategy) extends StatelessQueryTransformer {
15+
protected def productize(ast: Ident) =
16+
Ident(ast.name, Quat.Product("<Value>", "value" -> Quat.Value))
17+
18+
protected def valueize(ast: Ident) =
19+
Property(productize(ast), "value")
20+
21+
// Turn every `SELECT primitive-x` into a `SELECT case-class-x.primitive-value`
22+
override protected def expandNested(q: FlattenSqlQuery, level: QueryLevel): FlattenSqlQuery = {
23+
// get the alises before we transform (i.e. Valueize) the contexts inside turning the leaf-quat alises into product-quat alises
24+
val fromContextAliases = collectAliases(q.from).filter(!_.quat.isProduct)
25+
// now transform the inner clauses
26+
val from = q.from.map(expandContext(_))
27+
28+
def containsAlias(ast: Ast): Boolean =
29+
CollectAst.byType[Ident](ast).exists(id => fromContextAliases.contains(id))
30+
31+
// If there is one single select clause that has a primitive (i.e. Leaf) quat then we can alias it to "value"
32+
// This is the case of `SELECT primitive FROM (SELECT p.age from Person p) AS primitive`
33+
// where we turn it into `SELECT p.name AS value FROM Person p`
34+
def aliasSelects(selectValues: List[SelectValue]) =
35+
selectValues match {
36+
case List(sv @ SelectValue(LeafQuat(ast), _, _)) => List(sv.copy(alias = Some("value")))
37+
case other => other
38+
}
39+
40+
val valuizedQuery =
41+
q.copy(from = from)(q.quat).mapAsts { ast =>
42+
if (containsAlias(ast)) {
43+
val reductions = CollectAst.byType[Ident](ast).filter(id => fromContextAliases.contains(id)).map(id => id -> valueize(id))
44+
BetaReduction(ast, TypeBehavior.ReplaceWithReduction, reductions: _*)
45+
} else {
46+
ast
47+
}
48+
}
49+
50+
valuizedQuery.copy(select = aliasSelects(valuizedQuery.select))(q.quat)
51+
}
52+
53+
// Turn every `FROM primitive-x` into a `FROM case-class(x.primitive)`
54+
override protected def expandContext(s: FromContext): FromContext =
55+
super.expandContext(s) match {
56+
case QueryContext(query, LeafQuat(id: Ident)) =>
57+
QueryContext(query, productize(id))
58+
case other =>
59+
other
60+
}
61+
62+
// protected def expandContext(s: FromContext): FromContext =
63+
// s match {
64+
// case QueryContext(q, alias) =>
65+
// QueryContext(apply(q, QueryLevel.Inner), alias)
66+
// case JoinContext(t, a, b, on) =>
67+
// JoinContext(t, expandContext(a), expandContext(b), on)
68+
// case FlatJoinContext(t, a, on) =>
69+
// FlatJoinContext(t, expandContext(a), on)
70+
// case _: TableContext | _: InfixContext => s
71+
// }
72+
73+
private def collectAliases(contexts: List[FromContext]): List[Ident] =
74+
contexts.flatMap {
75+
case c: TableContext => List(c.alias)
76+
case c: QueryContext => List(c.alias)
77+
case c: InfixContext => List(c.alias)
78+
case JoinContext(_, a, b, _) => collectAliases(List(a)) ++ collectAliases(List(b))
79+
case FlatJoinContext(_, from, _) => collectAliases(List(from))
80+
}
81+
}
682

783
/**
884
* Remove aliases at the top level of the AST since they are not needed (quill

quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class SqlNormalize(
6363
.andThen(demarcate("ExpandJoin"))
6464
.andThen(ExpandMappedInfix.apply _)
6565
.andThen(demarcate("ExpandMappedInfix"))
66-
.andThen(SheathLeafClausesPhase.apply _)
67-
.andThen(demarcate("SheathLeaves"))
66+
// .andThen(SheathLeafClausesPhase.apply _)
67+
// .andThen(demarcate("SheathLeaves"))
6868
.andThen { ast =>
6969
// In the final stage of normalization, change all temporary aliases into
7070
// shorter ones of the form x[0-9]+.

0 commit comments

Comments
 (0)