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...
1 parent b05f97d commit dea877f07af04e30cbed71ffae495f4d076f4abe @szeiger szeiger committed Nov 3, 2016
@@ -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.