~ restored subselect (to put parens around subselects)
authorEric Prud'hommeaux <bertails@w3.org>
Sat, 02 Jan 2010 18:34:46 -0500
changeset 101 fb5ec48c0c74
parent 100 22e67b5fa2f0
child 102 2bec0e8a7aec
~ restored subselect (to put parens around subselects)
src/main/scala/RDB2RDFMain.scala
src/main/scala/SQL.scala
src/test/scala/SQLTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Sat Jan 02 16:33:28 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Sat Jan 02 18:34:46 2010 -0500
@@ -361,12 +361,87 @@
 	  })
 	  (outerState2, outerDisjoints ++ Set(subselect), no+1)
 	})
-	val union = Subselect(Union(disjoints))
-	R2RState(state.joins + InnerJoin(AliasedResource(union,unionAlias)), state2.varmap, state2.exprs)
+	val subselect = Subselect(Union(disjoints))
+	R2RState(state.joins + InnerJoin(AliasedResource(subselect,unionAlias)), state2.varmap, state2.exprs)
       }
-      case OptionalGraphPattern(gp) => {
-	state
-      }
+      // case OptionalGraphPattern(gp) => {
+      // 	val leftJoinAlias = RelAlias(Name("R_leftJoin" + state.joins.size))
+      // 	val initDisjoints:Set[Select] = Set()
+      // 	val emptyState = R2RState(
+      // 	  Set[Join](), 
+      // 	  Map[Var, SQL2RDFValueMapper](), 
+      // 	  Set[Expression]()
+      // 	)
+      // 	val leftJoinVars = list.foldLeft(Set[Var]())((mySet,disjoint) => mySet ++ findVars(disjoint)).toList
+      // 	val (state2, disjoints, count) = list.foldLeft((state, initDisjoints, 0))((incPair,disjoint) => {
+      // 	  val (outerState, outerDisjoints, no) = incPair
+      // 	  val disjointState = mapGraphPattern(db, emptyState, disjoint, pk, enforeForeignKeys)
+      // 	  val disjointVars = findVars(disjoint)
+      // 	  val disjointNo = NamedAttribute(ConstInt("" + no), AttrAlias(Name("_DISJOINT_")))
+      // 	  val disjointNoAliasAttr = RelAliasAttribute(leftJoinAlias, Attribute(Name("_DISJOINT_")))
+      // 	  val disjointCond = RelationalExpressionNe(disjointNoAliasAttr, RValueTyped(SQLDatatype.INTEGER,Name("" + no)))
+
+      // 	  val attrlist:Set[NamedAttribute] = leftJoinVars.foldLeft(Set(disjointNo))((attrs, v) => {
+      // 	    val attrOrNull = if (disjointState.varmap.contains(v)) varToAttribute(disjointState.varmap, v) else ConstNULL()
+      // 	    attrs ++ Set(NamedAttribute(attrOrNull, AttrAlias(Name("A_" + v.s))))
+      // 	  })
+
+      // 	  val subselect = Select(
+      // 	    AttributeList(attrlist),
+      // 	    TableList(disjointState.joins),
+      // 	    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 varAliasAttr = RelAliasAttribute(leftJoinAlias, Attribute(Name("A_" + v.s)))
+      // 	    if (myState.varmap.contains(v)) {
+      // 	      /* The variable has already been bound. */
+      // 	      val newMap:Map[Var, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
+      // 		/* Same var was bound in an earlier disjoint. */
+      // 		val oldDisjoints = varToAttributeDisjoints(myState.varmap, v)
+      // 		// myState
+      // 		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)
+      // 		} } )
+      // 	      } else
+      // 		Map()
+      // 	      val newConstraints =
+      // 		if (varToAttribute(outerState.varmap, v) != varAliasAttr) {
+      // 		  /* Constraint against binding from earlier GP. */
+      // 		  val constraint = RelationalExpressionEq(varToAttribute(outerState.varmap, v), RValueAttr(varAliasAttr))
+      // 		  if (varToAttributeDisjoints(outerState.varmap, v).size > 0)
+      // 		    // (leftJoin0._DISJOINT_ != 0 AND leftJoin1._DISJOINT_ != 2) OR leftJoin0.x=leftJoin1.x
+      // 		    varToAttributeDisjoints(outerState.varmap, v) map ((d) => ExprDisjunction(Set(ExprConjunction(Set(d, disjointCond)), constraint)))
+      // 		  else
+      // 		    Set(ExprDisjunction(Set(disjointCond, constraint)))
+      // 		} else {
+      // 		  Set()
+      // 		}
+      // 	      R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
+      // 	    } else {
+      // 	      /* This variable is new to the outer context. */
+      // 	      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))
+      // 	      }
+      // 	      R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
+      // 	    }
+      // 	  })
+      // 	  (outerState2, outerDisjoints ++ Set(subselect), no+1)
+      // 	})
+      // 	val subselect = Subselect(Union(disjoints))
+      // 	R2RState(state.joins + InnerJoin(AliasedResource(subselect,unionAlias)), state2.varmap, state2.exprs)
+      // }
       case x => error("no code to handle " + x)
     }
   }
--- a/src/main/scala/SQL.scala	Sat Jan 02 16:33:28 2010 -0500
+++ b/src/main/scala/SQL.scala	Sat Jan 02 18:34:46 2010 -0500
@@ -13,15 +13,23 @@
 import scala.util.parsing.combinator._
 import java.net.URI
 
-case class Union(disjoints:Set[Select]) {
-  override def toString = (disjoints mkString ("\nUNION\n"))
+sealed abstract class RelationORSubselect
+case class Subselect(sel:SelectORUnion) extends RelationORSubselect {
+  override def toString = "(\n" + sel + "\n                       )"
 }
-case class Select(attributelist:AttributeList, tablelist:TableList, expression:Option[Expression]) {
+sealed abstract class SelectORUnion
+case class Select(attributelist:AttributeList, tablelist:TableList, expression:Option[Expression]) extends SelectORUnion {
   override def toString = expression match {
     case Some(expr) => attributelist+"\n"+tablelist+"\n WHERE "+expr
     case None => attributelist+"\n"+tablelist
   }
 }
+case class Relation(n:Name) extends RelationORSubselect {
+  override def toString = n.s /* "'" + n.s + "'" */
+}
+case class Union(disjoints:Set[Select]) extends SelectORUnion {
+  override def toString = "\n" + (disjoints mkString ("\nUNION\n")) + "\n)"
+}
 case class AttributeList(attributes:Set[NamedAttribute]) {
   // foo, bar
   override def toString = "SELECT "+(attributes mkString (",\n       "))
@@ -34,7 +42,7 @@
 case class RelAliasAttribute(relalias:RelAlias, attribute:Attribute) extends RelAliasAttributeORConst {
   override def toString = relalias + "." + attribute
 }
-sealed abstract class Const  extends RelAliasAttributeORConst
+sealed abstract class Const extends RelAliasAttributeORConst
 case class ConstNULL() extends Const {
   override def toString = "NULL"
 }
@@ -51,23 +59,24 @@
 case class AttrAlias(n:Name) {
   override def toString = n.s /* "'" + n.s + "'" */
 }
-sealed abstract class RelationORSubselect
-case class Relation(n:Name) extends RelationORSubselect {
-  override def toString = n.s /* "'" + n.s + "'" */
-}
-case class Subselect(union:Union) extends RelationORSubselect {
-  override def toString = "\n" + union + "\n)"
-}
 case class RelAlias(n:Name) {
   override def toString = n.s /* "'" + n.s + "'" */
 }
 case class TableList(joins:Set[Join]) {
-  override def toString = "  FROM " + (joins mkString ("\n       INNER JOIN "))
+  override def toString = "  FROM " + joins.foldLeft(("", 0))(
+    (pair, entry) => (pair._1 + {
+      if (pair._2 == 0) entry.toString.substring(19) // !!! shameless!
+      else entry
+    }, pair._2+1))._1
 }
 
 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 InnerJoin(res:AliasedResource) extends Join(res) {
+  override def toString = "\n       INNER JOIN " + res
+}
+case class LeftOuterJoin(res:AliasedResource, on:Expression) extends Join(res) {
+  override def toString = "\n       LEFT OUTER JOIN " + res
+}
 
 case class AliasedResource(rel:RelationORSubselect, as:RelAlias) {
   override def toString = rel + " AS " + as
@@ -123,8 +132,8 @@
 
 case class Sql() extends JavaTokenParsers {
 
-  def union:Parser[Union] =
-    repsep(select, "UNION") ^^ { l => Union(l.toSet) }
+  def selectORunion:Parser[SelectORUnion] =
+    rep1sep(select, "UNION") ^^ { l => if (l.size == 1) l(0) else Union(l.toSet) }
 
   def select:Parser[Select] =
     "SELECT" ~ attributelist ~ "FROM" ~ tablelist ~ opt(where) ^^
@@ -164,7 +173,7 @@
 
   def relationORsubselect:Parser[RelationORSubselect] = (
       """[a-zA-Z_]\w*""".r ^^ { x => Relation(Name(x)) }
-    | "(" ~ union ~ ")" ^^ { case "("~s~")" => Subselect(s) }
+    | "(" ~ selectORunion ~ ")" ^^ { case "("~s~")" => Subselect(s) }
   )
 
   def relalias:Parser[RelAlias] =
--- a/src/test/scala/SQLTest.scala	Sat Jan 02 16:33:28 2010 -0500
+++ b/src/test/scala/SQLTest.scala	Sat Jan 02 18:34:46 2010 -0500
@@ -272,5 +272,32 @@
     assert(expected === (a.parseAll(a.select, e).get))
   }
 
+  test("parse LEFT OUTER SELECT") {
+    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 (
+    SELECT R_emp.lastName AS A_empName, R_mang.manageName AS A_manageName
+       FROM Employee AS R_emp
+            ) 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(
+			     Subselect(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")))))),
+				    None)),
+			     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))
+  }
+
 
 }