--- a/src/main/scala/RDB2RDFMain.scala Tue Jan 05 12:02:26 2010 -0500
+++ b/src/main/scala/RDB2RDFMain.scala Tue Jan 05 12:05:10 2010 -0500
@@ -18,12 +18,12 @@
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.RelationalExpressionNe])
- case class IntMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
- case class StringMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
- case class DateMapper(relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
- case class RDFNoder(relation:sql.Relation, relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
- case class RDFBNoder(relation:sql.Relation, relaliasattr:sql.RelAliasAttribute, disjoints:Set[sql.RelationalExpressionNe]) extends SQL2RDFValueMapper(relaliasattr, disjoints)
+ 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)
def relAliasFromS(s:sparql.S):sql.RelAlias = {
s match {
@@ -92,23 +92,18 @@
** Employee _emp.manager
*/
val reldesc = db.relationdescs(rel)
- if (state.varmap.contains(v)) {
- /* The variable has already been bound. */
- if (varToAttribute(state.varmap, v) == constrainMe)
- /* Don't bother stipulating that foo.bar=foo.bar . */
- state // !!! what about disjoints?
- else {
- /* Constraint against the initial binding for this variable. */
- val constraint = sql.RelationalExpressionEq(sql.PrimaryExpressionAttr(varToAttribute(state.varmap, v)), sql.PrimaryExpressionAttr(constrainMe))
- R2RState(state.joins, state.varmap,
- if (varToAttributeDisjoints(state.varmap, v).size > 0) {
- state.exprs ++ {varToAttributeDisjoints(state.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))}
- } else
- state.exprs + constraint
- )
- }
+ if (state.varmap.contains(v) && varToAttribute(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(varToAttribute(state.varmap, v)), sql.PrimaryExpressionAttr(constrainMe))
+ R2RState(state.joins, state.varmap,
+ if (varToAttributeDisjoints(state.varmap, v).size > 0) {
+ state.exprs ++ {varToAttributeDisjoints(state.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))}
+ } else
+ state.exprs + constraint
+ )
} else {
- /* This is a new variable. */
+ /* 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())
@@ -241,7 +236,7 @@
}
}
- def varToAttributeDisjoints(varmap:Map[sparql.Var, SQL2RDFValueMapper], vvar:sparql.Var):Set[sql.RelationalExpressionNe] = {
+ 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
@@ -338,8 +333,6 @@
val disjointState = mapGraphPattern(db, emptyState, disjoint, pk, enforceForeignKeys)
val disjointVars = findVars(disjoint)
val disjointNo = sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)), sql.AttrAlias(sql.Name("_DISJOINT_")))
- val disjointNoAliasAttr = sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("_DISJOINT_")))
- val disjointCond = sql.RelationalExpressionNe(sql.PrimaryExpressionAttr(disjointNoAliasAttr), sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)))
val attrlist:Set[sql.NamedAttribute] = unionVars.foldLeft(Set(disjointNo))((attrs, v) => {
val attrOrNull = if (disjointState.varmap.contains(v)) varToAttribute(disjointState.varmap, v) else sql.ConstNULL()
@@ -355,6 +348,8 @@
case _ => Some(sql.ExprConjunction(disjointState.exprs))
}
)
+ val disjointNoAliasAttr = sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("_DISJOINT_")))
+ val disjointCond = sql.RelationalExpressionNe(sql.PrimaryExpressionAttr(disjointNoAliasAttr), sql.PrimaryExpressionTyped(sql.Datatype.INTEGER,sql.Name("" + no)))
val outerState2 = disjointVars.foldLeft(outerState)((myState, v) => {
val varAliasAttr = sql.RelAliasAttribute(unionAlias, sql.Attribute(sql.Name("A_" + v.s)))
if (myState.varmap.contains(v)) {
@@ -412,9 +407,10 @@
)
val optionalState = mapGraphPattern(db, emptyState, gp, pk, 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 leftJoinVars = findVars(gp).toList
- val attrlist:Set[sql.NamedAttribute] = leftJoinVars.foldLeft(Set[sql.NamedAttribute]())((attrs, v) =>
+ val attrlist:Set[sql.NamedAttribute] = leftJoinVars.foldLeft(Set(disjointNo))((attrs, v) =>
attrs ++ Set(sql.NamedAttribute(varToAttribute(optionalState.varmap, v), sql.AttrAlias(sql.Name("A_" + v.s))))
)
@@ -428,37 +424,47 @@
}
)
- val outerState2 = optionalVars.foldLeft(R2RState(state.joins, state.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. */
- val newMap:Map[sparql.Var, SQL2RDFValueMapper] = if (varToAttribute(myState.varmap, v) == varAliasAttr) {
- /* Same var was already bound. */
- error("Variable " + v + " already bound to " + varAliasAttr)
- } else
- 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)
- // (leftJoin0._DISJOINT_ != 0 AND leftJoin1._DISJOINT_ != 2) OR leftJoin0.x=leftJoin1.x
- varToAttributeDisjoints(state.varmap, v) map ((d) => sql.ExprDisjunction(Set(d, constraint)))
- else
- Set(constraint)
- }
- R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
- } else {
- /* This variable is new to the outer context. */
- val mapper:SQL2RDFValueMapper = optionalState.varmap(v) match {
- case IntMapper(_, _) => IntMapper(varAliasAttr, Set())
- case StringMapper(_, _) => StringMapper(varAliasAttr, Set())
- case DateMapper(_, _) => DateMapper(varAliasAttr, Set())
- case RDFNoder(rel, _, _) => RDFNoder(rel, varAliasAttr, Set())
- case RDFBNoder(rel, _, _) => RDFBNoder(rel, varAliasAttr, Set())
- }
- R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
- }
- })
+ 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 varAliasAttr = sql.RelAliasAttribute(leftJoinAlias, sql.Attribute(sql.Name("A_" + v.s)))
+ if (myState.varmap.contains(v)) {
+ /* 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)
+ } } )
+ } else
+ 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)
+ // (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)))
+ else
+ Set(constraint)
+ }
+ R2RState(myState.joins, myState.varmap ++ newMap, myState.exprs ++ newConstraints)
+ } else {
+ /* This variable is new to the outer context. */
+ 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))
+ }
+ R2RState(myState.joins, myState.varmap + (v -> mapper), myState.exprs)
+ }
+ })
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.")
--- a/src/test/scala/RDB2RDFTest.scala Tue Jan 05 12:02:26 2010 -0500
+++ b/src/test/scala/RDB2RDFTest.scala Tue Jan 05 12:05:10 2010 -0500
@@ -481,7 +481,7 @@
SELECT R_emp.lastName AS A_empName, R_opt1.A_managName AS A_managName, R_opt1.A_grandManagName AS A_grandManagName
FROM Employee AS R_emp
LEFT OUTER JOIN (
- SELECT R_grandManager.lastName AS A_grandManagName, R_manager.lastName AS A_managName, R_mang.manages AS A_emp,
+ SELECT 1 AS _DISJOINT_, R_grandManager.lastName AS A_grandManagName, R_manager.lastName AS A_managName, R_mang.manages AS A_emp,
R_grandMang.manager AS A_grandManager,
R_mang.id AS A_mang,
R_mang.manager AS A_manager,
@@ -526,12 +526,12 @@
SELECT R_emp.lastName AS A_empName, R_opt1.A_managName AS A_managName, R_opt1.A_grandManagName AS A_grandManagName
FROM Employee AS R_emp
LEFT OUTER JOIN (
- SELECT R_opt2.A_grandManagName AS A_grandManagName, R_manager.lastName AS A_managName, R_mang.manages AS A_emp, R_mang.manager AS A_manager,
- R_mang.id AS A_mang, R_opt2.A_grandMang AS A_grandMang, R_opt2.A_grandManager AS A_grandManager
+ SELECT 1 AS _DISJOINT_, R_opt2.A_grandManagName AS A_grandManagName, R_manager.lastName AS A_managName, R_mang.manages AS A_emp,
+ R_mang.manager AS A_manager, R_mang.id AS A_mang, R_opt2.A_grandMang AS A_grandMang, R_opt2.A_grandManager AS A_grandManager
FROM Manage AS R_mang
INNER JOIN Employee AS R_manager
LEFT OUTER JOIN (
- SELECT R_grandManager.lastName AS A_grandManagName, R_grandMang.manages AS A_manager,
+ SELECT 2 AS _DISJOINT_, R_grandManager.lastName AS A_grandManagName, R_grandMang.manages AS A_manager,
R_grandMang.id AS A_grandMang, R_grandMang.manager AS A_grandManager
FROM Manage AS R_grandMang
INNER JOIN Employee AS R_grandManager
@@ -545,4 +545,61 @@
""").get
assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), PrimaryKey(Attribute(Name("id"))), false, false) === sqlSelect)
}
+
+ test("transform equivOpt1") {
+ val sparqlParser = Sparql()
+ val sparqlSelect = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX emplP: <http://hr.example/DB/Employee#>
+
+SELECT ?emp1Name ?emp2Name ?emp3Name
+ WHERE { ?emp1 emplP:lastName ?emp1Name
+ OPTIONAL { ?emp1 emplP:birthday ?birthday }
+ ?emp2 emplP:lastName ?emp2Name
+ OPTIONAL { ?emp2 emplP:birthday ?birthday }
+ ?emp3 emplP:lastName ?emp3Name .
+ ?emp3 emplP:birthday ?birthday .
+ ?emp4 emplP:lastName ?emp4Name .
+ ?emp4 emplP:birthday ?birthday
+ FILTER ( ?emp1Name < ?emp2Name && ?emp2Name < ?emp3Name && ?emp3Name < ?emp4Name) }
+""").get
+ val sqlParser = Sql()
+ val sqlSelect = sqlParser.parseAll(sqlParser.select, """
+SELECT R_emp1.lastName AS A_emp1Name, R_emp2.lastName AS A_emp2Name, R_emp3.lastName AS A_emp3Name
+ FROM Employee AS R_emp1
+ LEFT OUTER JOIN (
+ SELECT 1 AS _DISJOINT_,
+ R_emp1.id AS A_emp1,
+ R_emp1.birthday AS A_birthday
+ FROM Employee AS R_emp1
+ WHERE (R_emp1.id IS NOT NULL)
+ AND (R_emp1.birthday IS NOT NULL)
+ ) AS R_opt1 ON R_emp1.id=R_opt1.A_emp1
+ INNER JOIN Employee AS R_emp2 ON (R_emp1.lastName<R_emp2.lastName)
+ AND (R_emp2.id IS NOT NULL)
+ AND (R_emp2.lastName IS NOT NULL)
+ LEFT OUTER JOIN (
+ SELECT 3 AS _DISJOINT_,
+ R_emp2.id AS A_emp2,
+ R_emp2.birthday AS A_birthday
+ FROM Employee AS R_emp2
+ WHERE (R_emp2.id IS NOT NULL)
+ AND (R_emp2.birthday IS NOT NULL)
+ ) AS R_opt3 ON (R_emp2.id=R_opt3.A_emp2)
+ AND ((R_opt1._DISJOINT_ IS NULL) OR (R_opt1.A_birthday=R_opt3.A_birthday))
+ INNER JOIN Employee AS R_emp3 ON ((R_opt1._DISJOINT_ IS NULL) OR (R_opt1.A_birthday=R_emp3.birthday))
+ AND (R_emp2.lastName<R_emp3.lastName)
+ AND (R_emp3.id IS NOT NULL)
+ AND (R_emp3.lastName IS NOT NULL)
+ INNER JOIN Employee AS R_emp4 ON (R_emp3.lastName<R_emp4.lastName)
+ AND ((R_opt1._DISJOINT_ IS NULL) OR (R_opt1.A_birthday=R_emp4.birthday))
+ AND (R_emp4.id IS NOT NULL)
+ AND (R_emp4.lastName IS NOT NULL)
+ WHERE (R_emp1.id IS NOT NULL)
+ AND (R_emp1.lastName IS NOT NULL)
+ AND (R_opt1.A_birthday IS NOT NULL)
+""").get
+ assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), PrimaryKey(Attribute(Name("id"))), false, false) === sqlSelect)
+ }
+
+
}