+ INNER JOIN ... ON goes into WHERE expressions
authorEric Prud'hommeaux <bertails@w3.org>
Mon, 04 Jan 2010 17:03:53 -0500
changeset 110 5a81d4582f14
parent 109 f621ebbb6169
child 111 c8f85edeec6b
+ INNER JOIN ... ON goes into WHERE expressions
src/main/scala/SQL.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/SQL.scala	Mon Jan 04 15:48:21 2010 -0500
+++ b/src/main/scala/SQL.scala	Mon Jan 04 17:03:53 2010 -0500
@@ -89,6 +89,9 @@
 case class RelationalExpressionLt(l:Expression, r:Expression) extends RelationalExpression {
   override def toString = l + "<" + r
 }
+case class RelationalExpressionNull(l:Expression) extends RelationalExpression { // Expression?
+  override def toString = l + " IS NULL"
+}
 case class RelationalExpressionNotNull(l:Expression) extends RelationalExpression { // Expression?
   override def toString = l + " IS NOT NULL"
 }
@@ -135,7 +138,27 @@
 
   def select:Parser[Select] =
     "SELECT" ~ attributelist ~ "FROM" ~ tablelist ~ opt(where) ^^
-    { case "SELECT" ~ attributes ~ "FROM" ~ tables ~ whereexpr => Select(attributes, tables, whereexpr) }
+    {
+      case "SELECT" ~ attributes ~ "FROM" ~ tablesANDons ~ whereexpr => {
+	val t:Set[Expression] = tablesANDons._2
+	val onConjoints = tablesANDons._2.foldLeft(Set[Expression]())((set, ent) =>
+	  ent match {
+	    case ExprConjunction(l) => l
+	    case _ => Set(ent)
+	  })
+	val conjoints = whereexpr match {
+	  case Some(ExprConjunction(l)) => onConjoints ++ l
+	  case Some(x) => onConjoints + x
+	  case _ => onConjoints
+	}
+	val expr:Option[Expression] = conjoints.size match {
+	  case 0 => None
+	  case 1 => Some(conjoints.toList(0))
+	  case _ => Some(ExprConjunction(conjoints))
+	}
+	Select(attributes, tablesANDons._1, expr)
+      }
+    }
 
   def where:Parser[Expression] =
     "WHERE" ~ expression ^^ { case "WHERE" ~ expression => expression }
@@ -171,12 +194,15 @@
   def relalias:Parser[RelAlias] =
     """[a-zA-Z_]\w*""".r ^^ { x => RelAlias(Name(x)) }
 
-  def tablelist:Parser[TableList] =
-    aliasedjoin ~ rep(innerORouter) ^^ { case aj~l => TableList(AddOrderedSet(InnerJoin(aj) :: l)) }
+  def tablelist:Parser[(TableList, Set[Expression])] =
+    aliasedjoin ~ rep(innerORouter) ^^ { case aj~l => (TableList(AddOrderedSet(InnerJoin(aj) :: l.map((one) => one._1))), 
+						       l.foldLeft(Set[Expression]())((all, one) => all ++ one._2)) }
 
-  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 innerORouter:Parser[(Join, Set[Expression])] = (
+      "INNER" ~ "JOIN" ~ aliasedjoin ~ opt("ON" ~ expression) ^^
+      { case "INNER"~"JOIN"~a~o => (InnerJoin(a), { if (o.isDefined) Set(o.get._2) else Set[Expression]() } ) }
+    | "LEFT" ~ "OUTER" ~ "JOIN" ~ aliasedjoin ~ "ON" ~ expression ^^
+      { case l~o~j~alijoin~on~expr => (LeftOuterJoin(alijoin, expr), Set[Expression]()) }
   )
 
   def aliasedjoin:Parser[AliasedResource] =
@@ -201,6 +227,8 @@
       { case primaryexpression ~ "!=" ~ rvalue => RelationalExpressionNe(primaryexpression, rvalue) }
     | primaryexpression ~ "<" ~ primaryexpression ^^
       { case primaryexpression ~ "<" ~ rvalue => RelationalExpressionLt(primaryexpression, rvalue) }
+    | primaryexpression ~ "IS" ~ "NULL" ^^
+      { case primaryexpression ~ "IS" ~ "NULL" => RelationalExpressionNull(primaryexpression) }
     | primaryexpression ~ "IS" ~ "NOT" ~ "NULL" ^^
       { case primaryexpression ~ "IS" ~ "NOT" ~ "NULL" => RelationalExpressionNotNull(primaryexpression) }
     | primaryexpression ^^
--- a/src/test/scala/SQLTest.scala	Mon Jan 04 15:48:21 2010 -0500
+++ b/src/test/scala/SQLTest.scala	Mon Jan 04 17:03:53 2010 -0500
@@ -48,6 +48,45 @@
     assert(expected === (a.parseAll(a.expression, e).get))
   }
 
+  test("parse WHERE") {
+    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
+    val a = Sql()
+    val e = """
+SELECT R_emp.lastName AS A_empName
+       FROM Employee AS R_emp
+            INNER JOIN Employee AS R_manager
+ WHERE R_manager.id=R_emp.manager
+"""
+    val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_emp")),
+									     Attribute(Name("lastName"))),
+							   AttrAlias(Name("A_empName"))))),
+			  TableList(AddOrderedSet(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+						  InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
+			  Some(
+			    RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+						   PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager")))))))
+    assert(expected === (a.parseAll(a.select, e).get))
+  }
+
+  test("parse INNER JOIN ON") {
+    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
+    val a = Sql()
+    val e = """
+SELECT R_emp.lastName AS A_empName
+       FROM Employee AS R_emp
+            INNER JOIN Employee AS R_manager ON R_manager.id=R_emp.manager
+"""
+    val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_emp")),
+									     Attribute(Name("lastName"))),
+							   AttrAlias(Name("A_empName"))))),
+			  TableList(AddOrderedSet(InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))),
+						  InnerJoin(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))))),
+			  Some(
+			    RelationalExpressionEq(PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id")))),
+						   PrimaryExpressionAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager")))))))
+    assert(expected === (a.parseAll(a.select, e).get))
+  }
+
   test("parse SQLbgp") {
     // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
     val a = Sql()