-
-
Notifications
You must be signed in to change notification settings - Fork 609
/
MemoryQueryingProfile.scala
136 lines (115 loc) · 6.12 KB
/
MemoryQueryingProfile.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package slick.memory
import scala.collection.mutable.ArrayBuffer
import slick.SlickException
import slick.ast.*
import slick.ast.TypeUtil.*
import slick.basic.BasicProfile
import slick.compiler.*
import slick.relational.*
import slick.util.{??, ConstArray}
/** The querying (read-only) part that can be shared between MemoryProfile and DistributedProfile. */
trait MemoryQueryingProfile extends BasicProfile { self: MemoryQueryingProfile =>
type ColumnType[T] = ScalaType[T]
type BaseColumnType[T] = ScalaType[T] & BaseTypedType[T]
def compileInsert(tree: Node) = insertCompiler.run(tree).tree
type CompiledInsert = Node
trait MemoryQueryingAPI extends BasicAPI with ImplicitColumnTypes
val api: MemoryQueryingAPI
trait ImplicitColumnTypes {
implicit def booleanColumnType: ScalaBaseType[Boolean] = ScalaBaseType.booleanType
implicit def bigDecimalColumnType: ScalaNumericType[BigDecimal] = ScalaBaseType.bigDecimalType
implicit def byteColumnType: ScalaNumericType[Byte] = ScalaBaseType.byteType
implicit def charColumnType: ScalaBaseType[Char] = ScalaBaseType.charType
implicit def doubleColumnType: ScalaNumericType[Double] = ScalaBaseType.doubleType
implicit def floatColumnType: ScalaNumericType[Float] = ScalaBaseType.floatType
implicit def intColumnType: ScalaNumericType[Int] = ScalaBaseType.intType
implicit def longColumnType: ScalaNumericType[Long] = ScalaBaseType.longType
implicit def shortColumnType: ScalaNumericType[Short] = ScalaBaseType.shortType
implicit def stringColumnType: ScalaBaseType[String] = ScalaBaseType.stringType
}
/* internal: */
/** The profile-specific representation of types */
def typeInfoFor(t: Type): ScalaType[Any] = ((t.structural match {
case t: ScalaType[?] => t
case t: TypedType[?] => t.scalaType
case o: OptionType => typeInfoFor(o.elementType).asInstanceOf[ScalaBaseType[?]].optionType
case t => throw new SlickException("No ScalaType found for type "+t)
}): ScalaType[?]).asInstanceOf[ScalaType[Any]]
class MemoryCodeGen extends CodeGen with ResultConverterCompiler[MemoryResultConverterDomain] {
override def apply(state: CompilerState): CompilerState =
state.map(n => retype(apply(n, state))).withWellTyped(false)
override def compileServerSideAndMapping(serverSide: Node,
mapping: Option[Node],
state: CompilerState): (Node, Option[CompiledMapping]) =
(serverSide, mapping.map(compileMapping))
def retype(n: Node): Node = {
val n2 = transformSimpleGrouping(n)
val n3 = n2.mapChildren(retype, keepType = true)
n3 :@ trType(n3.nodeType)
}
def transformSimpleGrouping(n: Node) = n match {
case Bind(gen, g: GroupBy, p @ Pure(_: ProductNode | _: StructNode, _)) =>
val p2 = transformCountAll(gen, p)
if(p2 eq p) n else Bind(gen, g, p2).infer(typeChildren = true)
case Library.SilentCast(n :@ tpe1) :@ tpe2 if tpe1 == tpe2 => n
case n => n
}
def transformCountAll(gen: TermSymbol, n: Node): Node = n match {
case Apply(Library.CountAll, ch @ ConstArray(Bind(gen2, FwdPath(s :: _), Pure(ProductOfCommonPaths(s2, _), _))))
if s == gen && s2 == gen2 =>
Apply(Library.Count, ch)(n.nodeType)
case n =>
n.mapChildren(ch => transformCountAll(gen, ch), keepType = true)
}
def trType(t: Type): Type = t.structural match {
case t @ (_: StructType | _: ProductType | _: CollectionType | _: MappedScalaType | OptionType.NonPrimitive(_)) =>
t.mapChildren(trType)
case t =>
typeInfoFor(t)
}
override def compile(n: Node): ResultConverter[MemoryResultConverterDomain, ?] = n match {
// We actually get a Scala Option value from the interpreter, so the SilentCast is not silent after all
case Library.SilentCast(sel @ Select(_, ElementSymbol(idx)) :@ OptionType(_)) :@ tpe
if !tpe.isInstanceOf[OptionType] =>
val base =
createColumnConverter(sel, idx, None).asInstanceOf[ResultConverter[MemoryResultConverterDomain, Option[Any]]]
createGetOrElseResultConverter(
base,
() => throw new SlickException("Read null value for non-nullable column in Option")
)
case n =>
super.compile(n)
}
def createColumnConverter(n: Node,
idx: Int,
column: Option[FieldSymbol]): ResultConverter[MemoryResultConverterDomain, ?] =
new QueryResultConverter(idx, typeInfoFor(n.nodeType.structural).nullable)
class QueryResultConverter(ridx: Int, nullable: Boolean) extends ResultConverter[MemoryResultConverterDomain, Any] {
def read(pr: MemoryResultConverterDomain#Reader) = {
val v = pr(ridx-1)
if(!nullable && (v.asInstanceOf[AnyRef] eq null))
throw new SlickException("Read null value for non-nullable column")
// TODO: Remove this hack; see comment in ternary logic section of QueryInterpreter
if(!nullable && v.isInstanceOf[Option[?]]) v.asInstanceOf[Option[?]].get
else v
}
override def update(value: Any, pr: MemoryResultConverterDomain#Updater): Nothing = ??
override def set(value: Any, pp: MemoryResultConverterDomain#Writer, offset: Int): Nothing = ??
override def getDumpInfo = super.getDumpInfo.copy(mainInfo = s"ridx=$ridx, nullable=$nullable")
def width = 1
}
}
object ProductOfCommonPaths {
def unapply(n: ProductNode): Option[(TermSymbol, Vector[List[TermSymbol]])] = if(n.children.isEmpty) None else
n.children.iterator.foldLeft(null: Option[(TermSymbol, Vector[List[TermSymbol]])]) {
case (None, _) => None
case (null, FwdPath(sym :: rest)) => Some((sym, Vector(rest)))
case (Some((sym0, v)), FwdPath(sym :: rest)) if sym == sym0 => Some((sym, v :+ rest))
case _ => None
}
}
}
trait MemoryResultConverterDomain extends ResultConverterDomain {
type Reader = QueryInterpreter.ProductValue
type Writer = ArrayBuffer[Any]
}