--- 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, """