migrated to FullOrPartialBindings @@some unchecked removals of NOT NULL constraints on primary keys
authorEric Prud'hommeaux <eric@w3.org>
Thu, 28 Jan 2010 16:07:12 -0500
changeset 138 9f82ffaafe02
parent 137 aafd5a43289d
child 139 a08c8fded443
migrated to FullOrPartialBindings @@some unchecked removals of NOT NULL constraints on primary keys
src/main/scala/RDB2RDFMain.scala
src/main/scala/SQL.scala
src/test/scala/RDB2RDFTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Sat Jan 09 10:59:13 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Thu Jan 28 16:07:12 2010 -0500
@@ -28,12 +28,51 @@
 object RDB2RDF {
   case class R2RState(joins:util.AddOrderedSet[sql.Join], varmap:Map[sparql.Var, SQL2RDFValueMapper], exprs:Set[sql.Expression])
 
-  sealed abstract class SQL2RDFValueMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression])
-  case class IntMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
-  case class StringMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
-  case class DateMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
-  case class RDFNoder(relation:sql.Relation, relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
-  case class RDFBNoder(relation:sql.Relation, relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpression]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
+  sealed abstract class FullOrPartialBinding
+  case class FullBinding(relaliasattr:sql.RelAliasAttribute) extends FullOrPartialBinding
+  case class BindingConstraint(expr:sql.RelationalExpression, relaliasattr:sql.RelAliasAttribute)
+  case class PartialBinding(binders:Set[BindingConstraint]) extends FullOrPartialBinding
+
+  def toExpr(against:FullOrPartialBinding):sql.Expression = {
+    /* if (g_union1._DISJOINT_ != 0, g_union1.who, if (g_union2._DISJOINT_ != 3, g_union2.who, NULL)) */
+    against match {
+      case FullBinding(relaliasattr) =>
+	sql.PrimaryExpressionAttr(relaliasattr)
+      case PartialBinding(binders) =>
+	binders.toList.reverse.foldLeft(sql.ConstNULL():sql.Expression)((exp, binding) => {
+	  val BindingConstraint(expr, relaliasattr) = binding
+	  sql.IfElse(expr, sql.PrimaryExpressionAttr(relaliasattr), exp)
+	})
+    }
+  }
+  def addExpr(binding:FullOrPartialBinding, relAliasAttr:sql.RelAliasAttribute, expr:sql.RelationalExpression):FullOrPartialBinding = {
+    binding match {
+      case FullBinding(relaliasattr) =>
+	binding
+      case PartialBinding(binders) =>
+	PartialBinding(binders + BindingConstraint(expr, relAliasAttr))
+    }
+  }
+  def toConstraint999(constrainMe:sql.RelAliasAttribute, against:FullOrPartialBinding):sql.Expression = {
+    against match {
+      case FullBinding(relaliasattr) =>
+	sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(constrainMe),
+				   sql.PrimaryExpressionAttr(relaliasattr))
+      case PartialBinding(binders) =>
+	sql.ExprConjunction({binders.map(b => {
+	  val BindingConstraint(expr, relaliasattr) = b
+	  sql.ExprDisjunction(Set(expr,
+				  sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(constrainMe),
+							     sql.PrimaryExpressionAttr(relaliasattr))))})})
+    }
+  }
+
+  sealed abstract class SQL2RDFValueMapper(binding:FullOrPartialBinding)
+  case class IntMapper(binding:FullOrPartialBinding) extends SQL2RDFValueMapper(binding)
+  case class StringMapper(binding:FullOrPartialBinding) extends SQL2RDFValueMapper(binding)
+  case class DateMapper(binding:FullOrPartialBinding) extends SQL2RDFValueMapper(binding)
+  case class RDFNoder(relation:sql.Relation, binding:FullOrPartialBinding) extends SQL2RDFValueMapper(binding)
+  case class RDFBNoder(relation:sql.Relation, binding:FullOrPartialBinding) extends SQL2RDFValueMapper(binding)
 
   def relAliasFromS(s:sparql.S):sql.RelAlias = {
     s match {
@@ -103,7 +142,25 @@
     */
     val constrainMe = if (optAttr.isDefined) sql.RelAliasAttribute(alias, optAttr.get) else sql.RelAliasAttribute(alias, sql.Attribute(sql.Name("_no_such_attribute")))
     val reldesc = db.relationdescs(rel)
-    if (state.varmap.contains(v) && varToAttribute(state.varmap, v) != constrainMe) {
+    val boundTo = FullBinding(constrainMe)
+    val binding = reldesc.primarykey match {
+      case Some(sql.Attribute(constrainMe.attribute.n)) => RDFNoder(rel, boundTo)
+      case _ => {
+	// e.g. sql.Attribute(sql.Name("id")) or None
+	if (reldesc.attributes.contains(constrainMe.attribute)) {
+	  reldesc.attributes(constrainMe.attribute) match {
+	    case sql.ForeignKey(fkrel, fkattr) => RDFNoder(rel, boundTo)
+	    case sql.Value(sql.Datatype("Int")) => IntMapper(boundTo)
+	    case sql.Value(sql.Datatype("String")) => StringMapper(boundTo)
+	    case sql.Value(sql.Datatype("Date")) => DateMapper(boundTo)
+	  }
+	} else {
+	  RDFBNoder(rel, boundTo)
+	}
+      }
+    }
+
+    if (state.varmap.contains(v) && state.varmap(v) != constrainMe) {
       /* The variable has already been bound to another attribute. */
       /* Constraint against the initial binding for this variable. */
       val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(constrainMe),
@@ -116,27 +173,6 @@
 	     )
     } else {
       /* This is a new variable or a replacement bindinig for an old variable. */
-      val binding = reldesc.primarykey match {
-	case Some(sql.Attribute(constrainMe.attribute.n)) => 
-	  RDFNoder(rel, constrainMe, Set())
-	case _ => {
-	  // e.g. sql.Attribute(sql.Name("id")) or None
-	  if (reldesc.attributes.contains(constrainMe.attribute)) {
-	    reldesc.attributes(constrainMe.attribute) match {
-	      case sql.ForeignKey(fkrel, fkattr) =>
-		RDFNoder(rel, constrainMe, Set())
-	      case sql.Value(sql.Datatype("Int")) =>
-		IntMapper(constrainMe, Set())
-	      case sql.Value(sql.Datatype("String")) =>
-		StringMapper(constrainMe, Set())
-	      case sql.Value(sql.Datatype("Date")) =>
-		DateMapper(constrainMe, Set())
-	    }
-	  } else {
-	    RDFBNoder(rel, constrainMe, Set())
-	  }
-	}
-      }
       R2RState(state.joins, state.varmap + (v -> binding), state.exprs)
     }
   }
@@ -144,15 +180,15 @@
   def toString(relaliasattr:sql.RelAliasAttribute) : String = {
     relaliasattr.relalias.n.s + "." + relaliasattr.attribute.n.s
   }
-  def toString(mapper:SQL2RDFValueMapper) : String = {
-    mapper match {
-      case IntMapper(relalias, disjoints) => "INT: " + toString(relalias)
-      case StringMapper(relalias, disjoints) => "STRING: " + toString(relalias)
-      case DateMapper(relalias, disjoints) => "DATE: " + toString(relalias)
-      case RDFNoder(relation, relalias, disjoints) => "RDFNoder: " + relation.n.s + ", " + toString(relalias)
-      case RDFBNoder(relation, relalias, disjoints) => "RDFBNoder: " + relation.n.s + ", " + toString(relalias)
-    }
-  }
+  // def toString(mapper:SQL2RDFValueMapper) : String = {
+  //   mapper match {
+  //     case IntMapper(relalias, disjoints) => "INT: " + toString(relalias)
+  //     case StringMapper(relalias, disjoints) => "STRING: " + toString(relalias)
+  //     case DateMapper(relalias, disjoints) => "DATE: " + toString(relalias)
+  //     case RDFNoder(relation, relalias, disjoints) => "RDFNoder: " + relation.n.s + ", " + toString(relalias)
+  //     case RDFBNoder(relation, relalias, disjoints) => "RDFBNoder: " + relation.n.s + ", " + toString(relalias)
+  //   }
+  // }
 
   /* bindOnPredicate: map a given triple to one or two joined tables, variable
    * bindings to RelVarAttributes, and constraints if those variables were
@@ -249,49 +285,69 @@
     }
   }
 
+  def bindingConstraintToAttribute(constraint:BindingConstraint):sql.RelAliasAttribute = {
+    val BindingConstraint(expr:sql.RelationalExpression, relaliasattr:sql.RelAliasAttribute) = constraint;
+    relaliasattr
+  }
+  def bindingToAttribute(binding:FullOrPartialBinding):sql.RelAliasAttribute = {
+    binding match {
+      case FullBinding(relaliasattr:sql.RelAliasAttribute) => relaliasattr
+      case PartialBinding(binders) => bindingConstraintToAttribute(binders.toList(0))
+    }
+  }
   def varToAttribute(varmap:Map[sparql.Var, SQL2RDFValueMapper], vvar:sparql.Var):sql.RelAliasAttribute = {
     varmap(vvar) match {
-      case IntMapper(relalias, disjoints) => relalias
-      case StringMapper(relalias, disjoints) => relalias
-      case DateMapper(relalias, disjoints) => relalias
-      case RDFNoder(relation, relalias, disjoints) => relalias
-      case RDFBNoder(relation, relalias, disjoints) =>  relalias // error("BNode should not need relalias " + relalias)
+      case IntMapper(binding) => bindingToAttribute(binding)
+      case StringMapper(binding) => bindingToAttribute(binding)
+      case DateMapper(binding) => bindingToAttribute(binding)
+      case RDFNoder(relation, binding) => bindingToAttribute(binding)
+      case RDFBNoder(relation, binding) =>  bindingToAttribute(binding) // error("BNode should not need relalias " + relalias)
     }
   }
 
+  def bindingConstraintToExpression(constraint:BindingConstraint):sql.RelationalExpression = {
+    val BindingConstraint(expr:sql.RelationalExpression, relaliasattr:sql.RelAliasAttribute) = constraint;
+    expr
+  }
+  def bindingToDisjoints(binding:FullOrPartialBinding):Set[sql.RelationalExpression] = {
+    binding match {
+      case FullBinding(relaliasattr:sql.RelAliasAttribute) => Set[sql.RelationalExpression]()
+      case PartialBinding(binders) => binders.map({b => bindingConstraintToExpression(b)})
+    }
+  }
   def varToAttributeDisjoints(varmap:Map[sparql.Var, SQL2RDFValueMapper], vvar:sparql.Var):Set[sql.RelationalExpression] = {
     varmap(vvar) match {
-      case IntMapper(relalias, disjoints) => disjoints
-      case StringMapper(relalias, disjoints) => disjoints
-      case DateMapper(relalias, disjoints) => disjoints
-      case RDFNoder(relation, relalias, disjoints) => disjoints
-      case RDFBNoder(relation, relalias, disjoints) =>  disjoints // error("BNode should not need join constraints " + relalias)
+      case IntMapper(binding) => bindingToDisjoints(binding)
+      case StringMapper(binding) => bindingToDisjoints(binding)
+      case DateMapper(binding) => bindingToDisjoints(binding)
+      case RDFNoder(relation, binding) => bindingToDisjoints(binding)
+      case RDFBNoder(relation, binding) =>  bindingToDisjoints(binding) // error("BNode should not need relalias " + relalias)
     }
   }
 
   def varToConcat(varmap:Map[sparql.Var, SQL2RDFValueMapper], vvar:sparql.Var, stem:StemURI):sql.Expression = {
     varmap(vvar) match {
-      case IntMapper(relalias, _) => sql.PrimaryExpressionAttr(relalias)
-      case StringMapper(relalias, _) => 
+      case IntMapper(binding) => sql.PrimaryExpressionAttr(bindingToAttribute(binding))
+      case StringMapper(binding) => 
 	sql.Concat(List(sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name("'")),
-		    sql.PrimaryExpressionAttr(relalias),
+		    sql.PrimaryExpressionAttr(bindingToAttribute(binding)),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name("'^^<http://www.w3.org/2001/XMLSchema#string>"))))
-      case DateMapper(relalias, _) => sql.PrimaryExpressionAttr(relalias)
-      case RDFNoder(relation, relalias, _) => 
+      case DateMapper(binding) => sql.PrimaryExpressionAttr(bindingToAttribute(binding))
+      case RDFNoder(relation, binding) => 
 	sql.Concat(List(sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name(stem.s)),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),relation.n),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name("/")),
-		    sql.PrimaryExpressionTyped(sql.Datatype("String"),relalias.attribute.n),
+		    sql.PrimaryExpressionTyped(sql.Datatype("String"),bindingToAttribute(binding).attribute.n),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name(".")),
-		    sql.PrimaryExpressionAttr(relalias),
+		    sql.PrimaryExpressionAttr(bindingToAttribute(binding)),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name("#record"))))
-      case RDFBNoder(relation, relalias, _) => 
+      case RDFBNoder(relation, binding) => 
 	sql.Concat(List(sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name("_:")),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),relation.n),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name(".")),
-		    sql.PrimaryExpressionTyped(sql.Datatype("String"),relalias.attribute.n),
+		    sql.PrimaryExpressionTyped(sql.Datatype("String"),bindingToAttribute(binding).attribute.n),
 		    sql.PrimaryExpressionTyped(sql.Datatype("String"),sql.Name(".")),
-		    sql.PrimaryExpressionAttr(relalias)))
+		    sql.PrimaryExpressionAttr(bindingToAttribute(binding))))
     }
     
   }
@@ -367,7 +423,6 @@
 	val (subselects, _) = disjoints.foldLeft((Set[sql.Select](), 0))((incPair,disjoint) => {
 	  val (subselects, no) = incPair
 	  val disjointState = mapGraphPattern(db, emptyState, disjoint, enforceForeignKeys)
-	  val disjointVars = findVars(disjoint)
 	  val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)),
 					      sql.AttrAlias(sql.Name("_DISJOINT_")))
 
@@ -410,13 +465,12 @@
 	      /* The variable has already been bound. */
 	      val newMap:Map[sparql.Var, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
 		/* Same var was bound in an earlier disjoint. */
-		val oldDisjoints = varToAttributeDisjoints(myState.varmap, v)
-		Map(v -> { disjointState.varmap(v) match {
-		  case IntMapper(_, _)      => IntMapper(varAliasAttr, oldDisjoints + disjointCond)
-		  case StringMapper(_, _)   => StringMapper(varAliasAttr, oldDisjoints + disjointCond)
-		  case DateMapper(_, _)     => DateMapper(varAliasAttr, oldDisjoints + disjointCond)
-		  case RDFNoder(rel, _, _)  => RDFNoder(rel, varAliasAttr, oldDisjoints + disjointCond)
-		  case RDFBNoder(rel, _, _) => RDFBNoder(rel, varAliasAttr, oldDisjoints + disjointCond)
+		Map(v -> { myState.varmap(v) match {
+		  case IntMapper(binding)      => IntMapper(addExpr(binding, varAliasAttr, disjointCond))
+		  case StringMapper(binding)   => StringMapper(addExpr(binding, varAliasAttr, disjointCond))
+		  case DateMapper(binding)     => DateMapper(addExpr(binding, varAliasAttr, disjointCond))
+		  case RDFNoder(rel, binding)  => RDFNoder(rel, addExpr(binding, varAliasAttr, disjointCond))
+		  case RDFBNoder(rel, binding) => RDFBNoder(rel, addExpr(binding, varAliasAttr, disjointCond))
 		} } )
 	      } else
 		Map()
@@ -437,12 +491,13 @@
 	      R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
 	    } else {
 	      /* This variable is new to the outer context. */
+	      val p = PartialBinding(Set(BindingConstraint(disjointCond, varAliasAttr)))
 	      val mapper:SQL2RDFValueMapper = disjointState.varmap(v) match {
-		case IntMapper(_, _)      => IntMapper(varAliasAttr, Set(disjointCond))
-		case StringMapper(_, _)   => StringMapper(varAliasAttr, Set(disjointCond))
-		case DateMapper(_, _)   => DateMapper(varAliasAttr, Set(disjointCond))
-		case RDFNoder(rel, _, _)  => RDFNoder(rel, varAliasAttr, Set(disjointCond))
-		case RDFBNoder(rel, _, _) => RDFBNoder(rel, varAliasAttr, Set(disjointCond))
+		case IntMapper(_)      => IntMapper(p)
+		case StringMapper(_)   => StringMapper(p)
+		case DateMapper(_)   => DateMapper(p)
+		case RDFNoder(rel, _)  => RDFNoder(rel, p)
+		case RDFBNoder(rel, _) => RDFBNoder(rel, p)
 	      }
 	      R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
 	    }
@@ -516,14 +571,13 @@
 	    /* The variable has already been bound. */
 	    val newMap:Map[sparql.Var, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
 	      /* Same var was bound in an earlier optional. */
-	      val oldDisjoints = varToAttributeDisjoints(myState.varmap, v)
 	      // myState
-	      Map(v -> { optionalState.varmap(v) match {
-		case IntMapper(_, _)      => IntMapper(varAliasAttr, oldDisjoints + optionalCond)
-		case StringMapper(_, _)   => StringMapper(varAliasAttr, oldDisjoints + optionalCond)
-		case DateMapper(_, _)     => DateMapper(varAliasAttr, oldDisjoints + optionalCond)
-		case RDFNoder(rel, _, _)  => RDFNoder(rel, varAliasAttr, oldDisjoints + optionalCond)
-		case RDFBNoder(rel, _, _) => RDFBNoder(rel, varAliasAttr, oldDisjoints + optionalCond)
+	      Map(v -> { myState.varmap(v) match {
+		case IntMapper(binding)      => IntMapper(addExpr(binding, varAliasAttr, optionalCond))
+		case StringMapper(binding)   => StringMapper(addExpr(binding, varAliasAttr, optionalCond))
+		case DateMapper(binding)     => DateMapper(addExpr(binding, varAliasAttr, optionalCond))
+		case RDFNoder(rel, binding)  => RDFNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
+		case RDFBNoder(rel, binding) => RDFBNoder(rel, addExpr(binding, varAliasAttr, optionalCond))
 	      } } )
 	    } else
 	      Map()
@@ -540,12 +594,13 @@
 	    R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
 	  } else {
 	    /* This variable is new to the outer context. */
+	      val p = PartialBinding(Set(BindingConstraint(optionalCond, varAliasAttr)))
 	    val mapper:SQL2RDFValueMapper = optionalState.varmap(v) match {
-	      case IntMapper(_, _)      => IntMapper(varAliasAttr, Set(optionalCond))
-	      case StringMapper(_, _)   => StringMapper(varAliasAttr, Set(optionalCond))
-	      case DateMapper(_, _)   => DateMapper(varAliasAttr, Set(optionalCond))
-	      case RDFNoder(rel, _, _)  => RDFNoder(rel, varAliasAttr, Set(optionalCond))
-	      case RDFBNoder(rel, _, _) => RDFBNoder(rel, varAliasAttr, Set(optionalCond))
+	      case IntMapper(binding)      => IntMapper(p)
+	      case StringMapper(binding)   => StringMapper(p)
+	      case DateMapper(binding)   => DateMapper(p)
+	      case RDFNoder(rel, binding)  => RDFNoder(rel, p)
+	      case RDFBNoder(rel, binding) => RDFBNoder(rel, p)
 	    }
 	    R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
 	  }
--- a/src/main/scala/SQL.scala	Sat Jan 09 10:59:13 2010 -0500
+++ b/src/main/scala/SQL.scala	Thu Jan 28 16:07:12 2010 -0500
@@ -139,6 +139,9 @@
 case class Concat(args:List[Expression]) extends PrimaryExpression {
   override def toString = args.mkString("CONCAT(", ", ", ")")
 }
+case class IfElse(cond:Expression, pass:Expression, fail:Expression) extends PrimaryExpression {
+  override def toString = "CONCAT(" + cond + ", " + pass +  ", " + fail + ")"
+}
 
 case class Name(s:String)
 
@@ -276,6 +279,7 @@
     | chars  ^^ { x => PrimaryExpressionTyped(Datatype.STRING, Name(x.substring(1, x.size - 1))) }
     | "NULL" ^^ { case "NULL" => ConstNULL() }
     | "CONCAT" ~ "(" ~ rep1sep(expression, ",") ~ ")" ^^ { case "CONCAT"~"("~expressions~")" => Concat(expressions) }
+    | "IF" ~ "(" ~ expression ~ "," ~ expression ~ "," ~ expression ~ ")" ^^ { case "IF"~"("~c~","~p~","~f~")" => IfElse(c, p, f) }
     | "(" ~ expression ~ ")" ^^ { case "("~x~")" => x }
   )
 
@@ -290,6 +294,11 @@
 	PrettySql.stripNotNulls(expression.get, nonNullAttrs)
       }
       else None
+    val XeqXStripped =
+      if (nullStripped.isDefined) {
+	PrettySql.stripXeqX(nullStripped.get)
+      }
+      else None
     val strippedTables = tablelist.joins.foldLeft(AddOrderedSet[Join]())((set, join) => set + {
       join match {
 	case InnerJoin(AliasedResource(rel, as), optOn) => InnerJoin(AliasedResource({ rel match {
@@ -303,7 +312,7 @@
 	  case r:Relation => r
 	}}, as), on)
       }})
-    Select(attributelist, TableList(strippedTables), nullStripped)
+    Select(attributelist, TableList(strippedTables), XeqXStripped)
   }
 }
 
@@ -324,6 +333,7 @@
       case e:Concat => Set()
       case RelationalExpressionNull(a) => findNonNullRelAliasAttrs(a)
       case RelationalExpressionNotNull(a) => Set()
+      case IfElse(eef, den, els) => Set()
     }
   }
   def stripNotNulls(expr:Expression, stripMe:Set[RelAliasAttribute]):Option[Expression] = {
@@ -348,6 +358,33 @@
 	if (stripMe.contains(a)) None
 	else Some(RelationalExpressionNotNull(PrimaryExpressionAttr(a)))
       case e:RelationalExpressionNotNull => Some(e)
+      case e:IfElse => Some(e)
+    }
+  }
+  def stripXeqX(expr:Expression):Option[Expression] = {
+    expr match {
+      case ExprConjunction(l) => Some(ExprConjunction({l.foldLeft(Set[Expression]())((s, e) => {
+	val e2 = stripXeqX(e)
+	if (e2.isDefined) s + e2.get
+	else s})}))
+      case ExprDisjunction(l) => Some(ExprDisjunction({l.foldLeft(Set[Expression]())((s, e) => {
+	val e2 = stripXeqX(e)
+	if (e2.isDefined) s + e2.get
+	else s})}))
+      case e:RelationalExpressionEq => {
+	val RelationalExpressionEq(l, r) = e
+	if (l == r) None
+	else Some(e)
+      }
+      case e:RelationalExpressionNe => Some(e)
+      case e:RelationalExpressionLt => Some(e)
+      case e:PrimaryExpressionTyped => Some(e)
+      case e:PrimaryExpressionAttr => Some(e)
+      case e:ConstNULL => Some(e)
+      case e:Concat => Some(e)
+      case e:RelationalExpressionNull => Some(e)
+      case e:RelationalExpressionNotNull => Some(e)
+      case e:IfElse => Some(e)
     }
   }
   implicit def toPrettySql(select:Select) = PrettySql(select)
--- a/src/test/scala/RDB2RDFTest.scala	Sat Jan 09 10:59:13 2010 -0500
+++ b/src/test/scala/RDB2RDFTest.scala	Thu Jan 28 16:07:12 2010 -0500
@@ -310,8 +310,7 @@
 SELECT R_emp.lastName AS empName, R_manager.lastName AS manageName
        FROM Employee AS R_emp
             INNER JOIN Employee AS R_manager ON R_manager.empid=R_emp.manager
- WHERE R_emp    .lastName IS NOT NULL AND R_emp    .empid IS NOT NULL
-   AND R_manager.lastName IS NOT NULL
+ WHERE R_emp    .lastName IS NOT NULL AND R_manager.lastName IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), true, false)
     assert(generated === parsed)
@@ -340,7 +339,6 @@
 SELECT R_emp.lastName AS empName
        FROM Employee AS R_emp
  WHERE R_emp.manager=18 AND R_emp.lastName IS NOT NULL
- AND R_emp.empid IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), false, false)
     assert(generated === parsed)
@@ -368,7 +366,6 @@
        FROM Employee AS R_emp
             INNER JOIN Employee AS R_empid18
  WHERE R_empid18.empid=R_emp.manager AND R_empid18.empid=18 AND R_emp.lastName IS NOT NULL
- AND R_emp.empid IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), true, false)
     assert(generated === parsed)
@@ -399,7 +396,6 @@
   FROM Employee AS R_emp
        INNER JOIN Employee AS R_manager
 WHERE R_manager.empid=R_emp.manager AND R_manager.lastName="Johnson" AND R_emp.lastName IS NOT NULL
- AND R_emp.empid IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), true, false)
     assert(generated === parsed)
@@ -445,7 +441,7 @@
        INNER JOIN Tasks AS R_utask ON R_utask.taskid=R_upper.task
        INNER JOIN Employee AS R_grandManager ON R_grandManager.empid=R_utask.lead
  WHERE R_taskLead.birthday<R_emp.birthday AND R_grandManager.birthday<R_taskLead.birthday
-   AND R_emp.lastName IS NOT NULL AND R_grandManager.lastName IS NOT NULL AND R_lower.id IS NOT NULL AND R_upper.id IS NOT NULL
+   AND R_emp.lastName IS NOT NULL AND R_grandManager.lastName IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), true, false)
     assert(generated === parsed)
@@ -491,7 +487,6 @@
                 INNER JOIN Employee AS R_taskLead ON R_taskLead.empid=R_atask.lead
           WHERE R_above.employee IS NOT NULL
             AND R_taskLead.lastName IS NOT NULL
-            AND R_above.id IS NOT NULL
        UNION
          SELECT 1 AS _DISJOINT_, NULL AS above, NULL AS atask,
                 R_btask.lead AS who,R_managed.lastName AS name,
@@ -503,7 +498,6 @@
                 INNER JOIN Employee AS R_managed ON R_managed.empid=R_below.employee
           WHERE R_managed.lastName IS NOT NULL
             AND R_btask.lead IS NOT NULL
-            AND R_below.id IS NOT NULL
                        ) AS G_union1
  WHERE R_who.lastName="Smith"
        AND R_who.empid IS NOT NULL
@@ -558,8 +552,7 @@
            FROM TaskAssignments AS R_above
                 INNER JOIN Tasks AS R_atask ON R_atask.taskid=R_above.task
                 INNER JOIN Employee AS R_taskLead ON R_taskLead.empid=R_atask.lead
-          WHERE R_above.employee IS NOT NULL AND R_above.id IS NOT NULL
-            AND R_taskLead.lastName IS NOT NULL
+          WHERE R_above.employee IS NOT NULL AND R_taskLead.lastName IS NOT NULL
        UNION
          SELECT NULL AS above, NULL AS atask, R_managed.birthday AS bday, R_below.id AS below,
                 R_below.task AS btask, R_below.employee AS managed, R_managed.lastName AS name,
@@ -567,8 +560,7 @@
            FROM TaskAssignments AS R_below
                 INNER JOIN Tasks AS R_btask ON R_btask.taskid=R_below.task
                 INNER JOIN Employee AS R_managed ON R_managed.empid=R_below.employee
-          WHERE R_below.id IS NOT NULL
-            AND R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
+          WHERE R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
                   ) AS G_union0
        INNER JOIN Employee AS R_who
  WHERE (G_union0._DISJOINT_!=0 OR R_who.empid=G_union0.who)
@@ -616,8 +608,7 @@
            FROM TaskAssignments AS R_above
                 INNER JOIN Tasks AS R_atask ON R_atask.taskid=R_above.task
                 INNER JOIN Employee AS R_taskLead ON R_taskLead.empid=R_atask.lead
-          WHERE R_above.employee IS NOT NULL AND R_above.id IS NOT NULL
-            AND R_taskLead.lastName IS NOT NULL
+          WHERE R_above.employee IS NOT NULL AND R_taskLead.lastName IS NOT NULL
        UNION
          SELECT NULL AS above, NULL AS atask, R_managed.birthday AS bday, R_below.id AS below,
                 R_below.task AS btask, R_below.employee AS managed, R_managed.lastName AS name,
@@ -625,14 +616,12 @@
            FROM TaskAssignments AS R_below
                 INNER JOIN Tasks AS R_btask ON R_btask.taskid=R_below.task
                 INNER JOIN Employee AS R_managed ON R_managed.empid=R_below.employee
-          WHERE R_below.id IS NOT NULL
-            AND R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
+          WHERE R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
                   ) AS G_union1
  WHERE (G_union1._DISJOINT_!=0 OR G_union1.who=R_who.empid)
    AND (G_union1._DISJOINT_!=1 OR G_union1.bday=R_who.birthday)
    AND (G_union1._DISJOINT_!=1 OR G_union1.who=R_who.empid)
-   AND R_who.birthday IS NOT NULL AND R_who.empid IS NOT NULL
-   AND R_who.lastName="Smith"
+   AND R_who.birthday IS NOT NULL AND R_who.lastName="Smith"
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), false, false)
     assert(generated === parsed)
@@ -674,8 +663,7 @@
            FROM TaskAssignments AS R_above
                 INNER JOIN Tasks AS R_atask ON R_atask.taskid=R_above.task
                 INNER JOIN Employee AS R_taskLead ON R_taskLead.empid=R_atask.lead
-          WHERE R_above.employee IS NOT NULL AND R_above.id IS NOT NULL
-            AND R_taskLead.lastName IS NOT NULL
+          WHERE R_above.employee IS NOT NULL AND R_taskLead.lastName IS NOT NULL
        UNION
          SELECT NULL AS above, NULL AS atask, R_managed.birthday AS bday, R_below.id AS below,
                 R_below.task AS btask, R_below.employee AS managed, R_managed.lastName AS name,
@@ -683,13 +671,12 @@
            FROM TaskAssignments AS R_below
                 INNER JOIN Tasks AS R_btask ON R_btask.taskid=R_below.task
                 INNER JOIN Employee AS R_managed ON R_managed.empid=R_below.employee
-          WHERE R_below.id IS NOT NULL
-            AND R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
+          WHERE R_btask.lead IS NOT NULL AND R_managed.birthday IS NOT NULL AND R_managed.lastName IS NOT NULL
                   ) AS G_union1
  WHERE (G_union1._DISJOINT_!=0 OR G_union1.who=R_who.empid)
    AND (G_union1._DISJOINT_!=1 OR G_union1.who=R_who.empid)
    AND (G_union1._DISJOINT_!=1 OR R_who.birthday=G_union1.bday)
-   AND R_who.empid IS NOT NULL AND R_who.lastName="Smith"
+   AND R_who.lastName="Smith"
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), false, false)
     assert(generated === parsed)
@@ -747,7 +734,7 @@
        SELECT R_taskLead.manager AS emp, R_grandManager.lastName AS grandManagName, R_taskLead.manager AS grandManager, R_taskLead.empid AS taskLead, 1 AS _DISJOINT_
          FROM Employee AS R_taskLead
               INNER JOIN Employee AS R_grandManager ON R_grandManager.empid=R_taskLead.manager
-        WHERE R_grandManager.lastName IS NOT NULL AND R_taskLead.empid IS NOT NULL
+        WHERE R_grandManager.lastName IS NOT NULL
                   ) AS G_opt1 ON 1=1
        INNER JOIN Employee AS R_emp
  WHERE (G_opt1._DISJOINT_ IS NULL OR R_emp.empid=G_opt1.emp)
@@ -834,8 +821,7 @@
    AND (G_opt1._DISJOINT_ IS NULL OR R_emp4.birthday=G_opt1.birthday)
    AND R_emp1.lastName<R_emp2.lastName
    AND R_emp2.empid IS NOT NULL AND R_emp2.lastName<R_emp3.lastName
-   AND R_emp3.empid IS NOT NULL AND R_emp3.lastName<R_emp4.lastName
-   AND R_emp4.empid IS NOT NULL AND R_emp1.empid IS NOT NULL
+   AND R_emp3.lastName<R_emp4.lastName AND R_emp1.empid IS NOT NULL
 """).get
     val generated = RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), false, false)
     assert(generated === parsed)