Permalink
Browse files

Use a custom collection implementation in the AST:

`ConstArray` is a collection-like wrapper for an Array, similar to the
standard mutable `ArrayBuffer` but immutable. For performance reasons it
does not inherit any collection traits but it offers a similar API.

The changes in this commit reduce the measured query compilation times
in `CompilerBenchmark`by about 25%.

It would be possible to take this one step further by making ConstArray
a value class, which provides a further measurable performance
improvement. However, a value class cannot currently implement proper
equality semantics, thus making such a collection-like class much harder
to use.
  • Loading branch information...
szeiger committed Aug 24, 2015
1 parent b48115a commit 2adb7c36874c41f068176570d3812b674463660e
Showing with 838 additions and 307 deletions.
  1. +1 −1 slick-testkit/src/main/scala/com/typesafe/slick/testkit/tests/NewQuerySemanticsTest.scala
  2. +12 −10 slick/src/main/scala/slick/ast/ClientSideOp.scala
  3. +14 −13 slick/src/main/scala/slick/ast/Comprehension.scala
  4. +7 −5 slick/src/main/scala/slick/ast/Insert.scala
  5. +5 −3 slick/src/main/scala/slick/ast/Library.scala
  6. +71 −64 slick/src/main/scala/slick/ast/Node.scala
  7. +8 −6 slick/src/main/scala/slick/ast/Symbol.scala
  8. +20 −19 slick/src/main/scala/slick/ast/Type.scala
  9. +5 −19 slick/src/main/scala/slick/ast/Util.scala
  10. +9 −9 slick/src/main/scala/slick/compiler/CreateAggregates.scala
  11. +2 −1 slick/src/main/scala/slick/compiler/CreateResultSetMapping.scala
  12. +4 −3 slick/src/main/scala/slick/compiler/EmulateOuterJoins.scala
  13. +3 −2 slick/src/main/scala/slick/compiler/ExpandConditionals.scala
  14. +2 −2 slick/src/main/scala/slick/compiler/ExpandRecords.scala
  15. +14 −13 slick/src/main/scala/slick/compiler/ExpandSums.scala
  16. +3 −2 slick/src/main/scala/slick/compiler/ExpandTables.scala
  17. +3 −1 slick/src/main/scala/slick/compiler/FlattenProjections.scala
  18. +2 −1 slick/src/main/scala/slick/compiler/ForceOuterBinds.scala
  19. +17 −17 slick/src/main/scala/slick/compiler/HoistClientOps.scala
  20. +4 −4 slick/src/main/scala/slick/compiler/InsertCompiler.scala
  21. +21 −21 slick/src/main/scala/slick/compiler/MergeToComprehensions.scala
  22. +4 −3 slick/src/main/scala/slick/compiler/OptimizeScalar.scala
  23. +1 −1 slick/src/main/scala/slick/compiler/QueryCompiler.scala
  24. +1 −1 slick/src/main/scala/slick/compiler/RelabelUnions.scala
  25. +1 −1 slick/src/main/scala/slick/compiler/RemoveFieldNames.scala
  26. +3 −3 slick/src/main/scala/slick/compiler/ReorderOperations.scala
  27. +6 −5 slick/src/main/scala/slick/compiler/ResolveZipJoins.scala
  28. +10 −10 slick/src/main/scala/slick/compiler/RewriteJoins.scala
  29. +2 −1 slick/src/main/scala/slick/compiler/SpecializeParameters.scala
  30. +1 −1 slick/src/main/scala/slick/compiler/VerifyTypes.scala
  31. +3 −1 slick/src/main/scala/slick/driver/HsqldbDriver.scala
  32. +23 −23 slick/src/main/scala/slick/driver/JdbcStatementBuilderComponent.scala
  33. +5 −3 slick/src/main/scala/slick/driver/MySQLDriver.scala
  34. +4 −2 slick/src/main/scala/slick/driver/PostgresDriver.scala
  35. +5 −4 slick/src/main/scala/slick/lifted/Case.scala
  36. +5 −3 slick/src/main/scala/slick/lifted/ExtensionMethods.scala
  37. +3 −1 slick/src/main/scala/slick/lifted/Query.scala
  38. +4 −4 slick/src/main/scala/slick/lifted/Shape.scala
  39. +5 −5 slick/src/main/scala/slick/lifted/SimpleFunction.scala
  40. +3 −3 slick/src/main/scala/slick/memory/MemoryQueryingProfile.scala
  41. +13 −13 slick/src/main/scala/slick/memory/QueryInterpreter.scala
  42. +3 −3 slick/src/main/scala/slick/relational/ResultConverterCompiler.scala
  43. +498 −0 slick/src/main/scala/slick/util/ConstArray.scala
  44. +8 −0 slick/src/main/scala/slick/util/SQLBuilder.scala
@@ -576,7 +576,7 @@ class NewQuerySemanticsTest extends AsyncTest[RelationalTestDB] {
import slick.ast.Util._
val qc = new QueryCompiler(tdb.driver.queryCompiler.phases.takeWhile(_.name != "codeGen"))
val cs = qc.run(q.toNode)
val found = cs.tree.collect { case c: Comprehension => c }.size
val found = cs.tree.collect { case c: Comprehension => c }.length
if(found != exp)
throw cs.symbolNamer.use(new SlickTreeException(s"Found $found Comprehension nodes, should be $exp",
cs.tree, mark = (_.isInstanceOf[Comprehension]), removeUnmarked = false))
@@ -3,6 +3,7 @@ package slick.ast
import slick.ast.Util._
import slick.ast.TypeUtil._
import slick.SlickException
import slick.util.ConstArray
/**
* An operation which is expected to be run on the client side.
@@ -30,7 +31,7 @@ object ClientSideOp {
final case class First(val child: Node) extends UnaryNode with SimplyTypedNode with ClientSideOp {
type Self = First
protected[this] def rebuild(ch: Node) = copy(child = ch)
protected def buildType = children.head.nodeType.asCollectionType.elementType
protected def buildType = child.nodeType.asCollectionType.elementType
def nodeMapServerSide(keepType: Boolean, r: Node => Node) = mapChildren(r, keepType)
}
@@ -46,9 +47,9 @@ final case class ResultSetMapping(generator: TermSymbol, from: Node, map: Node)
def right = map
override def childNames = Seq("from "+generator, "map")
protected[this] def rebuild(left: Node, right: Node) = copy(from = left, map = right)
def generators = Vector((generator, from))
def generators = ConstArray((generator, from))
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = "")
protected[this] def rebuildWithSymbols(gen: IndexedSeq[TermSymbol]) = copy(generator = gen(0))
protected[this] def rebuildWithSymbols(gen: ConstArray[TermSymbol]) = copy(generator = gen(0))
def withInferredType(scope: Type.Scope, typeChildren: Boolean): Self = {
val from2 = from.infer(scope, typeChildren)
val (map2, newType) = from2.nodeType match {
@@ -59,7 +60,7 @@ final case class ResultSetMapping(generator: TermSymbol, from: Node, map: Node)
val map2 = map.infer(scope + (generator -> t), typeChildren)
(map2, map2.nodeType)
}
withChildren(Vector(from2, map2)) :@ (if(!hasType) newType else nodeType)
withChildren(ConstArray[Node](from2, map2)) :@ (if(!hasType) newType else nodeType)
}
def nodeMapServerSide(keepType: Boolean, r: Node => Node) = {
val this2 = mapScopedChildren {
@@ -73,16 +74,17 @@ final case class ResultSetMapping(generator: TermSymbol, from: Node, map: Node)
/** A switch for special-cased parameters that needs to be interpreted in order
* to find the correct query string for the query arguments. */
final case class ParameterSwitch(cases: IndexedSeq[((Any => Boolean), Node)], default: Node) extends SimplyTypedNode with ClientSideOp {
final case class ParameterSwitch(cases: ConstArray[((Any => Boolean), Node)], default: Node) extends SimplyTypedNode with ClientSideOp {
type Self = ParameterSwitch
def children = cases.map(_._2) :+ default
override def childNames = cases.map("[" + _._1 + "]") :+ "default"
protected[this] def rebuild(ch: IndexedSeq[Node]): Self =
copy(cases = (cases, ch).zipped.map { (c, n) => (c._1, n) }, default = ch.last)
override def childNames = cases.map("[" + _._1 + "]").toSeq :+ "default"
protected[this] def rebuild(ch: ConstArray[Node]): Self =
copy(cases = cases.zip(ch).map { case (c, n) => (c._1, n) }, default = ch.last)
protected def buildType = default.nodeType
def nodeMapServerSide(keepType: Boolean, r: Node => Node): Self = {
val ch2 = mapOrNull(children)(r)
val this2 = if(ch2 eq null) this else rebuild(ch2)
val ch = children
val ch2 = ch.endoMap(r)
val this2 = if(ch2 eq ch) this else rebuild(ch2)
if(keepType && hasType) this2 :@ nodeType
else this2
}
@@ -2,23 +2,24 @@ package slick.ast
import TypeUtil.typeToTypeUtil
import Util._
import slick.util.ConstArray
/** A SQL comprehension */
final case class Comprehension(sym: TermSymbol, from: Node, select: Node, where: Option[Node] = None,
groupBy: Option[Node] = None, orderBy: IndexedSeq[(Node, Ordering)] = Vector.empty,
groupBy: Option[Node] = None, orderBy: ConstArray[(Node, Ordering)] = ConstArray.empty,
having: Option[Node] = None,
fetch: Option[Node] = None, offset: Option[Node] = None) extends DefNode {
type Self = Comprehension
val children = Vector(from, select) ++ where ++ groupBy ++ orderBy.map(_._1) ++ having ++ fetch ++ offset
lazy val children = (ConstArray.newBuilder() + from + select ++ where ++ groupBy ++ orderBy.map(_._1) ++ having ++ fetch ++ offset).result
override def childNames =
Seq("from "+sym, "select") ++
where.map(_ => "where") ++
groupBy.map(_ => "groupBy") ++
orderBy.map("orderBy " + _._2) ++
orderBy.map("orderBy " + _._2).toSeq ++
having.map(_ => "having") ++
fetch.map(_ => "fetch") ++
offset.map(_ => "offset")
protected[this] def rebuild(ch: IndexedSeq[Node]) = {
protected[this] def rebuild(ch: ConstArray[Node]) = {
val newFrom = ch(0)
val newSelect = ch(1)
val whereOffset = 2
@@ -38,15 +39,15 @@ final case class Comprehension(sym: TermSymbol, from: Node, select: Node, where:
select = newSelect,
where = newWhere.headOption,
groupBy = newGroupBy.headOption,
orderBy = (orderBy, newOrderBy).zipped.map { case ((_, o), n) => (n, o) },
orderBy = orderBy.zip(newOrderBy).map { case ((_, o), n) => (n, o) },
having = newHaving.headOption,
fetch = newFetch.headOption,
offset = newOffset.headOption
)
}
def generators = Vector((sym, from))
def generators = ConstArray((sym, from))
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = "")
protected[this] def rebuildWithSymbols(gen: IndexedSeq[TermSymbol]) = copy(sym = gen.head)
protected[this] def rebuildWithSymbols(gen: ConstArray[TermSymbol]) = copy(sym = gen.head)
def withInferredType(scope: Type.Scope, typeChildren: Boolean): Self = {
// Assign type to "from" Node and compute the resulting scope
val f2 = from.infer(scope, typeChildren)
@@ -56,12 +57,12 @@ final case class Comprehension(sym: TermSymbol, from: Node, select: Node, where:
val w2 = mapOrNone(where)(_.infer(genScope, typeChildren))
val g2 = mapOrNone(groupBy)(_.infer(genScope, typeChildren))
val o = orderBy.map(_._1)
val o2 = mapOrNull(o)(_.infer(genScope, typeChildren))
val o2 = o.endoMap(_.infer(genScope, typeChildren))
val h2 = mapOrNone(having)(_.infer(genScope, typeChildren))
val fetch2 = mapOrNone(fetch)(_.infer(genScope, typeChildren))
val offset2 = mapOrNone(offset)(_.infer(genScope, typeChildren))
// Check if the nodes changed
val same = (f2 eq from) && (s2 eq select) && w2.isEmpty && g2.isEmpty && (o2 eq null) && h2.isEmpty && fetch2.isEmpty && offset2.isEmpty
val same = (f2 eq from) && (s2 eq select) && w2.isEmpty && g2.isEmpty && (o2 eq o) && h2.isEmpty && fetch2.isEmpty && offset2.isEmpty
val newType =
if(!hasType) CollectionType(f2.nodeType.asCollectionType.cons, s2.nodeType.asCollectionType.elementType)
else nodeType
@@ -71,7 +72,7 @@ final case class Comprehension(sym: TermSymbol, from: Node, select: Node, where:
select = s2,
where = w2.orElse(where),
groupBy = g2.orElse(groupBy),
orderBy = if(o2 eq null) orderBy else (orderBy, o2).zipped.map { case ((_, o), n) => (n, o) },
orderBy = if(o2 eq o) orderBy else orderBy.zip(o2).map { case ((_, o), n) => (n, o) },
having = h2.orElse(having),
fetch = fetch2.orElse(fetch),
offset = offset2.orElse(offset)
@@ -81,12 +82,12 @@ final case class Comprehension(sym: TermSymbol, from: Node, select: Node, where:
}
/** The row_number window function */
final case class RowNumber(by: IndexedSeq[(Node, Ordering)] = Vector.empty) extends SimplyTypedNode {
final case class RowNumber(by: ConstArray[(Node, Ordering)] = ConstArray.empty) extends SimplyTypedNode {
type Self = RowNumber
def buildType = ScalaBaseType.longType
lazy val children = by.map(_._1)
protected[this] def rebuild(ch: IndexedSeq[Node]) =
protected[this] def rebuild(ch: ConstArray[Node]) =
copy(by = by.zip(ch).map{ case ((_, o), n) => (n, o) })
override def childNames = by.zipWithIndex.map("by" + _._2)
override def childNames = by.zipWithIndex.map("by" + _._2).toSeq
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = "")
}
@@ -1,25 +1,27 @@
package slick.ast
import slick.util.ConstArray
/** Represents an Insert operation. */
final case class Insert(tableSym: TermSymbol, table: Node, linear: Node) extends BinaryNode with DefNode {
type Self = Insert
def left = table
def right = linear
override def childNames = Vector("table "+tableSym, "linear")
def generators = Vector((tableSym, table))
def generators = ConstArray((tableSym, table))
def rebuild(l: Node, r: Node) = copy(table = l, linear = r)
def rebuildWithSymbols(gen: IndexedSeq[TermSymbol]) = copy(tableSym = gen(0))
def rebuildWithSymbols(gen: ConstArray[TermSymbol]) = copy(tableSym = gen(0))
def withInferredType(scope: Type.Scope, typeChildren: Boolean): Self = {
val table2 = table.infer(scope, typeChildren)
val lin2 = linear.infer(scope + (tableSym -> table2.nodeType), typeChildren)
withChildren(Vector(table2, lin2)) :@ (if(!hasType) lin2.nodeType else nodeType)
withChildren(ConstArray[Node](table2, lin2)) :@ (if(!hasType) lin2.nodeType else nodeType)
}
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = "")
}
/** A column in an Insert operation. */
final case class InsertColumn(children: IndexedSeq[Node], fs: FieldSymbol, buildType: Type) extends Node with SimplyTypedNode {
final case class InsertColumn(children: ConstArray[Node], fs: FieldSymbol, buildType: Type) extends Node with SimplyTypedNode {
type Self = InsertColumn
protected[this] def rebuild(ch: IndexedSeq[Node]) = copy(children = ch)
protected[this] def rebuild(ch: ConstArray[Node]) = copy(children = ch)
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = fs.toString)
}
@@ -1,5 +1,7 @@
package slick.ast
import slick.util.ConstArray
/**
* The standard library for query operators.
*/
@@ -104,15 +106,15 @@ class FunctionSymbol(val name: String) extends TermSymbol {
/** Match an Apply of this Symbol */
def unapplySeq(n: Node) = n match {
case Apply(sym, ch) if sym eq this => Some(ch)
case Apply(sym, ch) if sym eq this => Some(ch.toSeq)
case _ => None
}
/** Create a typed Apply of this Symbol */
def typed(tpe: Type, ch: Node*): Apply = Apply(this, ch.toIndexedSeq)(tpe)
def typed(tpe: Type, ch: Node*): Apply = Apply(this, ConstArray.from(ch))(tpe)
/** Create a typed Apply of this Symbol */
def typed[T : ScalaBaseType](ch: Node*): Apply = Apply(this, ch.toIndexedSeq)(implicitly[ScalaBaseType[T]])
def typed[T : ScalaBaseType](ch: Node*): Apply = Apply(this, ConstArray.from(ch))(implicitly[ScalaBaseType[T]])
override def toString = "Function "+name
}
Oops, something went wrong.

0 comments on commit 2adb7c3

Please sign in to comment.