Permalink
Browse files

Unified expansion of table types with same baseIdentity

In `expandTables` table fields used to be identified based on the
tables’s identity. This breaks when performing a union operation over
raw table rows where the right-hand side references more columns than
the left-hand side.

This situation can be prevented by taking field references of sibling
tables (with a different identity but the same baseIdentity) into
account when computing a tables’s structural type to ensure that the
structural types on both sides are compatible.

Test in UnionTest.testMappedUnion. Fixes #1571.
  • Loading branch information...
szeiger committed Nov 3, 2016
1 parent b05f97d commit dea877f07af04e30cbed71ffae495f4d076f4abe
@@ -185,4 +185,22 @@ class UnionTest extends AsyncTest[RelationalTestDB] {
)
}
def testMappedUnion = {
class T(tag: Tag) extends Table[(String, Int, String, Int)](tag, u"t") {
def a = column[String]("a")
def b = column[Int]("b")
def c = column[String]("c")
def d = column[Int]("d")
def e = column[String]("e")
def * = (a, b, c, d)
override def create_* = collectFieldSymbols((*, e).shaped.toNode)
}
val ts = TableQuery[T]
val q1 = ts.filter(_.a === "a") ++ ts.filter(_.e === "e")
DBIO.seq(
ts.schema.create,
ts.map(f => (f.a, f.b, f.c, f.d, f.e)) += (("a", 1, "c", 3, "e")),
q1.result.map(_ shouldBe Vector(("a",1,"c",3), ("a",1,"c",3)))
)
}
}
@@ -34,19 +34,20 @@ class ExpandTables extends Phase {
}
val s2 = state.map { n => ClientSideOp.mapServerSide(n) { tree =>
// Find table fields
// Find table fields grouped by baseIdentity, so that all tables with the same baseIdentity end up with the same
// structural type under a different identity.
val baseIdentity = tree.collect[(TypeSymbol, TypeSymbol)] { case TableNode(_, _, i, b) => (i, b) }.toMap
val structs = tree.collect[(TypeSymbol, (FieldSymbol, Type))] {
case s @ Select(_ :@ (n: NominalType), sym: FieldSymbol) => n.sourceNominalType.sym -> (sym -> s.nodeType)
}.toSeq.groupBy(_._1).map { case (ts, v) => (ts, NominalType(ts, StructType(ConstArray.from(v.map(_._2).toMap)))) }
case s @ Select(_ :@ (n: NominalType), sym: FieldSymbol) => baseIdentity(n.sourceNominalType.sym) -> (sym -> s.nodeType)
}.toSeq.groupBy(_._1).mapValues(_.map(_._2).toMap)
logger.debug("Found Selects for NominalTypes: "+structs.keySet.mkString(", "))
val tables = new mutable.HashMap[TableIdentitySymbol, (TermSymbol, Node)]
var expandDistinct = false
def tr(tree: Node): Node = tree.replace {
case t: TableExpansion =>
val ts = t.table.asInstanceOf[TableNode].identity
tables += ((ts, (t.generator, t.columns)))
t.table :@ CollectionType(t.nodeType.asCollectionType.cons, structs(ts))
case TableExpansion(gen, t @ TableNode(_, _, i, b), columns) :@ CollectionType(cons, _) =>
tables += ((i, (gen, columns)))
t :@ CollectionType(cons, NominalType(i, StructType(ConstArray.from(structs(b)))))
case r: Ref => r.untyped
case d: Distinct =>
if(d.nodeType.existsType { case NominalType(_: TableIdentitySymbol, _) => true; case _ => false })

0 comments on commit dea877f

Please sign in to comment.