+ recursive SQL expressions
authorEric Prud'hommeaux <bertails@w3.org>
Thu, 31 Dec 2009 05:49:51 -0500
changeset 94 641948632f5f
parent 93 605ea99957be
child 95 79703c0f93a2
+ recursive SQL expressions
src/main/scala/RDB2RDFMain.scala
src/main/scala/SQL.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Wed Dec 30 21:16:35 2009 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Thu Dec 31 05:49:51 2009 -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[PrimaryExpression])
+  case class R2RState(joins:Set[AliasedResource], varmap:Map[Var, SQL2RDFValueMapper], exprs:Set[Expression])
 
   sealed abstract class SQL2RDFValueMapper(relaliasattr:RelAliasAttribute)
   case class IntMapper(relaliasattr:RelAliasAttribute) extends SQL2RDFValueMapper(relaliasattr)
@@ -53,15 +53,15 @@
 
   def uriConstraint(state:R2RState, constrainMe:RelAliasAttribute, u:ObjUri, enforeForeignKeys:Boolean):R2RState = {
     // println("equiv+= " + toString(constrainMe) + "=" + value)
-    //R2RState(state.joins, state.varmap, state.exprs + PrimaryExpressionEq(constrainMe,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
+    //R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(constrainMe,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
     val relvar = if (enforeForeignKeys) RelAliasAttribute(constrainMe.relalias, Attribute(Name(u.attr.s))) else { println("constraining to " + constrainMe)
 constrainMe }
-    R2RState(state.joins, state.varmap, state.exprs + PrimaryExpressionEq(relvar,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
+    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(relvar,RValueTyped(SQLDatatype.INTEGER,Name(u.v.s))))
   }
 
   def literalConstraint(state:R2RState, constrainMe:RelAliasAttribute, lit:SparqlLiteral, dt:SQLDatatype):R2RState = {
     // println("equiv+= " + toString(attr) + "=" + lit)
-    R2RState(state.joins, state.varmap, state.exprs + PrimaryExpressionEq(constrainMe,RValueTyped(dt,Name(lit.lit.lexicalForm))))    
+    R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(constrainMe,RValueTyped(dt,Name(lit.lit.lexicalForm))))    
   }
 
   /** varConstraint
@@ -97,7 +97,7 @@
 	state
       else
 	/* Constraint against the initial binding for this variable. */
-	R2RState(state.joins, state.varmap, state.exprs + PrimaryExpressionEq(varToAttribute(state.varmap, v), RValueAttr(constrainMe)))
+	R2RState(state.joins, state.varmap, state.exprs + RelationalExpressionEq(varToAttribute(state.varmap, v), RValueAttr(constrainMe)))
     } else {
       /* This is a new variable. */
       val binding = reldesc.primarykey match {
@@ -168,7 +168,7 @@
 	    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 + PrimaryExpressionEq(fkaliasattr,RValueAttr(objattr)))
+	      val state_t = R2RState(state_subjJoin.joins + AliasedResource(fkrel,oRelAlias), state_subjJoin.varmap, state_subjJoin.exprs + RelationalExpressionEq(fkaliasattr,RValueAttr(objattr)))
 
 	      (fkaliasattr, fkrel, fkdt, state_t)
 	    } else {
@@ -222,12 +222,12 @@
     }
   }
 
-  def filter2expr(varmap:Map[Var, SQL2RDFValueMapper], f:SparqlPrimaryExpression):PrimaryExpression = {
-    val (lTerm:Term, rTerm:Term, sqlexpr) = f match { // sqlexpr::((RelAliasAttribute,RValueAttr)=>PrimaryExpression)
-      case SparqlPrimaryExpressionEq(l, r) => (l.term, r.term, PrimaryExpressionEq(_,_))
-      case SparqlPrimaryExpressionLt(l, r) => (l.term, r.term, PrimaryExpressionLt(_,_))
+  def filter2expr(varmap:Map[Var, SQL2RDFValueMapper], f:SparqlPrimaryExpression):RelationalExpression = {
+    val (lTerm:Term, rTerm:Term, sqlexpr) = f match { // sqlexpr::((RelAliasAttribute,RValueAttr)=>RelationalExpression)
+      case SparqlPrimaryExpressionEq(l, r) => (l.term, r.term, RelationalExpressionEq(_,_))
+      case SparqlPrimaryExpressionLt(l, r) => (l.term, r.term, RelationalExpressionLt(_,_))
     }
-// PrimaryExpressionEq(_,_) === (x,y) => PrymaryExpressionEq(x,y)
+// RelationalExpressionEq(_,_) === (x,y) => PrymaryExpressionEq(x,y)
     lTerm match {
       // does not handle FILTER (<x> = ?v)
       case TermUri(obj) => error("only SPARQL PrimaryExpressions with a variable on the left have been implemented: punting on " + f)
@@ -254,7 +254,7 @@
 	val state2 = mapGraphPattern(db, state, gp2, pk, enforeForeignKeys)
 
 	/* Add constraints for all the FILTERS */
-	val filterExprs:Set[PrimaryExpression] =
+	val filterExprs:Set[RelationalExpression] =
 	  expr.conjuncts.toSet map ((x:SparqlPrimaryExpression) => filter2expr(state2.varmap, x))
 
 	R2RState(state2.joins, state2.varmap, state2.exprs ++ filterExprs)
@@ -262,7 +262,7 @@
       case TriplesBlock(triplepatterns) => {
 	/* Examine each triple, updating the compilation state. */
 	val state2 = triplepatterns.foldLeft(state)((incState,s) => bindOnPredicate(db, incState, s, pk, enforeForeignKeys))
-	val nullExprs = findVars(gp) map (vvar => PrimaryExpressionNotNull(varToAttribute(state2.varmap, vvar)))
+	val nullExprs = findVars(gp) map (vvar => RelationalExpressionNotNull(varToAttribute(state2.varmap, vvar)))
 	R2RState(state2.joins, state2.varmap, state2.exprs ++ nullExprs)
       }
       case TableConjunction(list) => {
@@ -274,7 +274,7 @@
 	val emptyState = R2RState(
 	  Set[AliasedResource](), 
 	  Map[Var, SQL2RDFValueMapper](), 
-	  Set[PrimaryExpression]()
+	  Set[Expression]()
 	)
 	val (state2, disjoints) = list.foldLeft((state, initDisjoints))((incPair,disjoint) => {
 	  val (outerState, outerDisjoints) = incPair
@@ -287,7 +287,11 @@
 	  val sel = Select(
 	    AttributeList(attrlist),
 	    TableList(disjointState.joins),
-	    Expression(disjointState.exprs)
+	    disjointState.exprs.size match {
+	      case 0 => None
+	      case 1 => Some(disjointState.exprs.toList(0))
+	      case _ => Some(ExprConjunction(disjointState.exprs))
+	    }
 	  )
 	  val outerState2 = disjointVars.foldLeft(outerState)((myState, v) => {
 	    val unionAliasAttr = RelAliasAttribute(unionAlias, Attribute(Name("A_" + v.s)))
@@ -298,7 +302,7 @@
 		myState
 	      else
 		/* Constraint against the initial binding for this variable. */
-		R2RState(myState.joins, myState.varmap, myState.exprs + PrimaryExpressionEq(varToAttribute(myState.varmap, v), RValueAttr(unionAliasAttr)))
+		R2RState(myState.joins, myState.varmap, myState.exprs + RelationalExpressionEq(varToAttribute(myState.varmap, v), RValueAttr(unionAliasAttr)))
 	    } else {
 	      /* This variable is new to the outer context. */
 	      val mapper:SQL2RDFValueMapper = disjointState.varmap(v) match {
@@ -327,7 +331,7 @@
     val initState = R2RState(
       Set[AliasedResource](), 
       Map[Var, SQL2RDFValueMapper](), 
-      Set[PrimaryExpression]()
+      Set[Expression]()
     )
 
     val r2rState = mapGraphPattern(db, initState, sparql.gp, pk, enforeForeignKeys)
@@ -341,7 +345,11 @@
     Select(
       AttributeList(attrlist),
       TableList(r2rState.joins),
-      Expression(r2rState.exprs)
+      r2rState.exprs.size match {
+	case 0 => None
+	case 1 => Some(r2rState.exprs.toList(0))
+	case _ => Some(ExprConjunction(r2rState.exprs))
+      }
     )
   }
 }
--- a/src/main/scala/SQL.scala	Wed Dec 30 21:16:35 2009 -0500
+++ b/src/main/scala/SQL.scala	Thu Dec 31 05:49:51 2009 -0500
@@ -16,8 +16,11 @@
 case class Union(disjoints:Set[Select]) {
   override def toString = (disjoints mkString ("\nUNION\n"))
 }
-case class Select(attributelist:AttributeList, tablelist:TableList, expression:Expression) {
-  override def toString = attributelist+"\n"+tablelist+"\n"+expression
+case class Select(attributelist:AttributeList, tablelist:TableList, expression:Option[Expression]) {
+  override def toString = expression match {
+    case Some(expr) => attributelist+"\n"+tablelist+"\n WHERE "+expr
+    case None => attributelist+"\n"+tablelist
+  }
 }
 case class AttributeList(attributes:Set[NamedAttribute]) {
   // foo, bar
@@ -53,7 +56,7 @@
   override def toString = n.s /* "'" + n.s + "'" */
 }
 case class Subselect(union:Union) extends RelationORSubselect {
-  override def toString = "(\n" + union + "\n)"
+  override def toString = "\n" + union + "\n)"
 }
 case class RelAlias(n:Name) {
   override def toString = n.s /* "'" + n.s + "'" */
@@ -64,17 +67,21 @@
 case class AliasedResource(rel:RelationORSubselect, as:RelAlias) {
   override def toString = rel + " AS " + as
 }
-case class Expression(conjuncts:Set[PrimaryExpression]) {
-  override def toString = " WHERE " + (conjuncts mkString ("\n       AND "))
+sealed abstract class Expression
+case class ExprConjunction(exprs:Set[Expression]) extends Expression {
+  override def toString = (exprs mkString ("\n       AND "))
 }
-sealed abstract class PrimaryExpression
-case class PrimaryExpressionEq(l:RelAliasAttribute, r:RValue) extends PrimaryExpression {
+case class ExprDisjunction(exprs:Set[Expression]) extends Expression {
+  override def toString = "(" + (exprs mkString (")\n       OR (")) + ")"
+}
+sealed abstract class RelationalExpression extends Expression
+case class RelationalExpressionEq(l:RelAliasAttribute, r:RValue) extends RelationalExpression {
   override def toString = l + "=" + r
 }
-case class PrimaryExpressionLt(l:RelAliasAttribute, r:RValue) extends PrimaryExpression {
+case class RelationalExpressionLt(l:RelAliasAttribute, r:RValue) extends RelationalExpression {
   override def toString = l + "<" + r
 }
-case class PrimaryExpressionNotNull(l:RelAliasAttribute) extends PrimaryExpression {
+case class RelationalExpressionNotNull(l:RelAliasAttribute) extends RelationalExpression {
   override def toString = l + " IS NOT NULL"
 }
 sealed abstract class RValue
@@ -113,15 +120,7 @@
 
   def select:Parser[Select] =
     "SELECT" ~ attributelist ~ "FROM" ~ tablelist ~ opt(where) ^^
-    {
-      case "SELECT" ~ attributes ~ "FROM" ~ tables ~ whereexpr => {
-	val expression:Expression = whereexpr match {
-	  case None => Expression(Set())
-	  case Some(f) => f
-	}
-	Select(attributes, tables, expression)
-      }
-    }
+    { case "SELECT" ~ attributes ~ "FROM" ~ tables ~ whereexpr => Select(attributes, tables, whereexpr) }
 
   def where:Parser[Expression] =
     "WHERE" ~ expression ^^ { case "WHERE" ~ expression => expression }
@@ -170,17 +169,33 @@
     relationORsubselect ~ "AS" ~ relalias ^^
     { case rel1 ~ "AS" ~ rel2 => AliasedResource(rel1, rel2) }
 
-  def expression:Parser[Expression] = 
-    repsep(primaryexpression, "AND") ^^ 
-    { m => Expression(m.toSet) }
+  def expression:Parser[Expression] =
+    ORexpression ^^ { x => x }
 
-  def primaryexpression:Parser[PrimaryExpression] = (
+  def ORexpression:Parser[Expression] =
+    rep1sep (ANDexpression, "OR") ^^ 
+    { xs => if (xs.size > 1) ExprDisjunction(xs.toSet) else xs(0) }
+
+  def ANDexpression:Parser[Expression] =
+    rep1sep (relationalexpression, "AND") ^^ 
+    { xs => if (xs.size > 1) ExprConjunction(xs.toSet) else xs(0) }
+
+  def expression999:Parser[Expression] = (
+      expression ~ "AND" ~ rep1sep(expression, "AND") ^^ { case x~"AND"~m => ExprConjunction(Set(x) ++ m.toSet) }
+    | expression ~ "OR"  ~ rep1sep(expression, "OR" ) ^^ { case x~"OR" ~m => ExprDisjunction(Set(x) ++ m.toSet) }
+    | "(" ~ expression ~ ")"                          ^^ { case "("~x~")" => x }
+    | relationalexpression                               ^^ { px => px }
+  )
+
+  def relationalexpression:Parser[Expression] = (
       fqattribute ~ "=" ~ rvalue ^^
-      { case fqattribute ~ "=" ~ rvalue => PrimaryExpressionEq(fqattribute, rvalue) }
+      { case fqattribute ~ "=" ~ rvalue => RelationalExpressionEq(fqattribute, rvalue) }
     | fqattribute ~ "<" ~ rvalue ^^
-      { case fqattribute ~ "<" ~ rvalue => PrimaryExpressionLt(fqattribute, rvalue) }
+      { case fqattribute ~ "<" ~ rvalue => RelationalExpressionLt(fqattribute, rvalue) }
     | fqattribute ~ "IS" ~ "NOT" ~ "NULL" ^^
-      { case fqattribute ~ "IS" ~ "NOT" ~ "NULL" => PrimaryExpressionNotNull(fqattribute) }
+      { case fqattribute ~ "IS" ~ "NOT" ~ "NULL" => RelationalExpressionNotNull(fqattribute) }
+    | "(" ~ expression ~ ")" ^^
+      { case "("~x~")" => x }
   )
 
   def rvalue:Parser[RValue] = (
--- a/src/test/scala/SQLTest.scala	Wed Dec 30 21:16:35 2009 -0500
+++ b/src/test/scala/SQLTest.scala	Thu Dec 31 05:49:51 2009 -0500
@@ -5,6 +5,48 @@
 
 class SQLTest extends FunSuite {
 
+  test("parse ANDexpression") {
+    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
+    val a = Sql()
+    val e = """
+R_manager.id=R_emp.manager AND R_emp.lastName IS NOT NULL AND R_manager.lastName IS NOT NULL
+"""
+    val expected = ExprConjunction(Set(
+      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
+			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+    assert(expected === (a.parseAll(a.expression, e).get))
+  }
+
+  test("parse ORexpression") {
+    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
+    val a = Sql()
+    val e = """
+R_manager.id=R_emp.manager OR R_emp.lastName IS NOT NULL OR R_manager.lastName IS NOT NULL
+"""
+    val expected = ExprDisjunction(Set(
+      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
+			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+    assert(expected === (a.parseAll(a.expression, e).get))
+  }
+
+  test("parse nested expression") {
+    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
+    val a = Sql()
+    val e = """
+( R_manager.id=R_emp.manager OR R_emp.lastName IS NOT NULL OR R_manager.lastName IS NOT NULL )
+"""
+    val expected = ExprDisjunction(Set(
+      RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
+			     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
+      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))
+    assert(expected === (a.parseAll(a.expression, e).get))
+  }
+
   test("parse SQLbgp") {
     // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
     val a = Sql()
@@ -22,11 +64,11 @@
 							    AttrAlias(Name("A_managName"))))),
 			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))),
 					 AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))),
-			  Expression(Set(
-			    PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
+			  Some(ExprConjunction(Set(
+			    RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
 						RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))))),
-			    PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-			    PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))
+			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
+			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -41,9 +83,9 @@
 									      Attribute(Name("lastName"))),
 							    AttrAlias(Name("A_empName"))))),
 			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))))),
-			  Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
+			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
 							      RValueTyped(SQLDatatype.INTEGER,Name("18"))),
-					  PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))))
+					  RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -60,11 +102,11 @@
 							    AttrAlias(Name("A_empName"))))),
 			  TableList(Set(AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp"))),
 					 AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))))),
-			  Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
+			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("manager"))),
 							      RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-					  PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))),
+					  RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))),
 							      RValueTyped(SQLDatatype.STRING,Name("Johnson"))),
-					  PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))))))
+					  RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName"))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -92,20 +134,20 @@
 					AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager"))),
 					AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_upper"))),
 					AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_grandManager"))))),
-			  Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages"))),
+			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))),
-					 PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
+					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manager"))))),
-					 PrimaryExpressionLt(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))),
+					 RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("birthday"))))),
-					 PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manages"))),
+					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manages"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-					 PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("id"))),
+					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("id"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manager"))))),
-					 PrimaryExpressionLt(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("birthday"))),
+					 RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("birthday"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))))),
-					 PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
-					 PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("lastName")))))))
+					 RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("lastName")))),
+					 RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_grandManager")),Attribute(Name("lastName"))))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
@@ -139,9 +181,9 @@
 						   AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))),
 						   AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_manager")))
 						 )), 
-						 Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("manager"))),
+						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("manager"))),
 										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))),
-								PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName"))))))), 
+								RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("lastName")))))))), 
 					  Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_managed")), Attribute(Name("lastName"))),
 										  AttrAlias(Name("A_name"))), 
 								   NamedAttribute(RelAliasAttribute(RelAlias(Name("R_below")), Attribute(Name("manager"))),
@@ -150,19 +192,18 @@
 						   AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_below"))),
 						   AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_managed")))
 						 )), 
-						 Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_below")),Attribute(Name("manages"))),
+						 Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_below")),Attribute(Name("manages"))),
 										    RValueAttr(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("id"))))),
-								PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("lastName")))))))))),
+								RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_managed")),Attribute(Name("lastName"))))))))))),
 							RelAlias(Name("R_union1"))))), 
-			  Expression(Set(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_union1")),Attribute(Name("A_who"))),
+			  Some(ExprConjunction(Set(RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_union1")),Attribute(Name("A_who"))),
 							     RValueAttr(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("id"))))),
-					 PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("lastName"))),
-							     RValueTyped(SQLDatatype.STRING,Name("Smith"))))))
+					 RelationalExpressionEq(RelAliasAttribute(RelAlias(Name("R_who")),Attribute(Name("lastName"))),
+							     RValueTyped(SQLDatatype.STRING,Name("Smith")))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
   test("parse NULL as A_foo") {
-    // AliasedResource(Relation(Name("Employee")),RelAlias(Name("R_emp")))
     val a = Sql()
     val e = """
 SELECT R_above.manages AS A_who, NULL AS A_bday
@@ -175,8 +216,33 @@
 					     NamedAttribute(ConstNULL(),
 							    AttrAlias(Name("A_bday"))))),
 			  TableList(Set(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))))),
-			  Expression(Set(
-			    PrimaryExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))))))
+			  Some(
+			    RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))))))
+    assert(expected === (a.parseAll(a.select, e).get))
+  }
+
+  test("parse expr disjunction") {
+    val a = Sql()
+    val e = """
+SELECT R_above.manages AS A_who, NULL AS A_bday
+                FROM Manage AS R_above
+          WHERE (R_above.id IS NOT NULL) OR (R_above.id < 5 AND R_above.id < 3)
+"""
+    val expected = Select(AttributeList(Set(NamedAttribute(RelAliasAttribute(RelAlias(Name("R_above")),
+									      Attribute(Name("manages"))),
+							    AttrAlias(Name("A_who"))),
+					     NamedAttribute(ConstNULL(),
+							    AttrAlias(Name("A_bday"))))),
+			  TableList(Set(AliasedResource(Relation(Name("Manage")),RelAlias(Name("R_above"))))),
+			  Some(
+			    ExprDisjunction(Set(
+			      RelationalExpressionNotNull(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id")))),
+			      ExprConjunction(Set(
+				RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))),
+						       RValueTyped(SQLDatatype.INTEGER,Name("5"))),
+				RelationalExpressionLt(RelAliasAttribute(RelAlias(Name("R_above")),Attribute(Name("id"))),
+						       RValueTyped(SQLDatatype.INTEGER,Name("3")))
+			      ))))))
     assert(expected === (a.parseAll(a.select, e).get))
   }