Slick Query Compiler Query Compiler.pdf · Write database code in Scala •...

31
Slick Query Compiler Stefan Zeiger, Typesafe Talk at LAMP (EPFL) 2014-03-04

Transcript of Slick Query Compiler Query Compiler.pdf · Write database code in Scala •...

Page 1: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Slick Query Compiler Stefan Zeiger, Typesafe

Talk at LAMP (EPFL) 2014-03-04

Page 2: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Overview

Page 3: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Scala Language Integrated Connection Kit

•  Database query and access library for Scala •  Successor of ScalaQuery •  Developed at Typesafe and EPFL •  Open Source

3  

Page 4: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Write database code in Scala

•  Instead  of  SQL,  JPQL,  Criteria  API,  etc.    

select p.NAME from PERSON p

for { p <- persons } yield p.name

4  

Page 5: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Result Slick AST

Slick APIs

Slick AST

Scala Compiler

Slick Macros

Query Compiler

Executor

DB

5  

Lifted Embedding

Scala AST

Direct Embedding

Page 6: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Overview

•  Compiler Architecture •  Types •  Trees •  Symbols •  Scopes •  Compiler Phases

6  

Page 7: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Compiler Architecture

Page 8: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Compiler Architecture

•  Immutable  ASTs  – Types  can  be  mutated  unBl  they  are  observed  

•  Immutable  compiler  state  – containing  AST  +  phase  output  state  

•  Phases  transform  compiler  state  – using  mutable  state  locally  

•  Drivers  provide  their  own  compilers  

8  

Page 9: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Generate Code •  codeGen

(driver-specific)

SQL Shape •  resolveZipJoins •  convertToComprehensions •  fuseComprehensions •  fixRowNumberOrdering •  hoistClientOps

Compiler Phases: SQL

Flatten Columns •  expandTables •  expandRecords •  flattenProjections •  relabelUnions •  pruneFields •  assignTypes

9  

Clean Up •  inline •  assignUniqueSymbols •  inferTypes •  createResultSetMapping •  forceOuterBinds

Page 10: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Prepare for Interpreter •  codeGen

Flatten Columns •  expandTables •  expandRecords •  flattenProjections •  relabelUnions •  pruneFields •  assignTypes

Compiler Phases: MemoryDriver

10  

Clean Up •  inline •  assignUniqueSymbols •  inferTypes •  createResultSetMapping •  forceOuterBinds

Page 11: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Prepare for Interpreter •  codeGen

Flatten Columns •  expandTables •  expandRecords •  flattenProjections •  relabelUnions •  pruneFields •  assignTypes

Clean Up II

•  inferTypes •  createResultSetMapping •  forceOuterBinds

Compiler Phases: Scheduling

11  

Distribute •  distribute (to other drivers' compilers)

e.g. H2

Query Compiler

MySQL Query

Compiler

… Query

Compiler

Clean Up •  inline •  assignUniqueSymbols

Page 12: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Types

Page 13: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Types /** Super-trait for all types */ trait Type { /** All children of this Type. */ def children: Seq[Type] /** Apply a transformation to all type children and reconstruct this type with the new children, or return the original object if no child is changed. */ def mapChildren(f: Type => Type): Type def select(sym: Symbol): Type = throw new SlickException("No type for symbol "+sym+ " found in "+this) /** The structural view of this type */ def structural: Type = this }

13  

Page 14: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Type

TypedType[T]

StructType

ProductType

CollectionType

OptionType

MappedScalaType

NominalType

BaseTypedType[T]

OptionTypedType[T]

ScalaType[T]

Driver Types Scala Types

UnassignedType

14  

Page 15: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Trees

Page 16: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Trees trait Node { type Self >: this.type <: Node def nodeChildren: Seq[Node] protected[this] def nodeRebuild(ch: IndexedSeq[Node]): Self final def nodeMapChildren(f: Node => Node, keepType: Boolean = false): Self = ... def nodeType: Type = ... def nodePeekType: Type = ... def nodeHasType: Boolean = ... final def nodeTypedOrCopy(tpe: Type): Self = ... final def nodeWithComputedType( scope: SymbolScope = SymbolScope.empty, typeChildren: Boolean = false, retype: Boolean = false): Self = ... ... } 16  

Page 17: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Symbols

Page 18: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Symbols /** A symbol which can be used in the AST. */ trait Symbol { def name: String override def toString = SymbolNamer(this) }

18  

Page 19: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Symbol FieldSymbol

ElementSymbol

AnonSymbol

IntrinsicSymbol

FunctionSymbol

TypeSymbol

Standard Library Functions

AnonTypeSymbol

TableIdentitySymbol

AnonTableIdentitySymbol

SimpleTableIdentitySymbol 19  

Page 20: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Scopes

Page 21: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Scopes trait SymbolScope { def + (entry: (Symbol, Type)): SymbolScope def get(sym: Symbol): Option[Type] def withDefault(f: (Symbol => Type)): SymbolScope }

•  Mainly  for  typing  •  Most  transformaBons  don't  need  scopes  

21  

Page 22: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Compiler Phases

Page 23: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Example val qa = for { c <- coffees.take(3) } yield (c.supID, (c.name, 42))

TableQuery

23  

Page 24: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Source AST

24  

Page 25: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

Unique Symbols

25  

Page 26: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

After Type Inference

26  

Page 27: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

inferTypes implementation class InferTypes extends Phase { val name = "inferTypes" def apply(state: CompilerState) = state.map { tree => val tree2 = tree.nodeWithComputedType(new DefaultSymbolScope(Map.empty), true, false) val structs = tree2.collect[(TypeSymbol, (Symbol, Type))] { case s @ Select(_ :@ (n: NominalType), sym) => n.sourceNominalType.sym -> (sym -> s.nodeType) }.groupBy(_._1).mapValues(v => StructType(v.map(_._2).toMap.toIndexedSeq)) logger.debug("Found Selects: "+structs.keySet.mkString(", ")) def tr(n: Node): Node = n.nodeMapChildren(tr, keepType = true).nodeTypedOrCopy(n.nodeType.replace { case UnassignedStructuralType(tsym) if structs.contains(tsym) => structs(tsym) }) tr(tree2) } }

27  

Page 28: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

After Flattening

28  

Page 29: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

SQL Form

29  

Page 30: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

After Code Generator

30  

Page 31: Slick Query Compiler Query Compiler.pdf · Write database code in Scala • Instead"of"SQL,"JPQL,"CriteriaAPI,"etc." " select p.NAME from PERSON p for { p

slick.typesafe.com @StefanZeiger