+ transform leadingOpt1
authorEric Prud'hommeaux <bertails@w3.org>
Thu, 07 Jan 2010 09:36:05 -0500
changeset 118 0ea6cb8f981a
parent 117 11e5b3672a69
child 119 cea99f45463a
+ transform leadingOpt1
src/main/scala/RDB2RDFMain.scala
src/test/scala/RDB2RDFTest.scala
--- a/src/main/scala/RDB2RDFMain.scala	Thu Jan 07 09:31:42 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala	Thu Jan 07 09:36:05 2010 -0500
@@ -402,7 +402,17 @@
 	R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(subselect,unionAlias)), state2.varmap, state2.exprs)
       }
       case sparql.OptionalGraphPattern(gp) => {
-      	val leftJoinAlias = sql.RelAlias(sql.Name("R_opt" + state.joins.size))
+	val state_postLeadingTable = if (state.joins.size == 0) {
+	  val emptySubselect = sql.Select(
+	    sql.AttributeList(Set(sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")), sql.AttrAlias(sql.Name("_EMPTY_"))))),
+	    sql.TableList(util.AddOrderedSet()),
+	    None
+	  )
+	  R2RState(state.joins + sql.InnerJoin(sql.AliasedResource(sql.Subselect(emptySubselect),sql.RelAlias(sql.Name("_EMPTY_")))), state.varmap, state.exprs)
+	} else {
+	  state
+	}
+      	val leftJoinAlias = sql.RelAlias(sql.Name("R_opt" + state_postLeadingTable.joins.size))
       	val initDisjoints:Set[sql.Select] = Set()
       	val emptyState = R2RState(
       	  util.AddOrderedSet[sql.Join](), 
@@ -411,7 +421,7 @@
       	)
       	val optionalState = mapGraphPattern(db, emptyState, gp, enforceForeignKeys)
       	val optionalVars = findVars(gp)
-	val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + state.joins.size)), sql.AttrAlias(sql.Name("_DISJOINT_")))
+	val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + state_postLeadingTable.joins.size)), sql.AttrAlias(sql.Name("_DISJOINT_")))
 
       	val leftJoinVars = findVars(gp).toList
       	val attrlist:Set[sql.NamedAttribute] = leftJoinVars.foldLeft(Set(disjointNo))((attrs, v) =>
@@ -430,7 +440,7 @@
 
 	val optionalNoAliasAttr = sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("_DISJOINT_")))
 	val optionalCond = sql.RelationalExpressionNull(sql.PrimaryExpressionAttr(optionalNoAliasAttr))
-	val outerState2 = optionalVars.foldLeft(R2RState(state.joins, state.varmap, Set[sql.Expression]()))((myState, v) => {
+	val outerState2 = optionalVars.foldLeft(R2RState(state_postLeadingTable.joins, state_postLeadingTable.varmap, Set[sql.Expression]()))((myState, v) => {
 	  val varAliasAttr = sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("A_" + v.s)))
 	  if (myState.varmap.contains(v)) {
 	    /* The variable has already been bound. */
@@ -449,10 +459,10 @@
 	      Map()
 	    val newConstraints = {
 	      /* Constraint against binding from earlier GP. */
-	      val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(state.varmap, v)), sql.PrimaryExpressionAttr(varAliasAttr))
-	      if (varToAttributeDisjoints(state.varmap, v).size > 0)
+	      val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(state_postLeadingTable.varmap, v)), sql.PrimaryExpressionAttr(varAliasAttr))
+	      if (varToAttributeDisjoints(state_postLeadingTable.varmap, v).size > 0)
 		// (leftJoin0._DISJOINT_ IS NOT NULL AND leftJoin1._DISJOINT_ IS NOT NULL) OR leftJoin0.x=leftJoin1.x
-		varToAttributeDisjoints(state.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))
+		varToAttributeDisjoints(state_postLeadingTable.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))
 	      else
 		Set(constraint)
 	    }
@@ -471,12 +481,13 @@
 	})
       	val join = sql.LeftOuterJoin(sql.AliasedResource(sql.Subselect(subselect), leftJoinAlias), 
       	  outerState2.exprs.size match {
-      	    case 0 => error ("Nested GP has no variables shared with its context; cowaredly refusing to join ON 1.")
+      	    case 0 => // error ("Nested GP has no variables shared with its context; cowaredly refusing to join ON 1.")
+	      sql.RelationalExpressionEq(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")), sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("1")))
       	    case 1 => outerState2.exprs.toList(0)
       	    case _ => sql.ExprConjunction(outerState2.exprs)
       	  }
 			       )
-      	R2RState(state.joins + join, outerState2.varmap, state.exprs)
+      	R2RState(state_postLeadingTable.joins + join, outerState2.varmap, state_postLeadingTable.exprs)
       }
       case x => error("no code to handle " + x)
     }
--- a/src/test/scala/RDB2RDFTest.scala	Thu Jan 07 09:31:42 2010 -0500
+++ b/src/test/scala/RDB2RDFTest.scala	Thu Jan 07 09:36:05 2010 -0500
@@ -504,6 +504,45 @@
     assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), false, false) === sqlSelect)
   }
 
+  test("transform leadOpt1") {
+    val sparqlParser = Sparql()
+    val sparqlSelect = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX emplP: <http://hr.example/DB/Employee#>
+
+SELECT ?empName ?grandManagName
+ WHERE { OPTIONAL { ?manager      emplP:manager    ?emp .
+                    ?manager      emplP:manager    ?grandManager .
+                    ?grandManager emplP:lastName   ?grandManagName } 
+                  ?emp            emplP:lastName   ?empName }
+""").get
+    val sqlParser = Sql()
+    val sqlSelect = sqlParser.parseAll(sqlParser.select, """
+SELECT R_emp.lastName AS A_empName,
+       R_opt1.A_grandManagName AS A_grandManagName
+  FROM (
+SELECT 1 AS _EMPTY_
+
+                       ) AS _EMPTY_
+       LEFT OUTER JOIN (
+SELECT R_grandManager.lastName AS A_grandManagName,
+       R_manager.id AS A_manager,
+       R_manager.manager AS A_grandManager,
+       R_manager.manager AS A_emp,
+       1 AS _DISJOINT_
+  FROM Employee AS R_manager
+       INNER JOIN Employee AS R_grandManager
+ WHERE (R_manager.manager=R_grandManager.id)
+       AND (R_manager.id IS NOT NULL)
+       AND (R_manager.manager IS NOT NULL)
+       AND (R_grandManager.lastName IS NOT NULL)
+                       ) AS R_opt1 ON 1=1
+       INNER JOIN Employee AS R_emp
+ WHERE ((R_opt1._DISJOINT_ IS NULL) OR (R_opt1.A_emp=R_emp.id))
+       AND (R_opt1.A_emp IS NOT NULL)
+       AND (R_emp.lastName IS NOT NULL)""").get
+    assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), false, false) === sqlSelect)
+  }
+
   test("transform nestOpt") {
     val sparqlParser = Sparql()
     val sparqlSelect = sparqlParser.parseAll(sparqlParser.select, """