+ TableList.Join
authorEric Prud'hommeaux <bertails@w3.org>
Sat, 02 Jan 2010 16:33:28 -0500
changeset 100 22e67b5fa2f0
parent 99 936f2f4836e2
child 101 fb5ec48c0c74
+ TableList.Join
src/main/scala/RDB2RDFMain.scala
src/main/scala/SQL.scala
src/test/scala/RDB2RDFTest.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Sat Jan 02 10:05:40 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Sat Jan 02 16:33:28 2010 -0500
@@ -13,7 +13,7 @@
 case class Enum(relaliasattr:RelAliasAttribute) extends Binding
 
 object RDB2RDF {
-  case class R2RState(joins:Set[AliasedResource], varmap:Map[Var, SQL2RDFValueMapper], exprs:Set[Expression])
+  case class R2RState(joins:Set[Join], varmap:Map[Var, SQL2RDFValueMapper], exprs:Set[Expression])
 
   sealed abstract class SQL2RDFValueMapper(relaliasattr:RelAliasAttribute, disjoints:Set[RelationalExpressionNe])
   case class IntMapper(relaliasattr:RelAliasAttribute, disjoints:Set[RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
@@ -161,7 +161,7 @@
 	  case SUri(u) => uriConstraint(stateP, subjattr, u, true)
 	  case SVar(v) => varConstraint(stateP, subjattr, v, db, rel)
 	}
-	val state_subjJoin = R2RState(state_postSubj.joins + AliasedResource(rel,relalias), state_postSubj.varmap, state_postSubj.exprs)
+	val state_subjJoin = R2RState(state_postSubj.joins + InnerJoin(AliasedResource(rel,relalias)), state_postSubj.varmap, state_postSubj.exprs)
 
 	val (targetattr, targetrel, dt, state_fkeys) = db.relationdescs(rel).attributes(attr) match {
 	  case ForeignKey(fkrel, fkattr) => {
@@ -174,7 +174,9 @@
 	    if (enforeForeignKeys) {
 	      val oRelAlias = relAliasFromO(o)
 	      val fkaliasattr = RelAliasAttribute(oRelAlias, fkattr)
-	      val state_t = R2RState(state_subjJoin.joins + AliasedResource(fkrel,oRelAlias), state_subjJoin.varmap, state_subjJoin.exprs + RelationalExpressionEq(fkaliasattr,RValueAttr(objattr)))
+	      val state_t = R2RState(state_subjJoin.joins + InnerJoin(AliasedResource(fkrel,oRelAlias)),
+				     state_subjJoin.varmap,
+				     state_subjJoin.exprs + RelationalExpressionEq(fkaliasattr,RValueAttr(objattr)))
 
 	      (fkaliasattr, fkrel, fkdt, state_t)
 	    } else {
@@ -288,7 +290,7 @@
 	val unionAlias = RelAlias(Name("R_union" + state.joins.size))
 	val initDisjoints:Set[Select] = Set()
 	val emptyState = R2RState(
-	  Set[AliasedResource](), 
+	  Set[Join](), 
 	  Map[Var, SQL2RDFValueMapper](), 
 	  Set[Expression]()
 	)
@@ -360,7 +362,10 @@
 	  (outerState2, outerDisjoints ++ Set(subselect), no+1)
 	})
 	val union = Subselect(Union(disjoints))
-	R2RState(state.joins + AliasedResource(union,unionAlias), state2.varmap, state2.exprs)
+	R2RState(state.joins + InnerJoin(AliasedResource(union,unionAlias)), state2.varmap, state2.exprs)
+      }
+      case OptionalGraphPattern(gp) => {
+	state
       }
       case x => error("no code to handle " + x)
     }
@@ -371,7 +376,7 @@
 
     /* Create an object to hold our compilation state. */
     val initState = R2RState(
-      Set[AliasedResource](), 
+      Set[Join](), 
       Map[Var, SQL2RDFValueMapper](), 
       Set[Expression]()
     )
--- a/src/main/scala/SQL.scala	Sat Jan 02 10:05:40 2010 -0500
+++ b/src/main/scala/SQL.scala	Sat Jan 02 16:33:28 2010 -0500
@@ -61,9 +61,14 @@
 case class RelAlias(n:Name) {
   override def toString = n.s /* "'" + n.s + "'" */
 }
-case class TableList(joins:Set[AliasedResource]) {
+case class TableList(joins:Set[Join]) {
   override def toString = "  FROM " + (joins mkString ("\n       INNER JOIN "))
 }
+
+sealed abstract class Join(res:AliasedResource)
+case class InnerJoin(res:AliasedResource) extends Join(res)
+case class LeftOuterJoin(res:AliasedResource, on:Expression) extends Join(res)
+
 case class AliasedResource(rel:RelationORSubselect, as:RelAlias) {
   override def toString = rel + " AS " + as
 }
@@ -166,7 +171,12 @@
     """[a-zA-Z_]\w*""".r ^^ { x => RelAlias(Name(x)) }
 
   def tablelist:Parser[TableList] =
-    repsep(aliasedjoin, "INNER" ~ "JOIN") ^^ { m => TableList(m.toSet) }
+    aliasedjoin ~ rep(innerORouter) ^^ { case aj~l => TableList(Set(InnerJoin(aj)) ++ l.toSet) }
+
+  def innerORouter:Parser[Join] = (
+      "INNER" ~ "JOIN" ~ aliasedjoin ^^ { case "INNER"~"JOIN"~a => InnerJoin(a) }
+    | "LEFT" ~ "OUTER" ~ "JOIN" ~ aliasedjoin ~ "ON" ~ expression ^^ { case l~o~j~alijoin~on~expr => LeftOuterJoin(alijoin, expr) }
+  )
 
   def aliasedjoin:Parser[AliasedResource] =
     relationORsubselect ~ "AS" ~ relalias ^^
--- a/src/test/scala/RDB2RDFTest.scala	Sat Jan 02 10:05:40 2010 -0500
+++ b/src/test/scala/RDB2RDFTest.scala	Sat Jan 02 16:33:28 2010 -0500
@@ -425,7 +425,6 @@
 
 // SELECT ?empName ?managName ?grandManagName
 //  WHERE {          ?emp            emplP:lastName   ?empName
-
 //        OPTIONAL { ?mang           mangP:manages    ?emp .
 //                   ?mang           mangP:manager    ?manager .
 //                   ?manager        emplP:lastName   ?managName
@@ -436,28 +435,20 @@
 // """).get
 //     val sqlParser = Sql()
 //     val sqlSelect = sqlParser.parseAll(sqlParser.select, """
-// SELECT R_union1.A_name AS A_name
-//   FROM Employee AS R_who
-//        INNER JOIN ( SELECT 0 AS _DISJOINT_, R_above.manager AS A_manager, R_manager.lastName AS A_name, R_above.id AS A_above, 
-//                 NULL AS A_below, NULL AS A_bday, R_above.manages AS A_who, NULL AS A_managed
-//                 FROM Manage AS R_above
+// SELECT R_emp.lastName AS A_empName, R_opt1.A_managName AS A_managName, R_opt1.A_grandManagName AS A_grandManagName
+//        FROM Employee AS R_emp
+//             LEFT OUTER JOIN (
+//     SELECT R_opt1.A_grandManagName AS A_grandManagName, R_manager.lastName AS A_managName, R_mang.manages AS R_emp, R_mang.manager AS A_manager
+//            FROM Manage AS R_mang
 //                 INNER JOIN Employee AS R_manager
-//           WHERE R_above.manager IS NOT NULL AND R_above.manager=R_manager.id AND R_above.id IS NOT NULL
-//                 AND R_above.manages IS NOT NULL AND R_manager.lastName IS NOT NULL
-//        UNION
-//          SELECT 1 AS _DISJOINT_, NULL AS A_manager, R_managed.lastName AS A_name, NULL AS A_above, 
-//                 R_below.id AS A_below, R_managed.birthday AS A_bday, R_below.manager AS A_who, R_below.manages AS A_managed
-//                 FROM Manage AS R_below
-//                 INNER JOIN Employee AS R_managed
-//           WHERE R_managed.birthday IS NOT NULL AND R_below.manager IS NOT NULL AND R_below.id IS NOT NULL
-//                 AND R_below.manages=R_managed.id AND R_below.manages IS NOT NULL AND R_managed.lastName IS NOT NULL
-//        ) AS R_union1
-//  WHERE R_who.lastName="Smith" AND
-//        (R_union1._DISJOINT_!=0 OR R_who.id=R_union1.A_who) AND
-//        (R_union1._DISJOINT_!=1 OR R_who.id=R_union1.A_who) AND
-//        (R_union1._DISJOINT_!=1 OR R_union1.A_bday=R_who.birthday) AND
-//        R_who.id IS NOT NULL AND
-//        R_union1.A_bday IS NOT NULL
+//                 LEFT OUTER JOIN (
+//         SELECT R_grandManager.lastName AS A_grandManagName, R_grandMang.manages AS A_manager
+//                FROM Manage AS R_grandMang
+//                     INNER JOIN Employee AS R_grandManager
+//                  ) AS R_opt1 ON R_opt1.manager=R_mang.manager
+//           WHERE R_manager.id=R_mang.manager AND R_grandManager.id=R_grandMang.manager
+//              ) AS R_opt1 ON opt1.emp=R_emp.id
+//  WHERE R_emp.lastName IS NOT NULL
 // """).get
 //     assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), PrimaryKey(Attribute(Name("id"))), false) === sqlSelect)
 //   }
--- a/src/test/scala/SQLTest.scala	Sat Jan 02 10:05:40 2010 -0500
+++ b/src/test/scala/SQLTest.scala	Sat Jan 02 16:33:28 2010 -0500
@@ -62,8 +62,8 @@
 					     NamedAttribute(RelAliasAttribute(RelAlias(Name("R_manager")),
 									      Attribute(Name("lastName"))),
 							    AttrAlias(Name("A_managName"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))),
-					 AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
 			  Some(ExprConjunction(Set(
 			    RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
 						RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
@@ -82,7 +82,7 @@
     val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_emp")),
 									      Attribute(Name("lastName"))),
 							    AttrAlias(Name("A_empName"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))))),
 			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
 							      RValueTyped(SQLDatatype.INTEGER,Name("18"))),
 					  RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))))
@@ -100,8 +100,8 @@
     val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_emp")),
 									      Attribute(Name("lastName"))),
 							    AttrAlias(Name("A_empName"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))),
-					 AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
 			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
 							      RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
 					  RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))),
@@ -129,11 +129,11 @@
 							   AttrAlias(Name("A_empName"))),
 					    NamedAttribute(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("lastName"))),
 							   AttrAlias(Name("A_grandManagName"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))),
-					AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_lower"))),
-					AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))),
-					AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_upper"))),
-					AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_grandManager"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+					InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_lower")))),
+					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))),
+					InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_upper")))),
+					InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_grandManager")))))),
 			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))),
 					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
@@ -171,15 +171,15 @@
 """
     val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_union1")), Attribute(Name("name"))),
 							   AttrAlias(Name("A_name"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_who"))),
-					AliasedResource(Subselect(Union(Set(
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_who")))),
+					InnerJoin(AliasedResource(Subselect(Union(Set(
 					  Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_manager")), Attribute(Name("lastName"))),
 										  AttrAlias(Name("A_name"))), 
 								   NamedAttribute(RelAliasAttribute(RelAlias(Name("R_above")), Attribute(Name("manages"))),
 										  AttrAlias(Name("A_who"))))),
 						 TableList(Set(
-						   AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))),
-						   AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))
+						   InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))),
+						   InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))
 						 )), 
 						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("manager"))),
 										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
@@ -189,13 +189,13 @@
 								   NamedAttribute(RelAliasAttribute(RelAlias(Name("R_below")), Attribute(Name("manager"))),
 										  AttrAlias(Name("A_who"))))),
 						 TableList(Set(
-						   AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_below"))),
-						   AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_managed")))
+						   InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_below")))),
+						   InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_managed"))))
 						 )), 
 						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_below")),Attribute(Name("manages"))),
 										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("id"))))),
 								RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("lastName"))))))))))),
-							RelAlias(Name("R_union1"))))), 
+							RelAlias(Name("R_union1")))))), 
 			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_union1")),Attribute(Name("A_who"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("id"))))),
 					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("lastName"))),
@@ -215,7 +215,7 @@
 							    AttrAlias(Name("A_who"))),
 					     NamedAttribute(ConstNULL(),
 							    AttrAlias(Name("A_bday"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))))),
 			  Some(
 			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))))))
     assert(expected === (a.parseAll(a.select, e).get))
@@ -233,7 +233,7 @@
 							    AttrAlias(Name("A_who"))),
 					     NamedAttribute(ConstNULL(),
 							    AttrAlias(Name("A_bday"))))),
-			  TableList(Set(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above")))))),
 			  Some(
 			    ExprDisjunction(Set(
 			      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))),
@@ -246,4 +246,31 @@
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
+  test("parse LEFT OUTER JOIN") {
+    val a = Sql()
+    val e = """
+SELECT R_emp.lastName AS A_empName, R_mang.manageName AS A_manageName
+       FROM Employee AS R_emp
+            LEFT OUTER JOIN Manage AS R_mang ON R_mang.emp=R_emp.id
+ WHERE R_emp.lastName IS NOT NULL
+"""
+    val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_emp")),
+									     Attribute(Name("lastName"))),
+							    AttrAlias(Name("A_empName"))),
+					     NamedAttribute(RelAliasAttribute(RelAlias(Name("R_mang")),
+									      Attribute(Name("manageName"))),
+							    AttrAlias(Name("A_manageName"))))),
+			  TableList(Set(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+					LeftOuterJoin(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_mang"))),
+						      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_mang")),Attribute(Name("emp"))),
+									     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))
+
+									   )))),
+			  Some(
+			      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))
+			    ))
+    assert(expected === (a.parseAll(a.select, e).get))
+  }
+
+
 }